虽然 uniapp 可以同时生成 小程序,app,移动网页版.但是有些项目 开始开发的时候 只要求要做小程序版,所以就没有考虑要用uniapp 做。我这边就有一个项目 要做一个 读书和听书的 小程序。先用 原生js 做了小程序,后台用的 laravel-admin。
项目运行了两年后 ,那边的人想要做 公众号版本。要求功能,界面都要和小程序一样。
我这边有了两个 选项 1.用 uniapp 重新做一遍 2.找到一个 小程序 移植 公众号的方法。
我选择了第二个。
用度娘和csdn 查找了一些方案后。最终决定用其中的几个合并起来用。现在在做这个项目。觉得其他人有可能 也需要这样的 功能。
1.登录部分移植,laravel 直接用esywechat 配置公众号然后获取用户信息
2.创建路由,创建控制器
public function index(Request $request)
{
$params = $request->all();
$ad = Ad::select(['id', 'image'])->get();;
$dt = [
'ads' => json_encode($ad)
];
$user = $request->session()->get('user');
$dt['user'] = $user;
$dt['platform'] = $this->getPlatform();
$dt['date']=$this->getVersionDate();
return view('mobile.pages.index.index', $dt);
}
3.页面移植 先创建 view 文件 xx.blade.php
页面中引入 vue.global.js,axios.min.js 和自己的页面 js 文件
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title></title>
<script src="/assets/js/vue.global.js"></script>
<script src="/assets/js/axios.min.js"></script>
<script src="/assets/js/hls.min.js"></script>
<link href="/assets/css/swiper-bundle.min.css" rel="stylesheet">
<link href="/assets/js/mobile/app.css?{{$date}}" rel="stylesheet">
<link href="/assets/js/mobile/pages/radio/radio.css?v={{$date}}" rel="stylesheet">
</head>
<body>
<div id="app">
</div>
<script>
var recommended_voices = <?php echo json_encode($recommended_voices) ?>;
var user = <?php echo json_encode($user) ?>;
var platform = <?php echo json_encode($platform) ?>;
</script>
<script type="text/javascript" src="/assets/js/jquery.min.js-v=2.1.4.js"></script>
<script src="/assets/js/swiper-bundle.min.js"></script>
<script src="/assets/js/mobile/app.js?{{$date}}"></script>
<script src="/assets/js/mobile/pages/radio/radio.js?v={{$date}}"></script>
</body>
</html>
4.复制小程序页面的wxss文件 ,view 变 div ,image 变 img ,rpx 除以 2 保存 xx.css
5.复制页面 xx.wxml 文件 view 变 div ,image 变 img ,rpx 除以 2 ,wx:if 变 v-if ,wx:for 变 v-for, bindtap 变 @click
数据绑定方式 要用 vue 的方式改变
然后全部复制放到另一个文件保存 待会儿要用
6.页面js 文件 大概就长这个样子
var _self = this;
const app = Vue.createApp({
created() {
console.log('creatd');
},
mounted() {
this.init();
setTitle(this.audio.name_ug);
},
data() {
return {
server_url: '/',
isBack: false,
loading: false,
loadMsg: '',
toast: false,
recommended_voices: recommended_voices,
hotMusic:recommended_voices,
audio: recommended_voices[0],
playing: false,
time: 0,
percent: 0,
duration: 0,
play_action: 'pause',
modal_msg: '',
bg: getMainColor(),
}
},
template: `
<div class='cu-load load-modal' v-if="loading" @click="hideModal()">
<img src='/assets/image/logo.png' class='png' mode='aspectFit' />
<div class='gray-text ug'>{{loadMsg}}</div>
</div>
<div v-if="toast" class="cu-modal ug show ">
<div :class="toast_success==true?'text-white cu-dialog bg-atom':'cu-dialog bg-atom text-red'">
<div class="padding-xl">
{{toast_msg}}
</div>
</div>
</div>
<div style="height:45px">
<div class="cu-custom" style="height:45px" >
<div :class="bg+' cu-bar fixed none-bg text-white bg-img'" style="height:45px;padding-top:0px;">
<div class="action border-custom" style="width:70px">
<text class="cuIcon-back" @click="BackPage"></text>
<text class="cuIcon-homefill" @click="toHome"></text>
</div>
<div class="head-content ug" style="top:0px">
<div class="page-header ug">{{audio.name_ug}}</div>
</div>
</div>
</div>
</div>
<div class="player">
<div class="audio-content">
<div class="image">
<img lazy-load="true" :style="{'height': '120px !important','width': '120px','border-radius': '120px','animation-play-state':playing?'running':'paused'}" id="imageicon" :src="audio.course.image_url" />
</div>
<div class="ug text-center padding">
<text class="ug text-white">{{audio.course.name_ug}}</text>
</div>
<div class="ug text-center rtl">
<text class="ug text-white">{{audio.name_ug}}</text>
</div>
</div>
<div>
</div>
<div class="audio-control">
<div class="row ">
<div class="grid padding">
<div class="left">
<div class="text-white">{{time}}</div>
</div>
<div class="center">
<input min="1" max="100" type="range" v-model="percent" @change="slider1change()" style="width: 98%;margin-top: 10px;"/>
</div>
<div class="right">
<div class="text-white">{{duration!='Nan'?duration:''}}</div>
</div>
</div>
</div>
<div class=" grid col-5 ">
<div class="padding" data-action="back"></div>
<div class="padding text-center " @click="play" data-action="pre">
<i class="play-icon play-left text-white fa fa-step-backward "></i>
</div>
<div class="padding text-center " @click="play" data-action="play"><i :class="play_action=='play'?' play-icon-center text-white fa fa-pause-circle-o':' play-icon-center text-white fa fa-play-circle-o'"></i>
</div>
<div class="padding text-center" @click="play" data-action="next">
<i class="play-icon play-right text-white fa fa-step-forward "></i>
</div>
<div class="padding" data-action="forward"></div>
</div>
<div class="grid col-1 row">
<button class="cu-btn ug round bg-orange texte-white more-btn row" @click="listenMore">داۋامىنى ئاڭلاش</button>
</div>
</div>
</div>
<div style="display:none;">
<audio :src="audio.audio_url" id="audio" ref="audio" controls="controls" crossOrigin="anonymous" ></audio>
</div>
<div class="cu-modal ug {{modalName=='bottomModal'?'show':'hidden'}}">
<div class="cu-dialog bg-atom {{success==true?'text-white':'text-red'}}">
<div class="padding-xl">
{{modal_msg}}
</div>
</div>
</div>
<div class="cu-modal ug {{modalName=='inputModal'?'show':'hidden'}}">
<div class="cu-dialog">
<div class="grid col-1 bg-white">
<textarea placeholder="ئىنكاس مەزمۇنى" placeholder-class="textarea">
</textarea>
</div>
<div class="grid col-2">
<div class="padding"><button class="cu-btn round" @click="hideModal">چېكىنىش</button></div>
<div class="padding"><button class="cu-btn {{bg}} round" @click="hideModal">جەزملەش</button></div>
</div>
</div>
</div>
`, // 插值表达式
methods: {
BackPage() {
_self.window.history.back(true);
},
toHome() {
_self.location.href = '/mobile';
},
NavChange(e) {
console.log('original navChange', e);
_self.location.href = e;
},
showLoading() {
console.log('showLoading');
this.loading = true;
},
hideLoading() {
console.log('hideLoading');
this.loading = false
},
hideModal() {
this.modalName = null;
},
setData(obj) {
// console.log('setData', obj);
var keys = Object.keys(obj);
// console.log('keys', keys);
for (var i = 0; i < keys.length; i++) {
this[keys[i]] = obj[keys[i]];
}
},
init() {
var w = _self.window.innerWidth - 20;
var h = _self.window.innerHeight - 50;
this.setData({
window_width: w,
window_height: h,
})
// this.audioInit();
console.log('Hls', Hls);
if (Hls.isSupported()) {
console.log('audio',this.$refs.audio);
var hls = new Hls(); // 实例化 Hls 对象
var url='https://kitap.kutux.cn/api/data/radio';
// url='https://ngcdn013.cnr.cn/live/wygb/index.m3u8';
hls.loadSource(url); // 传入路径
hls.attachMedia(this.$refs.audio);
hls.on(Hls.Events.MANIFEST_PARSED, () => { //加载成功
setTimeout(() => {
console.log('audio',this.$refs.audio)
this.$refs.audio.play(); // 调用播放 API
}, 0)
});
} else {
console.log('Hls not supportred');
}
},
formatSeconds(value) {
let result = parseInt(value)
let h = Math.floor(result / 3600) < 10 ? '0' + Math.floor(result / 3600) : Math.floor(result / 3600);
let m = Math.floor((result / 60 % 60)) < 10 ? '0' + Math.floor((result / 60 % 60)) : Math.floor((result / 60 % 60));
let s = Math.floor((result % 60)) < 10 ? '0' + Math.floor((result % 60)) : Math.floor((result % 60));
let res = '';
if (h !== '00') res += `${h}:`;
if (m !== '00') res += `${m}:`;
res += `${s}`;
if (value < 60) {
res = '00:' + res;
}
// console.log('sec', res);
return res;
},
audioInit() {
// this.showLoading()
this.setData({
percent: 0,
duration: 0,
time: 0,
})
var self = this;
var data = this.audio;
console.log('audioInit', data);
var au = this.$refs['audio'];
console.log('audio elm', au);
au.title = data.name_ug;
var audioContext = au;
self.audioContext = au;
au.autoplay = true;
if (data.audio_url != '') {
data.audio_url = encodeURI(data.audio_url);
}
console.log('data.audio_url', data.audio_url);
audioContext.src = data.audio_url;
audioContext.startTime = 0;
audioContext.onplay = function (res) {
self.hideLoading();
self.setData({
playing: true,
duration: 0,
play_action: 'play',
})
// console.log('开始播放 开始时间:', audioContext.startTime, '全部时长:', audioContext.duration, '当前时间:', audioContext.currentTime)
self.setData({
play_action: 'play',
isPlay: true,
})
}
audioContext.onwaiting = function (res) {
self.showLoading()
}
audioContext.onpause = function (res) {
self.setData({
play_action: 'pause',
isPlay: false,
})
self.setData({
playing: false,
})
}
audioContext.ontimeupdate = function (res) {
// self.hideLoading()
// //console.log('time update startTime:', audioContext.startTime, audioContext.duration, audioContext.currentTime);
var tm = audioContext.currentTime;
var duration = audioContext.duration;
var dr = audioContext.duration;
// //console.log('audio time update', tm, duration);
var c_time = tm;
//时间转换秒,分钟,小时
c_time = c_time.toFixed(1);
if (c_time > 60) {
var mi = parseInt(c_time / 60);
var si = c_time % 60;
if (mi.length == 1) {
mi = "0" + mi;
}
si = si.toFixed(0);
if (si.length == 1) {
si = "0" + si;
}
c_time = mi + ':' + si;
}
// if (duration == null) {
// self.setData({
// percent: 0,
// })
// return;
// }
var d_time = duration.toFixed(0);
console.log('长度 d_time ', res, d_time);
if (d_time > 60) {
var mi = parseInt(d_time / 60);
if (mi.length == 1) {
mi = "0" + mi;
}
var si = d_time % 60;
si = si.toFixed(0);
if (si.length == 1) {
si = "0" + si;
}
d_time = mi + ':' + si;
}
// if(isNaN(d_time)){
// d_time=0;
// }
self.setData({
original_duration: dr,
current_time: c_time,
tm: tm,
duration: self.formatSeconds(dr),//d_time,
time: self.formatSeconds(tm),
// duration: d_time,
// time: c_time,
});
var percent = tm / duration;
percent = percent * 100;
self.setData({
percent: percent
});
}
// audioContext.onerror = function (res) {
// console.log('error',res);
// return;
// self.hideLoading()
// self.setData({
// playing: false,
// })
// //console.log(res.errMsg);
// //console.log(res.errCode);
// self.msg('خاتالىق كۆرۈلدى كېيىنكىسىگە ئالمىشىۋاتىدۇ');
// self.nextOne(true);
// }
audioContext.onended = function (res) {
//console.log('ended', res);
self.nextOne(true);
}
// self.getCourse();
// setTimeout(() => {
// audioContext.pause();
// }, 1500);
},
msg(msg, success = true) {
var self = this;
self.setData({
modalName: 'bottomModal',
modal_msg: msg,
success: success
})
setTimeout(() => {
self.setData({
modalName: null,
modal_msg: null,
success: false
})
}, 2000);
},
read(id) {
_self.location.href = '/mobile/read?id=' + id;
},
listenMore() {
var audio = this.audio;
_self.location.href = '/mobile/read?id=' + audio.course.id
},
play(e) {
console.log('play',e);
var self = this;
var data = e.currentTarget.dataset;
var action = data.action;
if (action == 'play') {
var p = self.isPlay;
var pp = !p;
if (p) {
self.audioContext.pause();
} else {
self.audioContext.play();
}
self.setData({
isPlay: pp
});
}
switch (action) {
case "back":
wx.navigateBack({
delta: 0,
})
break;
case "pre":
self.next(false);
break;
case "next":
self.next(true);
break;
case "forward":
break;
}
},
next(nextOne = true) {
var self = this;
var items = self.hotMusic;
console.log('next', items);
if (items != undefined) {
var audio_index = self.audio_index;
// if (nextOne) {
// audio_index++;
// if (audio_index > items.length - 1) {
// audio_index = 0;
// }else{
// }
// } else {
// audio_index--;
// if (audio_index < 0) {
// audio_index = items.length - 1;
// }else{
// }
// }
audio_index = Math.floor((Math.random() * items.length));
var audio = items[audio_index];
self.setData({
audio_index: audio_index,
audio: audio
})
self.audioInit();
}
},
},
})
const vm = app.mount('#app')
console.log('vm', vm);
其中的 template 是 刚才复制好的 页面文件,
app.js 内容如下
var default_background='bg-green';
function getData(url, param, method, success_function, fail_function) {
// var url = this.server_url + url;
url = window.location.origin + '/' + url;
// console.log('getData', url, param, method);
if (method.toLowerCase() == 'get') {
axios.get(url, {
params: param,
headers:
{
'code': 'some code'
}
}).then((response) => {
// console.log('axios get', url);
// console.log('res', response)
if (response.status == 200) {
success_function(response.data);
} else {
fail_function(response.data);
}
}).catch(function (error) {
console.log('error', error);
})
} else {
axios.post(url, param,{
headers:
{
'code': 'some code'
}
}).then((response) => {
// console.log('axios post', url);
// console.log('res', response)
if (response.status == 200) {
success_function(response.data);
} else {
fail_function(response.data);
}
}).catch(function (error) {
// console.log('error', error);
fail_function(error);
})
}
}
function upload(url, file, params, success_function, fail_function) {
url = window.location.origin + '/' + url;
let formData = new FormData();
var keys = Object.keys(params);
for (var i = 0; i < keys.length; i++) {
formData.set(keys[i], params[keys[i]]);
}
formData.set("file", file);
axios
.post(url, formData, {
headers: {
"Content-type": "multipart/form-data"
}
}).then((response) => {
// console.log('axios post', url);
// console.log('res', response)
if (response.status == 200) {
success_function(response.data);
} else {
fail_function(response.data);
}
}).catch(function (error) {
// console.log('error', error);
fail_function(error);
})
}
function setStorage(key,value){
window.localStorage.setItem(key,value);
}
function getStorage(key){
return window.localStorage.getItem(key);
}
function getMainColor(){
var bg=window.localStorage.getItem('bg');
if(bg==null){
setStorage('bg',default_background);
bg=default_background;
}
return bg;
}
function setTitle(title){
document.title=title;
}
try{
Vue.config.productionTip= false;
}catch{
}