12306小卡片-鸿蒙智慧出行

想了解更多内容,小卡行请访问:

和华为官方合作共建的片鸿鸿蒙技术社区

https://harmonyos.51cto.com

在没有鸿蒙之前,我大喵相信很多人座高铁是蒙智这样的:

1.买了高铁票,打车去高铁站

2.进入高铁站大门然后安检

3.在候车室等车和等检票

4.到了站台等高铁

5.上车

我大喵怕乘错车和晚上车经常会在3,慧出4步骤的时候打开12306,然后打开车票,小卡行查看车票信息和发车时间,片鸿保证我是蒙智准点去检票座车,并且没有乘错车。慧出像我这种经常去出差的小卡行人,一到高铁站会一直重复的片鸿去打开12306去查看车票信息,我觉得是蒙智一件非常非常麻烦的事情。还有现在的慧出12306不会把车票信息以短信的方式发给我们,情景智能都不能用。小卡行那如何解决这个问题呢,片鸿答案就是蒙智鸿蒙小卡片。

下面我们来看一个视频

https://harmonyos.51cto.com/show/7339

12306小卡片效果展示

可以看到车票的网站模板所有重要信息都展示了出来,这样在出发去高铁站之前,我们只需要长按选择这个12306小卡片,添加到桌面上,然后长按小卡片出现编辑页面

在小卡片编辑页面添加车票相关信息,点击查询后会查询到相关列车信息,然后点就确认就会把数据放到小卡片上去了

12306小卡片开发准备

1.创建工程

详见鸿蒙官网

2.工程中添加小卡片

详见鸿蒙官网

3.目录结构

4.添加12306域名

"deviceConfig": {      "default": {        "network": {          "securityConfig": {            "domainSettings": {              "cleartextPermitted": true,             "domains": [               {                  "name": "search.12306.cn"               },               {                  "name": "kyfw.12306.cn"               }             ]           }         }       }     }   }, 

小卡片页面开发

index.hml

小卡片主要分成两大模块:车票基本信息和车次列表

车票基本信息里面有:开始地址、开始时间、车次号、日期、座位号、结束地址和结束时间

车次列表里面有:到达时间,发车时间,所需时间和正点状态

<div class="main_container">     <!-- 车票基本信息 -->     <div class="title_container" @click="getNewData">         <div class="title_div">             <text class="title_address_text">{ { treainData.startAddress}}</text>             <text class="title_time_text">{ { treainData.startTime}}</text>         </div>         <div class="title_div" style="margin-top: 5px;" >             <text class="title_ticket_text">{ { treainData.trainDataString}}</text>         </div>         <div class="title_div">             <text class="title_address_text">{ { treainData.endAddress}}</text>             <text class="title_time_text">{ { treainData.endTime}}</text>         </div>     </div>     <!-- 车次列表 -->     <div>         <list class="station_list">             <list-item class="station_list_item" clickeffect="false" for="{ { stationList}}">                 <text class="station_start_time">{ { $item.start_time}}</text>                 <div class="circle"></div>                 <text class="station_name_text">{ { $item.station_name}}</text>                 <text class="station_mess_text grey_color">{ { $item.arrive_time}}</text>                 <text class="station_mess_text grey_color">{ { $item.running_time}}</text>                 <text class="station_mess_text grey_color">{ { $item.arrive_day_str}}</text>             </list-item>         </list>     </div> </div> 

index.css

.main_container{      flex-direction: column;     align-items: center;     justify-content: center;     width: 100%;     height: 100%;     background-color: #FFFFFF; } .title_container{      margin-top: 5px; } .title_time_text{      font-size: 10px;     width: 100%;     height: 10px;     text-align: center;     color: darkgray; } .title_address_text{      font-size: 16px;     width: 100%;     height: 20px;     text-align: center;     color: #5F80FF; } .title_div{      height: 30px;     display: flex;     flex-direction: column; } .title_ticket_text{      width: 300px;     font-size: 10px;     text-align: center;     color: #5F80FF; } .teain_d_text{      width: 120px;     height: 35px; /*    background-image: url("/common/jt.png");*/     background-size: 100% 100%;     text-align: center;     font-size: 10px;     line-height: 15px; } .station_list{      width: 100%;     height: 120px;     margin-left: 10px;     margin-top: 5px; } .station_list_item{  } .station_name_text{      width: 80px;     font-size: 12px;     padding-left: 5px;     border-left-style: solid;     border-left-color: #5F80FF;     border-left-width: 1px;     padding-bottom: 8px; } .grey_color{      color: rgb(80,80,80); } .station_mess_text{      width: 60px;     text-align: center;     font-size: 10px; } .station_start_time{      font-size: 11px; } .circle{      width: 8px;     height: 8px;     border-radius: 100px;     border: 4px solid #5F80FF;     margin-top: 3.5px;     left: 4.5px; } 

index.json

小卡片通过json配置数据绑定和点击事件,车票的基本信息保存在treainData里面,车次列表保存在stationList里面

{    "data": {      "treainData": {        "startAddress": "",       "startTime": "",       "endAddress": "",       "endTime": "",       "trainDataString": ""     },     "stationList": [],   },   "actions": {      "getNewData": {        "action": "message",       "params": {          "message": "getNewData"       }     }   } } 

小卡片编辑功能的开发准备

鸿蒙的小卡片可以创建很多个,最大好像是8个,每个小卡片肯定是展现不一样的信息,所以需要添加编辑页面来让用户编辑这个小卡片,没有编辑页面的小卡片是服务器托管没有灵魂的。

首先需要新建一个TrainConfigAbility,在里面setInstanceName TrainConfig

package com.example.phone.ability.train; import ohos.aafwk.ability.AbilitySlice; import ohos.aafwk.content.Intent; import ohos.aafwk.content.IntentParams; import ohos.ace.ability.AceAbility; public class TrainConfigAbility extends AceAbility {      public static Long cardId;     @Override     public void onStart(Intent intent) {          setInstanceName("TrainConfig");         IntentParams params = intent.getParams();         cardId = (long) params.getParam(AbilitySlice.PARAM_FORM_IDENTITY_KEY);         super.onStart(intent);     }     @Override     public void onStop() {          super.onStop();     } } 

然后在小卡片配置JSON里面添加

"formConfigAbility": "ability://com.example.phone.ability.train.TrainConfigAbility" 

我的小卡片配置文件时这样的

{          "name": "com.example.phone.ability.train.TrainAbility",         "icon": "$media:icon",         "description": "$string:widget_trainability_description",         "formsEnabled": true,         "label": "$string:entry_TrainAbility",         "type": "page",         "forms": [           {              "jsComponentName": "train",             "isDefault": true,             "scheduledUpdateTime": "10:30",             "defaultDimension": "2*4",             "name": "Train",             "description": "train card",             "colorMode": "auto",             "type": "JS",             "supportDimensions": [               "2*4"             ],             "updateEnabled": true,             "updateDuration": 1,             "formConfigAbility": "ability://com.example.phone.ability.train.TrainConfigAbility"           }         ],         "launchType": "singleton"       }, 

小卡片数据库

关系型数据库加入包

在对应的entry的build.gradle中添加包

dependencies {      implementation fileTree(dir: libs, include: [*.jar, *.har])     testCompile junit:junit:4.12     compile files(ORM_ANNOTATIONS_JAVA, ORM_ANNOTATIONS_PROCESSOR_JAVA, JAVAPOET_JAVA)     annotationProcessor files(ORM_ANNOTATIONS_JAVA, ORM_ANNOTATIONS_PROCESSOR_JAVA, JAVAPOET_JAVA) } 

 在gradle.properties中添加gradle全局变量

JAVAPOET_JAVA=C:/Users/XX/AppData/Local/Huawei/Sdk/java/2.1.1.21/build-tools/lib/javapoet_java.jar ORM_ANNOTATIONS_PROCESSOR_JAVA=C:/Users/XX/AppData/Local/Huawei/Sdk/java/2.1.1.21/build-tools/lib/orm_annotations_processor_java.jar ORM_ANNOTATIONS_JAVA=C:/Users/XX/AppData/Local/Huawei/Sdk/java/2.1.1.21/build-tools/lib/orm_annotations_java.jar 

 TrainStore.java

package com.example.phone.store; import com.example.phone.store.from.Train; import ohos.data.orm.OrmDatabase; import ohos.data.orm.annotation.Database; @Database(entities = { Train.class}, version = 1) public abstract class TrainStore extends OrmDatabase {  } 

 Train.java

package com.example.phone.store.from; import ohos.data.orm.OrmObject; import ohos.data.orm.annotation.Entity; import ohos.data.orm.annotation.PrimaryKey; import ohos.utils.zson.ZSONArray; @Entity(tableName = "trail") public class Train extends OrmObject {      @PrimaryKey(autoGenerate = true)     private Long id;     private String trainList;     public Train() {      }     public Train(Long id, String trainList) {          this.id = id;         this.trainList = trainList;     }     public Long getId() {          return id;     }     public void setId(Long id) {          this.id = id;     }     @Override     public String toString() {          return "Train{ " +                 "id=" + id +                 ", trainList=" + trainList +                 };     }     public String getTrainList() {          return trainList;     }     public void setTrainList(String trainList) {          this.trainList = trainList;     } } 

小卡片Ability

主要作用时对小卡片的创建删除进行管理,点击事件的处理

在创建小卡片时,在数据库中新建一条小卡片数据,当点击更新按钮时,从数据库里面读取对应卡片的trainNo和date,然后请求API获取数据

"https://kyfw.12306.cn/otn/queryTrainInfo/query?leftTicketDTO.train_no="+trainNo+"&leftTicketDTO.train_date="+date+"&rand_code="; 

TrainAbility.java

package com.example.phone.ability.train; import com.example.phone.store.OilStore; import com.example.phone.store.TrainStore; import com.example.phone.store.from.OilPrice; import com.example.phone.store.from.Train; import ohos.aafwk.ability.*; import ohos.aafwk.content.Intent; import ohos.app.Context; import ohos.data.DatabaseHelper; import ohos.data.orm.OrmContext; import ohos.data.orm.OrmPredicates; import ohos.hiviewdfx.HiLog; import ohos.hiviewdfx.HiLogLabel; import ohos.utils.zson.ZSONArray; import ohos.utils.zson.ZSONObject; import okhttp3.*; import org.jetbrains.annotations.NotNull; import java.io.IOException; import java.util.List; public class TrainAbility extends Ability {      public static final int DEFAULT_DIMENSION_2X2 = 2;     public static final int INVALID_FORM_ID = -1;     private static final HiLogLabel TAG = new HiLogLabel(HiLog.DEBUG, 0x0, TrainAbility.class.getName());     private static OrmContext ormContext = null;     private DatabaseHelper helper = new DatabaseHelper(this);     OkHttpClient okHttpClient = new OkHttpClient();     public void create(Context context){          System.out.println("创建高铁数据库");         ormContext = helper.getOrmContext("TrainStore", "TrainStore.db", TrainStore.class);     }     @Override     protected void onStart(Intent intent) {          super.onStart(intent);         stopAbility(intent);     }     @Override     protected ProviderFormInfo onCreateForm(Intent intent) {          long formId = intent.getLongParam(AbilitySlice.PARAM_FORM_IDENTITY_KEY, INVALID_FORM_ID);         String formName = intent.getStringParam(AbilitySlice.PARAM_FORM_NAME_KEY);         int dimension = intent.getIntParam(AbilitySlice.PARAM_FORM_DIMENSION_KEY, DEFAULT_DIMENSION_2X2);         HiLog.info(TAG, "onCreateForm: formId=" + formId + ",formName=" + formName);         if(ormContext == null){              create(getContext());         }         // 数据库新建数据         Train train = new Train();         train.setId(formId);         boolean isSuccessed = ormContext.insert(train);         isSuccessed = ormContext.flush();         return null;     }     @Override     protected void onTriggerFormEvent(long formId, String message) {          OrmPredicates predicates = ormContext.where(Train.class);         predicates.equalTo("id", formId);         List<Train> trailList = ormContext.query(predicates);         Train trail = trailList.get(0);         ZSONObject trainData = ZSONObject.stringToZSON(trail.getTrainList());         System.out.println(trail);         // 查询数据         String date = trainData.getString("chooseDate");         String trainNo = trainData.getString("trainNo");         String url = "https://kyfw.12306.cn/otn/queryTrainInfo/query?leftTicketDTO.train_no="+trainNo+"&leftTicketDTO.train_date="+date+"&rand_code=";         System.out.println(url);         Call call = okHttpClient.newCall(new Request.Builder().url(url).build());         call.enqueue(new Callback() {              @Override             public void onFailure(@NotNull Call call, @NotNull IOException e) {              }             @Override             public void onResponse(Call call, Response response) throws IOException {                  String body = response.body().string();                 ZSONObject data = new ZSONObject();                 ZSONArray statinList = ZSONObject.stringToZSON(body).getZSONObject("data").getZSONArray("data");                 data.put("stationList",statinList);                 System.out.println(data);                 FormBindingData formBindingData = new FormBindingData(data);                 try {                      if (!updateForm(formId, formBindingData)) {                      }                 } catch (FormException e) {                      e.printStackTrace();                 }             }         });         super.onTriggerFormEvent(formId, message);     } } 

小卡片编辑页面的开发

index.hml

配置页面CSS代码很少我就不贴了

<div class="container">     <div>         <text class="title">车次号</text>         <input type="text" maxlength="6" style="width: 300px;" on:change="TrainNumberChange"></input>     </div>     <div>         <text class="title">日期</text>         <picker type="date" start="{ { today}}" value="{ { today}}" onchange="DataChange" ></picker>     </div>     <div>         <text class="title">站台</text>         <input type="text" maxlength="20" style="width: 300px;" on:change="addressChange"></input>     </div>     <div>         <text class="title">座位类型</text>         <picker type="text" range="{ { seatPickerList}}" value="{ { seatValue}}" on:change="seatChange"></picker>     </div>     <div>         <text class="title">车号</text>         <input type="number" maxlength="2" style="width: 300px;" on:change="VehicleNumberChange"></input>     </div>     <div>         <text class="title">座位号</text>         <input type="text" maxlength="4" style="width: 300px;" on:change="SeatNumberChange" ></input>     </div>     <div>         <text class="title">发车站台</text>         <text>{ { trainData.start_station_name}}</text>     </div>     <div>         <text class="title">结束站台</text>         <text>{ { trainData.end_station_name}}</text>     </div>     <div>         <text class="title">列车类型</text>         <text>{ { trainData.train_class_name}}</text>     </div>     <div>         <text class="title">OCR自动识别信息</text>     </div>     <button class="qr_but" @click="search">查询</button>     <button class="qr_but" @click="qr">确定</button> </div> 

具体业务逻辑

1.当页面初始化时请求API获取全国站台的缩写代码(编号),因为请求的是JS文件,需要使用|隔开存入数组

https://kyfw.12306.cn/otn/resources/js/framework/station_name.js

var station_names =@bjb|北京北|VAP|beijingbei|bjb|0@bjd|北京东|BOP|b 

2.获取今日日期

3.获取所有输入框和选择器的数值

4.点击查询按钮时

提取train_station_code 获取该站台所有列车数据 "https://kyfw.12306.cn/otn/czxx/query?train_start_date="+that.chooseDate+"&train_station_code="+addressCode 

5.整理数据发送给service ability

index.js

import fetch from @system.fetch import app from @system.app var that; const ABILITY_TYPE_EXTERNAL = 0; const ACTION_SYNC = 0; const CHOOSE = 1001; var stationNameArray = []; export const TrainAbility = {      choose: async function(data){          var action = { };         var sendUserData = data;         console.info(sendUserData);         action.bundleName = com.example.phone;         action.abilityName = com.example.phone.ability.train.TrainServiceAbility;         action.messageCode = CHOOSE;         action.data = sendUserData;         action.abilityType = ABILITY_TYPE_EXTERNAL;         action.syncOption = ACTION_SYNC;         var result = await FeatureAbility.callAbility(action);     } } export default {      data: {          title: "",         today: "2000-01-01",         chooseDate:"",         seatPickerList:["商务座","一等座","二等座","站票"],         seatValue: "二等座",         pickerDefineIndex: 0,         trainNumber: "",         vehicleNumber: "",         seatNumber: "",         address: "",         trainNo:"",         trainData:{ }     },     onInit() {          that = this;         //设置今日今天的时间         var nowDay = new Date();         let mount = (nowDay.getMonth()+1);         let day = nowDay.getDate();         if(mount < 10) mount = "0"+mount;         if(day < 10) day = "0"+day;         this.today = nowDay.getFullYear()+"-" + mount + "-" + day;         this.chooseDate = this.today;         // 请求获取站编号         fetch.fetch({              url: "https://kyfw.12306.cn/otn/resources/js/framework/station_name.js",             success: function(response) {                  var result = response.data;                 stationNameArray = result.split(|);             },             fail: function() {                  console.info("---get station_name fetch fail---");             }         });     },     addressChange(value){          this.address = value.text;     },     DataChange(e){          let mount = e.month+1;         let day = e.day;         if(mount < 10) mount = "0"+mount;         if(day < 10) day = "0"+day;         this.today = e.year + "-" + mount + "-" + day;     },     seatChange(obj){          this.seatValue = this.seatPickerList[obj.newSelected];     },     TrainNumberChange(value){          this.trainNumber = value.text;     },     VehicleNumberChange(value){          this.vehicleNumber = value.text;     },     SeatNumberChange(value){          this.seatNumber = value.text;     },     search(){          // 查询车架号https://kyfw.12306.cn/otn/czxx/query?train_start_date=2021-07-17&train_station_code=CAU         //  1.提取train_station_code         let index = stationNameArray.indexOf(this.address);         if(index == 0){              // 输入错误             return;         }         let addressCode = stationNameArray[index+1];         // 2.获取该站台所有列车数据 C3863 54000C386601         //         fetch.fetch({              url: "https://kyfw.12306.cn/otn/czxx/query?train_start_date="+that.chooseDate+"&train_station_code="+addressCode,             success: function(response) {                  var result = JSON.parse(response.data);                 if(result.data == null){                      return;                 }                 // 查询trainNumber                 let data = result.data.data;                 for(var dindex in data){                      if(data[dindex].station_train_code == that.trainNumber){                          that.trainData = data[dindex];                         that.trainNo = data[dindex].train_no;                         break;                     }                 }                 console.info("车数据:"+that.trainNo);             },             fail: function() {                  console.info("https://kyfw.12306.cn/otn/czxx/query fail");             }         });     },     qr(){          if(that.trainNo == null || that.trainNo.length == 0){              console.info("数据有误");             return;         }         // 整理数据         let SendData = {              treainData:{                  "seat": that.seatValue,                 "today": that.today,                 "trainNumber": that.trainNumber,                 "vehicleNumber": that.vehicleNumber,                 "seatNumber": that.seatNumber,                 "trainNo": that.trainNo,                 "chooseDate": that.chooseDate,                 "startAddress": that.trainData.start_station_name,                 "endAddress": that.trainData.end_station_name,                 "startTime": that.trainData.start_start_time,                 "endTime": that.trainData.end_arrive_time,                 "trainDataString": that.today+" "+that.trainNumber+"\n"+that.seatValue+" "+that.vehicleNumber+" "+that.seatNumber             }         }         // 调试打印         console.info(this.trainData.toString());         // 发送数据给后端java service ability         TrainAbility.choose(SendData);         // 关闭当前页面         app.terminate();     } } 

编辑页面数据处理

TrainServiceAbility.java

package com.example.phone.ability.train; import com.example.phone.ability.OilConfigAbility; import com.example.phone.store.OilStore; import com.example.phone.store.TrainStore; import com.example.phone.store.from.OilPrice; import com.example.phone.store.from.Train; import com.example.phone.utils.TrainDataTF; import ohos.aafwk.ability.Ability; import ohos.aafwk.ability.FormBindingData; import ohos.aafwk.ability.FormException; import ohos.aafwk.content.Intent; import ohos.data.DatabaseHelper; import ohos.data.orm.OrmContext; import ohos.data.orm.OrmPredicates; import ohos.hiviewdfx.HiLog; import ohos.hiviewdfx.HiLogLabel; import ohos.rpc.*; import ohos.utils.zson.ZSONArray; import ohos.utils.zson.ZSONObject; import java.util.List; public class TrainServiceAbility extends Ability {      private static final HiLogLabel LABEL_LOG = new HiLogLabel(3, 0xD001100, "Demo");     private static final int CHOOSE = 1001;     private static OrmContext ormContext = null;     private DatabaseHelper helper = new DatabaseHelper(this);     private TrainServiceAbility.TrainServiceRemote trainServiceRemote;     @Override     public void onStart(Intent intent) {          HiLog.info(LABEL_LOG, "TrainServiceAbility::onStart");         trainServiceRemote = new TrainServiceAbility.TrainServiceRemote();         ormContext = helper.getOrmContext("TrainStore", "TrainStore.db", TrainStore.class);         super.onStart(intent);     }     @Override     protected IRemoteObject onConnect(Intent intent) {          super.onConnect(intent);         return trainServiceRemote.asObject();     }     @Override     public void onDisconnect(Intent intent) {      }     class TrainServiceRemote extends RemoteObject implements IRemoteBroker {          public TrainServiceRemote() {              super("TrainServiceRemote");         }         @Override         public boolean onRemoteRequest(int code, MessageParcel data, MessageParcel reply, MessageOption option) throws RemoteException {              switch (code) {                  case CHOOSE:{                      // 更新数据库                     ZSONObject zsonStr = ZSONObject.stringToZSON(data.readString());                     System.out.println(zsonStr);                     OrmPredicates predicates = ormContext.where(Train.class);                     predicates.equalTo("id", TrainConfigAbility.cardId);                     List<Train> trailList = ormContext.query(predicates);                     Train trail = trailList.get(0);                     trail.setTrainList(zsonStr.getZSONObject("treainData").toString());                     ormContext.update(trail);                     ormContext.flush();                     // 更新小卡片                     FormBindingData formBindingData = new FormBindingData(zsonStr);                     try {                          if (!updateForm(TrainConfigAbility.cardId, formBindingData)) {                          }                     } catch (FormException e) {                          e.printStackTrace();                     }                     break;                 }                 default: {                      reply.writeString("service not defined");                     return false;                 }             }             return true;         }         @Override         public IRemoteObject asObject() {              return this;         }     } } 

想了解更多内容,请访问:

和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

滇ICP备2023000592号-31