linwu 9 ヶ月 前
コミット
aaf22db852

+ 12 - 0
app/mobile/common.php

@@ -37,3 +37,15 @@ function get_user_id()
     return $sessionUserId;
 }
 
+function get_soldier()
+{
+    $id = session('mobile.soldier.id');
+    if (empty($id)) {
+        $response = redirect('/mobile/soldier/login');
+        throw new \think\exception\HttpResponseException($response);
+    }
+
+    $soldier = \app\common\model\SoldierModel::find($id);
+    return $soldier;
+}
+

+ 153 - 1
app/mobile/controller/Soldier.php

@@ -2,6 +2,10 @@
 
 namespace app\mobile\controller;
 
+use app\common\model\SoldierModel;
+use app\common\model\SoldierVideoModel;
+use app\common\model\SoldierVideoSeriesModel;
+use app\common\model\SoldierVideoWatchModel;
 use app\mobile\MobileBaseController;
 
 class Soldier extends MobileBaseController
@@ -15,7 +19,155 @@ class Soldier extends MobileBaseController
     {
         $param = input('post.');
         if (empty($param['mobile']) || empty($param['password'])) {
-            ajax_return(1,'请输入账号或密码');
+            ajax_return(1, '请输入手机号或密码');
         }
+
+        $soldier = SoldierModel::where('mobile', $param['mobile'])->find();
+        if (empty($soldier)) {
+            ajax_return(1, '手机号不存在');
+        }
+        if ($soldier['password'] != md5(md5($soldier['salt']) . $param['password'])) {
+            ajax_return(1, '密码错误');
+        }
+
+        session('mobile.soldier.id', $soldier['id']);
+        ajax_return();
+    }
+
+    public function index()
+    {
+        $soldier = get_soldier();
+
+        return view('');
+    }
+
+    public function listVideo()
+    {
+        $soldier = get_soldier();
+
+        $where = [
+            ['status', '=', SoldierVideoSeriesModel::STATUS_SHOW],
+        ];
+        $list  = SoldierVideoSeriesModel::where($where)
+            ->order(['priority' => 'desc', 'id' => 'desc'])
+            ->limit(input('limit', 5))
+            ->page(input('page', 1))
+            ->select();
+        foreach ($list as $v) {
+            $video_list = SoldierVideoModel::where('series_id', $v['id'])
+                ->where('status', SoldierVideoModel::STATUS_SHOW)
+                ->limit(3)
+                ->order('priority desc')
+                ->select();
+
+            $video_ids  = $video_list->column('id');
+            $watch_list = SoldierVideoWatchModel::where('user_id', $soldier['id'])
+                ->where('video_id', 'in', $video_ids)
+                ->column('status', 'video_id');
+            foreach ($video_list as $video) {
+                if (empty($watch_list[$video['id']])) {
+                    $video['watch_status'] = 1;
+                } else {
+                    $video['watch_status'] = $watch_list[$video['id']];
+                }
+                $video['watch_text'] = SoldierVideoWatchModel::STATUS[$video['watch_status']];
+            }
+            $v['video'] = $video_list;
+        }
+
+        ajax_success($list);
+    }
+
+    public function series()
+    {
+        $soldier = get_soldier();
+        $id      = input('id/d', 0);
+        empty($id) && jump('参数错误');
+
+        $series = SoldierVideoSeriesModel::find($id);
+
+        return view('', ['id' => $id, 'series' => $series]);
+    }
+
+    public function listSeries()
+    {
+        $soldier = get_soldier();
+        $id      = input('id/d', 0);
+        empty($id) && jump('参数错误');
+
+        $list = SoldierVideoModel::where('series_id', $id)
+            ->where('status', SoldierVideoModel::STATUS_SHOW)
+            ->order(['priority' => 'desc'])
+            ->limit(input('limit', 10))
+            ->page(input('page', 1))
+            ->select();
+
+        $video_ids  = $list->column('id');
+        $watch_list = SoldierVideoWatchModel::where('user_id', $soldier['id'])
+            ->where('video_id', 'in', $video_ids)
+            ->column('status', 'video_id');
+        foreach ($list as $video) {
+            if (empty($watch_list[$video['id']])) {
+                $video['watch_status'] = 1;
+            } else {
+                $video['watch_status'] = $watch_list[$video['id']];
+            }
+            $video['watch_text'] = SoldierVideoWatchModel::STATUS[$video['watch_status']];
+        }
+
+        ajax_success($list);
+    }
+
+    public function video()
+    {
+        $soldier = get_soldier();
+        $id      = input('id/d', 0);
+        empty($id) && jump('参数错误');
+
+        $video        = SoldierVideoModel::find($id);
+        $watch_status = SoldierVideoWatchModel::where('user_id', $soldier['id'])
+            ->where('video_id', $id)
+            ->value('status');
+        if (empty($watch_status)) {
+            $video['watch_status'] = 1;
+        } else {
+            $video['watch_status'] = $watch_status;
+        }
+        $video['watch_text'] = SoldierVideoWatchModel::STATUS[$video['watch_status']];
+
+        $video_list = SoldierVideoModel::where('series_id', $video['series_id'])
+            ->where(function ($query) use ($video) {
+                $query->where('id', '>', $video['id'])->whereOr('priority', '<', $video['priority']);
+            })->order(['priority' => 'desc'])
+            ->limit(5)
+            ->select();
+        $video_ids  = $video_list->column('id');
+        $watch_list = SoldierVideoWatchModel::where('user_id', $soldier['id'])
+            ->where('video_id', 'in', $video_ids)
+            ->column('status', 'video_id');
+        foreach ($video_list as $video_item) {
+            if (empty($watch_list[$video_item['id']])) {
+                $video_item['watch_status'] = 1;
+            } else {
+                $video_item['watch_status'] = $watch_list[$video_item['id']];
+            }
+            $video_item['watch_text'] = SoldierVideoWatchModel::STATUS[$video_item['watch_status']];
+        }
+
+        return view('', [
+            'video'      => $video,
+            'video_list' => $video_list,
+        ]);
+    }
+
+    public function videoEnd()
+    {
+        $soldier = get_soldier();
+        $id = input('id/d',0);
+        empty($id) && jump('参数错误');
+
+        SoldierVideoWatchModel::update(['status'=>SoldierVideoWatchModel::STATUS_FINISH],['user_id'=>$soldier['id'],'video_id'=>$id]);
+
+        ajax_return();
     }
 }

+ 1 - 1
app/mobile/view/public/meta_header.html

@@ -1,5 +1,5 @@
 <meta charset="utf-8">
-<title>演示项目</title>
+<title>晋江市退役军人事务局适应性培训线上培训平台</title>
 <meta name="renderer" content="webkit">
 <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover"/>
 <meta name="apple-mobile-web-app-capable" content="yes" />

+ 113 - 0
app/mobile/view/soldier/index.html

@@ -0,0 +1,113 @@
+{extend name="public/base"/}
+{block name="css"}
+<style>
+    .series-title {font-size: 16px;}
+    .series-time {font-size: 12px;color: #999;}
+
+    .lw-list article{border-radius: 5px;width:95%;height:110px;display:flex;align-items:center;background:white;padding:5px 10px;}
+    .lw-list article .s-left {height:90px;display:flex;flex-direction:column;width: 100%;margin-left:auto;}
+    .lw-list article .s-left.image {width: calc(100% - 120px);}
+    .lw-list article .s-left .s-title{font-size: 16px;text-overflow: ellipsis;display: -webkit-box;overflow: hidden;-webkit-box-orient: vertical;-webkit-line-clamp: 3;text-align:left;color:#323233;}
+    .lw-list article .s-left .s-time{font-size: 12px;color:#999;margin-top:auto;text-align:right;}
+    .lw-list article .s-left .s-color3{color:var(--blue);}
+    .lw-list article .s-left .s-color2{color:var(--red);}
+    .lw-list article .s-right {width:110px;}
+</style>
+{/block}
+{block name="body"}
+<van-nav-bar
+        class="nav-theme"
+        :fixed="true"
+        :placeholder="true"
+>
+    <template #title>
+        <span class="text-white">首页</span>
+    </template>
+</van-nav-bar>
+
+<van-pull-refresh v-model="refreshing" @refresh="onRefresh">
+    <van-list
+            v-model:loading="loading"
+            :finished="finished"
+            finished-text="没有更多了"
+            @load="onList"
+    >
+        <van-collapse v-model="activeNames">
+            <van-collapse-item :name="item.id" v-for="item in list">
+                <template #title>
+                    <div class="series-title">{{item.title}}</div>
+                    <div class="series-time">{{item.start_time}} 至 {{item.end_time}}</div>
+                </template>
+                <div class="lw-list">
+                    <article v-for="video in item.video" @click="toVideo(video.id)">
+                        <section class="s-right">
+                            <van-image
+                                    width="110"
+                                    height="85"
+                                    fit="cover"
+                                    :src="video.main_image"
+                            ></van-image>
+                        </section>
+                        <section class="s-left image">
+                            <div class="s-title">{{video.title}}</div>
+                            <div class="s-time" :class="'s-color'+video.watch_status">{{video.watch_text}}</div>
+                        </section>
+                    </article>
+                    <div class="van-list__finished-text" @click="toSeries(item.id)">更多视频</div>
+                </div>
+            </van-collapse-item>
+        </van-collapse>
+    </van-list>
+</van-pull-refresh>
+
+{/block}
+{block name="script"}
+<script>
+    function v_setup() {
+        let base = {};
+
+        base.activeNames = Vue.ref([]);
+
+        //列表
+        base.page = Vue.ref(1);
+        base.loading = Vue.ref(false);
+        base.finished = Vue.ref(false);
+        base.refreshing = Vue.ref(false);
+        base.list = Vue.reactive([]);
+        base.onList = () => {
+            let param = {};
+            param.page = base.page.value;
+            base.page.value++;
+
+            postJson("soldier/listVideo", param).then( ({data}) => {
+                base.loading.value = false;
+                if (base.refreshing.value) base.refreshing.value = false;
+                if (data.length === 0) {
+                    base.finished.value = true;
+                } else {
+                    base.list.push(...data);
+                }
+            });
+
+        };
+        base.onRefresh = () => {
+            base.list = Vue.reactive([]);
+            base.page.value = 1;
+            base.loading.value = true;
+            base.finished.value = false;
+
+            base.onList();
+        };
+
+        base.toSeries = (id) => {
+            location.href = "{:url('soldier/series')}?id=" + id;
+        };
+        base.toVideo = (id) => {
+            location.href = "{:url('soldier/video')}?id=" + id;
+        };
+
+
+        return base;
+    }
+</script>
+{/block}

+ 2 - 2
app/mobile/view/soldier/login.html

@@ -66,8 +66,8 @@
 {block name="body"}
 <div class="content">
     <div class="topBox">
-        <h3>WELCOME</h3>
-        <h3>欢迎使用</h3>
+        <h3>晋江市退役军人事务局</h3>
+        <h3>适应性培训线上培训平台</h3>
     </div>
     <div class="inputBox">
         <div class="ipt">

+ 101 - 0
app/mobile/view/soldier/series.html

@@ -0,0 +1,101 @@
+{extend name="public/base"/}
+{block name="css"}
+<style>
+    .series-title {font-size: 16px;}
+    .series-time {font-size: 12px;color: #999;}
+
+    .lw-list article{border-radius: 5px;width:100%;height:110px;display:flex;align-items:center;background:white;padding:5px 10px;}
+    .lw-list article .s-left {height:90px;display:flex;flex-direction:column;width: 100%;margin-left:auto;}
+    .lw-list article .s-left.image {width: calc(100% - 120px);}
+    .lw-list article .s-left .s-title{font-size: 16px;text-overflow: ellipsis;display: -webkit-box;overflow: hidden;-webkit-box-orient: vertical;-webkit-line-clamp: 3;text-align:left;color:#323233;}
+    .lw-list article .s-left .s-time{font-size: 12px;color:#999;margin-top:auto;text-align:right;}
+    .lw-list article .s-left .s-color3{color:var(--blue);}
+    .lw-list article .s-left .s-color2{color:var(--red);}
+    .lw-list article .s-right {width:110px;}
+</style>
+{/block}
+{block name="body"}
+<van-nav-bar
+        class="nav-theme"
+        :fixed="true"
+        :placeholder="true"
+>
+    <template #title>
+        <span class="text-white">{$series.title}</span>
+    </template>
+</van-nav-bar>
+
+<van-pull-refresh v-model="refreshing" @refresh="onRefresh">
+    <van-list
+            v-model:loading="loading"
+            :finished="finished"
+            finished-text="没有更多了"
+            @load="onList"
+    >
+        <div class="lw-list">
+            <article v-for="video in list" @click="toVideo(video.id)">
+                <section class="s-right">
+                    <van-image
+                            width="110"
+                            height="85"
+                            fit="cover"
+                            :src="video.main_image"
+                    ></van-image>
+                </section>
+                <section class="s-left image">
+                    <div class="s-title">{{video.title}}</div>
+                    <div class="s-time" :class="'s-color'+video.watch_status">{{video.watch_text}}</div>
+                </section>
+            </article>
+        </div>
+    </van-list>
+</van-pull-refresh>
+
+{/block}
+{block name="script"}
+<script>
+    function v_setup() {
+        let base = {};
+
+        base.activeNames = Vue.ref([]);
+
+        //列表
+        base.page = Vue.ref(1);
+        base.loading = Vue.ref(false);
+        base.finished = Vue.ref(false);
+        base.refreshing = Vue.ref(false);
+        base.list = Vue.reactive([]);
+        base.onList = () => {
+            let param = {};
+            param.page = base.page.value;
+            base.page.value++;
+
+            postJson("soldier/listSeries?id={$id}", param).then( ({data}) => {
+                base.loading.value = false;
+                if (base.refreshing.value) base.refreshing.value = false;
+                if (data.length === 0) {
+                    base.finished.value = true;
+                } else {
+                    base.list.push(...data);
+                }
+            });
+
+        };
+        base.onRefresh = () => {
+            base.list = Vue.reactive([]);
+            base.page.value = 1;
+            base.loading.value = true;
+            base.finished.value = false;
+
+            base.onList();
+        };
+
+        base.toVideo = (id) => {
+            location.href = "{:url('soldier/video')}?id=" + id;
+        };
+
+
+        return base;
+    }
+</script>
+{/block}

+ 121 - 0
app/mobile/view/soldier/video.html

@@ -0,0 +1,121 @@
+{extend name="public/base"/}
+{block name="css"}
+<style>
+    .series-title {font-size: 16px;}
+    .series-time {font-size: 12px;color: #999;}
+
+    .lw-list article{border-radius: 5px;width:100%;height:110px;display:flex;align-items:center;background:white;padding:5px 10px;}
+    .lw-list article .s-left {height:90px;display:flex;flex-direction:column;width: 100%;margin-left:auto;}
+    .lw-list article .s-left.image {width: calc(100% - 120px);}
+    .lw-list article .s-left .s-title{font-size: 16px;text-overflow: ellipsis;display: -webkit-box;overflow: hidden;-webkit-box-orient: vertical;-webkit-line-clamp: 3;text-align:left;color:#323233;}
+    .lw-list article .s-left .s-time{font-size: 12px;color:#999;margin-top:auto;text-align:right;}
+    .lw-list article .s-left .s-color3{color:var(--blue);}
+    .lw-list article .s-left .s-color2{color:var(--red);}
+    .lw-list article .s-right {width:110px;}
+
+    .s-status {font-size: 14px;color:#999;margin-top:auto;text-align:right;padding-right:20px;}
+    .s-status.s-color3{color:var(--blue);}
+    .s-status.s-color2{color:var(--red);}
+
+    .lw-header {background:white;padding:5px 10px;}
+</style>
+{/block}
+{block name="body"}
+<van-nav-bar
+        class="nav-theme"
+        :placeholder="true"
+        left-text="返回"
+        left-arrow
+        @click-left="onBack"
+>
+    <template #title>
+        <span class="text-white">学习视频</span>
+    </template>
+</van-nav-bar>
+<h3 style="text-align:center;">{$video.title}</h3>
+<div class="s-status" :class="'s-color'+video.watch_status">{{video.watch_text}}</div>
+<video src="{$video.video}"
+       style="width:100%;margin:20px auto;"
+       id="myVideo"
+       controls
+       loop="false"
+       currenttime="15"
+       @timeupdate="timeUpdate"
+       @ended="VideoEnded">
+</video>
+<div class="lw-header">相关视频</div>
+<div class="lw-list">
+    <article v-for="video in list" @click="toVideo(video.id)">
+        <section class="s-right">
+            <van-image
+                    width="110"
+                    height="85"
+                    fit="cover"
+                    :src="video.main_image"
+            ></van-image>
+        </section>
+        <section class="s-left image">
+            <div class="s-title">{{video.title}}</div>
+            <div class="s-time" :class="'s-color'+video.watch_status">{{video.watch_text}}</div>
+        </section>
+    </article>
+
+    <div class="van-list__finished-text" v-if="list.length == 0">暂无更多视频</div>
+</div>
+
+{/block}
+{block name="script"}
+<script>
+    function v_setup() {
+        let base = {};
+
+        base.video = Vue.reactive({$video});
+        base.list = Vue.reactive({$video_list});
+
+
+        base.toVideo = (id) => {
+            location.href = "{:url('soldier/video')}?id=" + id;
+        };
+
+        //视频
+        let time = localStorage.getItem('video_{$video.id}');
+        base.currentTime = time ? time : 0; //上次观看时间
+        base.newsschedule = base.currentTime; //当前观看时间
+        base.timeUpdate = (e) => {
+            if (e.target.currentTime - base.newsschedule > 1) {
+                vant.showToast('不允许拖动进度条');
+                let videoContext = document.getElementById("myVideo");
+                videoContext.currentTime = base.newsschedule;
+            } else {
+                base.newsschedule = e.target.currentTime;
+                localStorage.setItem('video_{$video.id}',base.newsschedule);
+            }
+        };
+        base.VideoEnded = () => {
+            postJson('/soldier/videoEnd', {id:base.video.id}).then(({data, code}) => {
+                base.video.watch_status = 3;
+                base.video.watch_text = '已完成';
+                vant.showDialog({ message: '学习完毕' });
+            })
+        };
+
+        base.onBack = () => {
+            history.back();
+        };
+
+        Vue.onMounted(() => {
+            let videoContext = document.getElementById("myVideo");
+            if (videoContext) {
+                if (base.currentTime > 0) {
+                    vant.showToast('上次已观看到'+ parseInt(base.currentTime)+'秒');
+                }
+                videoContext.currentTime = base.currentTime;
+            }
+        });
+
+        return base;
+    }
+
+
+</script>
+{/block}