linwu 2 月之前
父节点
当前提交
426f397f71
共有 82 个文件被更改,包括 15113 次插入7 次删除
  1. 6 1
      app/ExceptionHandle.php
  2. 232 0
      app/admin/controller/Recruit.php
  3. 19 0
      app/admin/controller/RecruitApply.php
  4. 39 0
      app/admin/controller/Select.php
  5. 25 0
      app/admin/validate/RecruitNewsValidate.php
  6. 145 0
      app/admin/view/recruit/index.html
  7. 190 0
      app/admin/view/recruit/news.html
  8. 111 0
      app/admin/view/recruit/news_form.html
  9. 171 0
      app/admin/view/recruit/post_form.html
  10. 783 0
      app/admin/view/recruit/recruit_form.html
  11. 88 0
      app/admin/view/select/recruit.html
  12. 1 1
      app/admin/view/slide/slide_form.html
  13. 31 1
      app/common.php
  14. 9 0
      app/common/model/NoticeModel.php
  15. 32 0
      app/common/model/RecruitAppointBasicModel.php
  16. 16 0
      app/common/model/RecruitAppointInfoModel.php
  17. 191 0
      app/common/model/RecruitModel.php
  18. 39 0
      app/common/model/RecruitNewsModel.php
  19. 16 0
      app/common/model/RecruitPostModel.php
  20. 2 1
      app/common/service/SmsService.php
  21. 1 0
      app/home/.htaccess
  22. 67 0
      app/home/HomeBaseController.php
  23. 62 0
      app/home/common.php
  24. 18 0
      app/home/config/view.php
  25. 18 0
      app/home/controller/About.php
  26. 18 0
      app/home/controller/Help.php
  27. 53 0
      app/home/controller/Index.php
  28. 102 0
      app/home/controller/Login.php
  29. 96 0
      app/home/controller/My.php
  30. 75 0
      app/home/controller/News.php
  31. 69 0
      app/home/controller/Notice.php
  32. 72 0
      app/home/controller/Recruit.php
  33. 98 0
      app/home/controller/Register.php
  34. 47 0
      app/home/controller/Upload.php
  35. 17 0
      app/home/event.php
  36. 10 0
      app/home/middleware.php
  37. 9 0
      app/home/provider.php
  38. 147 0
      app/home/view/about/index.html
  39. 53 0
      app/home/view/help/index.html
  40. 16 0
      app/home/view/index/base.html
  41. 107 0
      app/home/view/index/index.html
  42. 279 0
      app/home/view/login/index.html
  43. 75 0
      app/home/view/my/account.html
  44. 75 0
      app/home/view/my/feedback.html
  45. 75 0
      app/home/view/my/index.html
  46. 646 0
      app/home/view/my/info.html
  47. 75 0
      app/home/view/my/tip.html
  48. 49 0
      app/home/view/news/detail.html
  49. 96 0
      app/home/view/news/index.html
  50. 29 0
      app/home/view/notice/detail.html
  51. 100 0
      app/home/view/notice/index.html
  52. 108 0
      app/home/view/public/base.html
  53. 47 0
      app/home/view/public/jump.html
  54. 51 0
      app/home/view/public/list_load.html
  55. 19 0
      app/home/view/public/meta_header.html
  56. 96 0
      app/home/view/recruit/index.html
  57. 283 0
      app/home/view/register/index.html
  58. 1 1
      config/app.php
  59. 4 2
      extend/chuanglan/Chuanglan.php
  60. 二进制
      public/favicon.ico
  61. 0 0
      public/static/home/css/element.css
  62. 0 0
      public/static/home/css/element_plus_2.8.2.css
  63. 二进制
      public/static/home/css/fonts/element-icons.woff
  64. 26 0
      public/static/home/css/index.css
  65. 47 0
      public/static/home/css/style.css
  66. 二进制
      public/static/home/images/123.png
  67. 二进制
      public/static/home/images/about.jpg
  68. 二进制
      public/static/home/images/bg_back.png
  69. 二进制
      public/static/home/images/img_logo.png
  70. 二进制
      public/static/home/images/index_logo.png
  71. 二进制
      public/static/home/images/location.png
  72. 二进制
      public/static/home/images/logo.png
  73. 二进制
      public/static/home/images/nebanner.jpg
  74. 二进制
      public/static/home/images/tel.png
  75. 0 0
      public/static/home/js/area.js
  76. 50 0
      public/static/home/js/axios_instance.js
  77. 0 0
      public/static/home/js/element.js
  78. 9388 0
      public/static/home/js/element_plus_2.8.2.js
  79. 2 0
      public/static/home/js/element_plus_icons.js
  80. 190 0
      public/static/home/js/element_plus_zhcn.js
  81. 0 0
      public/static/home/js/qs.min.js
  82. 1 0
      public/static/home/js/vue.min.js

+ 6 - 1
app/ExceptionHandle.php

@@ -51,8 +51,13 @@ class ExceptionHandle extends Handle
     public function render($request, Throwable $e): Response
     {
         // 添加自定义异常处理机制
+//        return json([
+//            'code' => $e->getCode(),
+//            'msg'  => $e->getMessage(),
+//            'data' => null
+//        ], 500);
 
         // 其他错误交给系统处理
-        return parent::render($request, $e);
+         return parent::render($request, $e);
     }
 }

+ 232 - 0
app/admin/controller/Recruit.php

@@ -0,0 +1,232 @@
+<?php
+
+namespace app\admin\controller;
+
+use app\admin\AdminBaseController;
+use app\admin\validate\RecruitNewsValidate;
+use app\common\model\RecruitAppointInfoModel;
+use app\common\model\RecruitModel;
+use app\common\model\RecruitNewsModel;
+use app\common\model\RecruitPostModel;
+use think\exception\ValidateException;
+
+class Recruit extends AdminBaseController
+{
+
+    /**
+     * 列表
+     */
+    public function index()
+    {
+        return view('', [
+            'status_list' => RecruitModel::STATUS_INT,
+        ]);
+    }
+
+    public function listRecruit()
+    {
+        $map   = $this->dealEqualInput(['status'], $this->dealLikeInput(['name']));
+        $list  = RecruitModel::where($map)
+            ->order(['priority' => 'desc', 'update_time' => 'desc'])
+            ->limit(input('limit'))
+            ->page(input('page'))
+            ->append(['status_text'])
+            ->select();
+        $count = RecruitModel::where($map)->count();
+        if ($count == 0) {
+            ajax_return(1, '未查询到数据');
+        }
+        list_return($list, $count);
+    }
+
+    public function delRecruit()
+    {
+        $id_arr = input('id_arr/a');
+        RecruitModel::destroy($id_arr);
+        ajax_return();
+    }
+
+    /**
+     * 编辑
+     */
+    public function recruitForm()
+    {
+        $id   = input('id/d, 0');
+        $info = RecruitModel::find($id);
+
+        return view('', [
+            'info'                      => $info,
+            'forms'                     => RecruitModel::FORMS,
+            'status_list'               => RecruitModel::STATUS_INT,
+            'status_step_list'          => RecruitModel::STATUS_STEP,
+            'status_ticket_type_list'   => RecruitModel::STATUS_TICKET_TYPE,
+            'status_ticket_status_list' => RecruitModel::STATUS_TICKET_STATUS,
+            'status_score_list'         => RecruitModel::STATUS_SCORE,
+        ]);
+    }
+
+    public function editRecruit()
+    {
+        $data = input('post.');
+        if (empty($data['forms'])) {
+            ajax_return(1, '请选择表单模块');
+        }
+        if (empty($data['introduction'])) {
+            ajax_return(1, '请输入招考正文');
+        }
+        if ($data['pen_confirm'] == RecruitModel::STATUS_YES) {
+            if (empty($data['pen_confirm_start']) || empty($data['pen_confirm_end'])) {
+                ajax_return(1, '请选择确认笔试时间');
+            }
+        }
+
+        $data['step'] = array_values($data['step']);
+        $data         = del_empty($data);
+        if (empty($data['id'])) {
+            $data['name_en'] = uniqid();
+            RecruitModel::create($data);
+        } else {
+            RecruitModel::update($data, ['id' => $data['id']]);
+        }
+
+        ajax_return();
+    }
+
+    /**
+     * 编辑
+     */
+    public function postForm()
+    {
+        $id   = input('id/d, 0');
+        $info = RecruitModel::find($id);
+        if (empty($info)) {
+            return '该招考不存在';
+        }
+
+        $list = RecruitPostModel::field(['id', 'code', 'name', 'number', 'limit', 'special'])
+            ->where('recruit_id', $id)
+            ->select();
+        if ($list->isEmpty()) {
+            $list = json_encode([['id' => 0, 'code' => '', 'name' => '', 'number' => 1, 'limit' => '{}', 'special' => '{}']]);
+        }
+
+        return view('', [
+            'info' => $info,
+            'list' => $list,
+        ]);
+    }
+
+    public function editPost()
+    {
+        $data       = input('post.');
+        $recruit_id = $data['recruit_id'];
+        $list       = $data['list'];
+        if (empty($recruit_id)) {
+            ajax_return(1, '请选择招考场次');
+        }
+        if (empty($list)) {
+            ajax_return(1, '请添加岗位');
+        }
+
+        //数据分离
+        $insert     = [];
+        $update     = [];
+        $ids        = RecruitPostModel::where('recruit_id', $recruit_id)->column('id');
+        $data_ids   = array_column($list, 'id');
+        $delete_ids = array_diff($ids, $data_ids);
+        $check      = RecruitAppointInfoModel::whereIn('post_id', $delete_ids)->find();
+        if (!empty($check)) {
+            ajax_return(1, "删除的岗位已有人报名,无法删除,请刷新页面重新录入");
+        }
+        foreach ($list as $v) {
+            if (empty($v['id'])) {
+                $insert[] = $v;
+            } else {
+                if (!in_array($v['id'], $delete_ids))
+                    $update[] = $v;
+            }
+        }
+
+        if (!empty($insert)) {
+            foreach ($insert as $v) {
+                $v['recruit_id'] = $recruit_id;
+                RecruitPostModel::create($v);
+            }
+        }
+        if (!empty($update)) {
+            foreach ($update as $v) {
+                RecruitPostModel::update($v);
+            }
+        }
+        if (!empty($delete_ids)) {
+            RecruitPostModel::destroy(array_values($delete_ids));
+        }
+
+        ajax_return();
+    }
+
+    /**
+     * 列表
+     */
+    public function news()
+    {
+        return view('', [
+            'status_list' => RecruitNewsModel::STATUS,
+        ]);
+    }
+
+    public function newsForm()
+    {
+        $id        = input('id/d, 0');
+        $info      = RecruitNewsModel::with('recruit')->find($id)->toArray();
+
+        return view('', [
+            'info'        => $info,
+            'status_list' => RecruitNewsModel::STATUS,
+        ]);
+    }
+
+    public function listNews()
+    {
+        $map = $this->dealEqualInput(['status','recruit_id'], $this->dealLikeInput(['title']));
+
+        $list  = RecruitNewsModel::with('recruit')
+            ->where($map)
+            ->order(['priority' => 'desc', 'id' => 'desc'])
+            ->limit(input('limit'))
+            ->page(input('page'))
+            ->append(['status_text'])
+            ->select();
+        $count = RecruitNewsModel::where($map)->count();
+
+        if ($count == 0) {
+            ajax_return(1, '未查询到数据');
+        }
+        list_return($list, $count);
+    }
+
+    public function delNews()
+    {
+        $id_arr = input('id_arr/a');
+        RecruitNewsModel::destroy($id_arr);
+        ajax_return();
+    }
+
+    public function editNews()
+    {
+        $data = input('post.');
+        try {
+            validate(RecruitNewsValidate::class)->check($data);
+        } catch (ValidateException $e) {
+            ajax_return(1, $e->getError());
+        }
+
+        if (empty($data['id'])) {
+            RecruitNewsModel::create($data);
+        } else {
+            RecruitNewsModel::update($data, ['id' => $data['id']]);
+        }
+
+        ajax_return();
+    }
+}

+ 19 - 0
app/admin/controller/RecruitApply.php

@@ -0,0 +1,19 @@
+<?php
+
+namespace app\admin\controller;
+
+use app\admin\AdminBaseController;
+use app\common\model\RecruitAppointInfoModel;
+use app\common\model\RecruitModel;
+use app\common\model\RecruitPostModel;
+
+class RecruitApply extends AdminBaseController
+{
+    /**
+     * 列表
+     */
+    /*public function index()
+    {
+        halt(input('id'));
+    }*/
+}

+ 39 - 0
app/admin/controller/Select.php

@@ -0,0 +1,39 @@
+<?php
+
+namespace app\admin\controller;
+
+use app\admin\AdminBaseController;
+use app\common\model\NoticeModel;
+use app\admin\validate\NoticeValidate;
+use app\common\model\RecruitModel;
+use think\exception\ValidateException;
+
+class Select extends AdminBaseController
+{
+
+    /**
+     * 列表
+     */
+    public function recruit()
+    {
+        return view('', [
+            'status_list' => RecruitModel::STATUS_INT,
+        ]);
+    }
+
+    public function listRecruit()
+    {
+        $map   = $this->dealEqualInput(['status'], $this->dealLikeInput(['name']));
+        $list  = RecruitModel::where($map)
+            ->order(['priority' => 'desc', 'update_time' => 'desc'])
+            ->limit(input('limit'))
+            ->page(input('page'))
+            ->append(['status_text'])
+            ->select();
+        $count = RecruitModel::where($map)->count();
+        if ($count == 0) {
+            ajax_return(1, '未查询到数据');
+        }
+        list_return($list, $count);
+    }
+}

+ 25 - 0
app/admin/validate/RecruitNewsValidate.php

@@ -0,0 +1,25 @@
+<?php
+
+namespace app\admin\validate;
+
+use think\Validate;
+
+class RecruitNewsValidate extends Validate
+{
+    protected $rule = [
+        'recruit_id' => 'require',
+        'title'      => 'require',
+        'priority'   => 'require|number',
+        'volume'     => 'require|number',
+    ];
+
+    protected $message = [
+        'recruit_id'       => '请选择招考场次',
+        'title'            => '标题不能为空',
+        'priority.require' => '排序不能为空',
+        'priority.number'  => '排序必须是数字',
+        'volume.require'   => '浏览量不能为空',
+        'volume.number'    => '浏览量必须是数字',
+    ];
+
+}

+ 145 - 0
app/admin/view/recruit/index.html

@@ -0,0 +1,145 @@
+<div class="layui-fluid">
+    <div class="layui-card">
+        <div class="layui-form layui-form-pane  layui-card-header layuiadmin-card-header-auto">
+            <div class="layui-form-item">
+                <div class="layui-inline">
+                    <label class="layui-form-label">招考名称</label>
+                    <div class="layui-input-block">
+                        <input type="text" name="name" placeholder="请输入" autocomplete="off" class="layui-input">
+                    </div>
+                </div>
+                <div class="layui-inline">
+                    <label class="layui-form-label">开启</label>
+                    <div class="layui-input-block">
+                        <select name="status">
+                            <option value="">全部状态</option>
+                            {volist name="status_list" id="v"}
+                            <option value="{$key}">{$v}</option>
+                            {/volist}
+                        </select>
+                    </div>
+                </div>
+                <div class="layui-inline">
+                    <button class="layui-btn" lay-submit lay-filter="{$lay_btn}">
+                        <i class="layui-icon layui-icon-search layuiadmin-button-btn"></i>
+                    </button>
+                </div>
+            </div>
+        </div>
+        <div class="layui-form layui-card-header layuiadmin-card-header-auto">
+            <button class="layui-btn layuiadmin-btn" data-type="add">添加</button>
+        </div>
+
+        <div class="layui-card-body">
+            <table id="{$lay_table}" lay-filter="{$lay_table}"></table>
+            <script type="text/html" id="setTpl">
+                <a class="layui-btn layui-btn-xs" lay-event="post">
+                    <i class="layui-icon layui-icon-list"></i>岗位管理
+                </a>
+                <a class="layui-btn layui-btn-xs" lay-href="{:url('recruitApply/index')}?id={{d.id}}" lay-text="{{d.company}}">
+                    <i class="layui-icon layui-icon-form"></i>报名管理
+                </a>
+                <a class="layui-btn layui-btn-normal layui-btn-xs" lay-event="edit">
+                    <i class="layui-icon layui-icon-edit"></i>编辑
+                </a>
+                <a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">
+                    <i class="layui-icon layui-icon-delete"></i>删除
+                </a>
+            </script>
+        </div>
+    </div>
+</div>
+
+<script>
+    layui.use(['index', 'admin', 'form', 'table'], function () {
+        const $ = layui.$;
+        const admin = layui.admin;
+        const form = layui.form;
+        const table = layui.table;
+        form.render();
+
+        table.render({
+            elem: '#{$lay_table}',
+            url: "{:url('recruit/listRecruit')}",
+            cols: [
+                [
+                    {field: 'name', title: '标题'},
+                    {field: 'company', title: '招聘主体', width: 300},
+                    {field: 'priority', title: '排序', width: 160},
+                    {field: 'status_text', title: '开启', width: 80, align: 'center'},
+                    {field: 'update_time', title: '发布时间', width: 160, align: 'center'},
+                    {title: '操作', width: 350, align: 'center', fixed: 'right', toolbar: '#setTpl'}
+                ]
+            ],
+            page: true,
+            limit: 50,
+            cellMinWidth: 150,
+            text: '对不起,加载出现异常!'
+        });
+
+        form.on('submit({$lay_btn})', function (data) {
+            table.reload('{$lay_table}', {
+                where: data.field,
+                page: {
+                    curr: 1
+                }
+            });
+        });
+
+        //事件
+        const active = {
+            add: function () {
+                const index = layer.open({
+                    type: 2,
+                    title: '添加招考',
+                    content: "{:url('recruit/recruitForm')}",
+                    maxmin: true,
+                    area: ['550px', '550px']
+                });
+                layer.full(index);
+            }
+        };
+
+        //监听工具条
+        table.on('tool({$lay_table})', function (obj) {
+            const data = obj.data;
+            if (obj.event === 'del') {
+                layer.confirm('确定删除吗?', function (index) {
+                    admin.req({
+                        url: "{:url('recruit/delRecruit')}",
+                        data: {
+                            id_arr: data.id
+                        },
+                        done: function (res) {
+                            layui.table.reload('{$lay_table}');
+                        }
+                    });
+                    layer.close(index);
+                });
+            } else if (obj.event === 'edit') {
+                const index = layer.open({
+                    type: 2,
+                    title: '编辑招考',
+                    content: "{:url('recruit/recruitForm')}?id=" + data.id,
+                    maxmin: true,
+                    area: ['550px', '550px']
+                });
+                layer.full(index);
+            } else if (obj.event === 'post') {
+                const index = layer.open({
+                    type: 2,
+                    title: '编辑岗位',
+                    content: "{:url('recruit/postForm')}?id=" + data.id,
+                    maxmin: true,
+                    area: ['550px', '550px']
+                });
+                layer.full(index);
+            }
+        });
+
+        $('.layui-btn.layuiadmin-btn').on('click', function () {
+            const type = $(this).data('type');
+            active[type] ? active[type].call(this) : '';
+        });
+    });
+</script>

+ 190 - 0
app/admin/view/recruit/news.html

@@ -0,0 +1,190 @@
+<div class="layui-fluid">
+    <div class="layui-card">
+        <div class="layui-form layui-form-pane layui-card-header layuiadmin-card-header-auto">
+            <div class="layui-form-item">
+                <div class="layui-inline">
+                    <label class="layui-form-label">标题</label>
+                    <div class="layui-input-block">
+                        <input type="text" name="title" placeholder="请输入" autocomplete="off" class="layui-input">
+                    </div>
+                </div>
+                <div class="layui-inline">
+                    <label class="layui-form-label">招考场次</label>
+                    <div class="layui-input-block">
+                        <input type="text" name="recruit_name" readonly placeholder="请选择" autocomplete="off" id="recruit_name" class="layui-input">
+                        <input type="hidden" name="recruit_id" value="0" id="recruit_id">
+                    </div>
+                </div>
+                <div class="layui-inline">
+                    <label class="layui-form-label">状态</label>
+                    <div class="layui-input-block">
+                        <select name="status">
+                            <option value="">全部状态</option>
+                            {volist name="status_list" id="v"}
+                            <option value="{$key}">{$v}</option>
+                            {/volist}
+                        </select>
+                    </div>
+                </div>
+                <div class="layui-inline">
+                    <button class="layui-btn" lay-submit lay-filter="{$lay_btn}">
+                        <i class="layui-icon layui-icon-search layuiadmin-button-btn"></i>
+                    </button>
+                </div>
+            </div>
+        </div>
+
+        <div class="layui-card-body">
+            <div style="padding-bottom: 10px;">
+                <button class="layui-btn layuiadmin-btn layui-btn-danger" data-type="del">删除</button>
+                <button class="layui-btn layuiadmin-btn" data-type="add">添加</button>
+            </div>
+            <table id="{$lay_table}" lay-filter="{$lay_table}"></table>
+            <script type="text/html" id="recruitTpl">
+                {{d.recruit.name}}
+            </script>
+            <script type="text/html" id="setTpl">
+                <a class="layui-btn layui-btn-normal layui-btn-xs" lay-event="edit">
+                    <i class="layui-icon layui-icon-edit"></i>编辑
+                </a>
+                <a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">
+                    <i class="layui-icon layui-icon-delete"></i>删除
+                </a>
+            </script>
+        </div>
+
+    </div>
+</div>
+
+<script>
+    layui.use(['index', 'form', 'set', 'table'], function () {
+        $ = layui.$;
+        const admin = layui.admin;
+        const form = layui.form;
+        const table = layui.table;
+        form.render();
+
+        //列表
+        table.render({
+            elem: '#{$lay_table}',
+            url: "{:url('recruit/listNews')}",
+            cols: [
+                [
+                    {type: 'checkbox', fixed: 'left'},
+                    {field: 'id', width: 80, title: '表ID', sort: true},
+                    {width: 100, title: '招考场次', templet: '#recruitTpl'},
+                    {field: 'title', title: '标题'},
+                    {field: 'create_time', width: 160, title: '发布时间', sort: true},
+                    {field: 'volume', width: 90, title: '浏览量', sort: true},
+                    {field: 'status_text', width: 90, title: '状态', minWidth: 80, align: 'center'},
+                    {field: 'priority', width: 80, title: '排序'},
+                    {title: '操作', width: 160, align: 'center', fixed: 'right', toolbar: '#setTpl'}
+                ]
+            ],
+            page: true,
+            limit: 50,
+            cellMinWidth: 150,
+            text: '对不起,加载出现异常!'
+        });
+
+        //监听搜索
+        form.on('submit({$lay_btn})', function (data) {
+            table.reload('{$lay_table}', {
+                where: data.field,
+                page: {
+                    curr: 1
+                }
+            });
+        });
+
+        //事件
+        const active = {
+            del: function () {
+                const check_status = table.checkStatus('{$lay_table}');
+                const check_data = check_status.data; //得到选中的数据
+                if (check_data.length === 0) {
+                    return layer.msg('请选择数据');
+                }
+                let id_arr = [];
+                for (let i = 0; i < check_data.length; i++) {
+                    id_arr.push(check_data[i].id);
+                }
+                layer.confirm('确定删除所选新闻吗?', function (index) {
+                    admin.req({
+                        url: "{:url('recruit/delNews')}",
+                        data: {
+                            id_arr: id_arr
+                        },
+                        done: function (res) {
+                            table.reload('{$lay_table}');
+                            layer.msg('已删除');
+                        }
+                    });
+                    layer.close(index);
+                });
+            },
+            add: function () {
+                const index = layer.open({
+                    type: 2,
+                    title: '添加新闻',
+                    content: "{:url('recruit/newsForm')}",
+                    maxmin: true,
+                    area: ['550px', '550px']
+                });
+                layer.full(index);
+            }
+        };
+
+        //监听工具条
+        table.on('tool({$lay_table})', function (obj) {
+            const data = obj.data;
+            if (obj.event === 'del') {
+                layer.confirm('确定删除此新闻吗?', function (index) {
+                    admin.req({
+                        url: "{:url('recruit/delNews')}",
+                        data: {
+                            id_arr: [data.id]
+                        },
+                        done: function (res) {
+                            obj.del();
+                            layer.msg('已删除');
+                        }
+                    });
+                    layer.close(index);
+                });
+            } else if (obj.event === 'edit') {
+                const index = layer.open({
+                    type: 2,
+                    title: '添加新闻',
+                    content: "{:url('recruit/newsForm')}?id=" + data.id,
+                    maxmin: true,
+                    area: ['550px', '550px']
+                });
+                layer.full(index);
+            }
+        });
+
+        $('.layui-btn.layuiadmin-btn').on('click', function () {
+            const type = $(this).data('type');
+            active[type] ? active[type].call(this) : '';
+        });
+
+        //选择招考场次
+        $('#recruit_name').click(function(){
+            const index = layer.open({
+                type: 2,
+                title: '选择招考',
+                content: "{:url('select/recruit')}",
+                maxmin: true,
+                area: ['1000px', '550px'],
+            });
+        });
+
+        window.selectRecruit = function(val){
+            $('#recruit_id').val(val.id);
+            $('#recruit_name').val(val.name);
+        }
+    });
+
+
+</script>

+ 111 - 0
app/admin/view/recruit/news_form.html

@@ -0,0 +1,111 @@
+<div class="layui-fluid">
+    <div class="layui-row layui-col-space15">
+        <div class="layui-col-md12">
+            <div class="layui-card">
+                <div class="layui-card-header">招考新闻</div>
+                <div class="layui-card-body" pad15>
+                    <div class="layui-form layui-form-pane" lay-filter="{$lay_table}">
+                        <input type="hidden" name="id" value="{:array_get($info,'id')}">
+                        <div class="layui-form-item">
+                            <label class="layui-form-label"><span style="color:#f90c05;">*</span>标题</label>
+                            <div class="layui-input-block">
+                                <input type="text" name="title" value="{:array_get($info,'title')}" lay-verify="required"
+                                       placeholder="请输入标题" autocomplete="off"
+                                       class="layui-input">
+                            </div>
+                        </div>
+                        <div class="layui-form-item">
+                            <label class="layui-form-label"><span style="color:#f90c05;">*</span>招考场次</label>
+                            <div class="layui-input-block">
+                                <input type="text" value="{:array_get($info,'recruit.name','')}" lay-verify="required"
+                                       placeholder="请选择招考场次" autocomplete="off" id="recruit_name" readonly
+                                       class="layui-input">
+                                <input type="hidden" name="recruit_id" value="{:array_get($info,'recruit.id',0)}" id="recruit_id">
+                            </div>
+                        </div>
+                        <div class="layui-form-item" id="form-content">
+                            <label class="layui-form-label"><span style="color:#f90c05;">*</span>详情</label>
+                            <div class="layui-input-block">
+                                <script id="editor" type="text/plain">{:array_get($info,'content')}</script>
+                            </div>
+                        </div>
+                        <div class="layui-form-item">
+                            <label class="layui-form-label"><span style="color:#f90c05;">*</span>排序</label>
+                            <div class="layui-input-block">
+                                <input type="text" name="priority" value="{:array_get($info,'priority',255)}"
+                                       lay-verify="required|number" placeholder="请输入排序"
+                                       autocomplete="off" class="layui-input">
+                                <div class="layui-form-mid layui-word-aux">倒序,值越大越靠前</div>
+                            </div>
+                        </div>
+                        <div class="layui-form-item">
+                            <label class="layui-form-label"><span style="color:#f90c05;">*</span>状态</label>
+                            <div class="layui-input-block">
+                                {volist name="status_list" id="v"}
+                                <input type="radio" name="status" value="{$key}" title="{$v}" {eq name=":array_get($info,'status',1)" value="$key" }checked{/eq}>
+                                {/volist}
+                            </div>
+                        </div>
+                        <div class="layui-form-item">
+                            <label class="layui-form-label"><span style="color:#f90c05;">*</span>浏览量</label>
+                            <div class="layui-input-block">
+                                <input type="text" name="volume" value="{:array_get($info,'volume',0)}"
+                                       lay-verify="required|number" placeholder="请输入浏览量"
+                                       autocomplete="off" class="layui-input">
+                            </div>
+                        </div>
+                        <div class="layui-form-item">
+                            <div class="layui-input-block">
+                                <input type="button" lay-submit lay-filter="{$lay_btn}" value="确认提交" class="layui-btn">
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+{include file="public/ueditor" /}
+<script>
+    var $;
+    layui.use(['index', 'form', 'upload'], function () {
+        UE.getEditor('editor');
+        $ = layui.$;
+        const admin = layui.admin;
+        const form = layui.form;
+        form.render();
+
+        form.on('submit({$lay_btn})', function (obj) {
+            const index = parent.layer.getFrameIndex(window.name);
+            obj.field.content = UE.getEditor('editor').getContent();
+            admin.req({
+                url: "{:url('recruit/editNews')}",
+                type: 'post',
+                data: obj.field,
+                done: function (res) {
+                    layer.msg("提交成功", {
+                        icon: 1
+                    });
+                    parent.layui.table.reload('lay-recruit-news-table'); //重载表格
+                    parent.layer.close(index);
+                }
+            });
+        });
+
+        //选择招考场次
+        $('#recruit_name').click(function(){
+            const index = layer.open({
+                type: 2,
+                title: '选择招考',
+                content: "{:url('select/recruit')}",
+                maxmin: true,
+                area: ['1000px', '550px']
+            });
+        });
+    });
+
+    function selectRecruit(val){
+        $('#recruit_id').val(val.id);
+        $('#recruit_name').val(val.name);
+    }
+</script>

+ 171 - 0
app/admin/view/recruit/post_form.html

@@ -0,0 +1,171 @@
+<div class="layui-fluid">
+    <div class="layui-row layui-col-space15">
+        <div class="layui-col-md12">
+            <div class="layui-card">
+                <div class="layui-card-header">{$info.name}</div>
+                <div class="layui-card-body" pad15>
+                    <div class="layui-form layui-form-pane" lay-filter="{$lay_table}">
+                        <input type="hidden" name="recruit_id" value="{:array_get($info,'id')}">
+                        <div>
+                            <button class="layui-btn layuiadmin-btn" id="add-table" type="button">添加</button>
+                        </div>
+                        <form id="form">
+                            <table class="layui-table">
+                                <colgroup>
+                                    <col width="100">
+                                    <col width="200">
+                                    <col width="100">
+                                    <col width="300">
+                                    <col width="300">
+                                    <col width="100">
+                                </colgroup>
+                                <thead>
+                                <tr>
+                                    <th>岗位编号</th>
+                                    <th>岗位名称</th>
+                                    <th>招聘人数</th>
+                                    <th>岗位限制条件(JSON格式)</th>
+                                    <th>岗位加分项(JSON格式)</th>
+                                    <th>操作</th>
+                                </tr>
+                                </thead>
+                                <tbody id="tb">
+
+                                </tbody>
+                            </table>
+                        </form>
+                        <div class="layui-form-item">
+                            <div class="layui-input-block">
+                                <input type="button" lay-submit lay-filter="{$lay_btn}" value="确认提交"
+                                       class="layui-btn">
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+
+<script>
+    layui.use(['index', 'form', 'upload'], function () {
+        const admin = layui.admin;
+        const form = layui.form;
+        const $ = layui.$;
+        form.render();
+
+        form.on('submit({$lay_btn})', function (obj) {
+            const index = parent.layer.getFrameIndex(window.name);
+            admin.req({
+                url: "{:url('recruit/editPost')}",
+                type: 'post',
+                data: obj.field,
+                done: function (res) {
+                    layer.msg("提交成功", {
+                        icon: 1
+                    });
+                    parent.layui.table.reload('lay-notice-index-table'); //重载表格
+                    parent.layer.close(index);
+                }
+            });
+        });
+
+        let data = {$list};
+
+        function createTable() {
+            let html = "";
+            data.forEach((v, k) => {
+                html += `<tr>
+                        <input type="hidden" name="list[${k}][id]" value="${v.id}">
+                        <td>
+                            <input placeholder="岗位编号"
+                                   name="list[${k}][code]"
+                                   lay-verify="required"
+                                   value="${v.code}"
+                                   class="layui-input"/>
+                        </td>
+                        <td>
+                            <input placeholder="岗位名称"
+                                   name="list[${k}][name]"
+                                   lay-verify="required"
+                                   value="${v.name}"
+                                   class="layui-input"/>
+                        </td>
+                        <td>
+                            <input placeholder="招聘人数"
+                                   type="number"
+                                   name="list[${k}][number]"
+                                   lay-verify="required"
+                                   value="${v.number}"
+                                   class="layui-input"/>
+                        </td>
+                        <td>
+                            <input placeholder="限制条件"
+                                   name="list[${k}][limit]"
+                                   lay-verify="required"
+                                   value="${v.limit}"
+                                   class="layui-input"/>
+                        </td>
+                        <td>
+                            <input placeholder="岗位加分项"
+                                   name="list[${k}][special]"
+                                   lay-verify="required"
+                                   value="${v.special}"
+                                   class="layui-input"/>
+                        </td>
+                        <td>
+                            <button class="layui-btn layui-btn-danger layuiadmin-btn delete-table" type="button" data-index="${k}">删除</button>
+                        </td>
+                </tr>`;
+            });
+
+            $('#tb').html(html);
+        }
+
+        createTable();
+
+        $('#add-table').click(function () {
+            data = getFormData();
+            data.push({id: "0", code: "", name: "", number: 1, limit: "{}", special: "{}"});
+            createTable();
+        });
+
+        $("#tb").on('click', '.delete-table', function () {
+            let index = $(this).data('index');
+            if (data.length <= 1) {
+                return false;
+            }
+
+            data = getFormData();
+            data.splice(index,1);
+            createTable();
+        });
+
+        function getFormData() {
+            let arr = $('#form').serializeArray();
+            let obj = {};
+            arr.forEach(v => {
+                obj[v.name] = v.value;
+            });
+
+            // 提取键名中的索引和属性名
+            let keys = Object.keys(obj).map(key => {
+                let parts = key.match(/list\[(\d+)\]\[(\w+)\]/);
+                return { index: parseInt(parts[1], 10), prop: parts[2] };
+            });
+
+            // 根据索引分组属性
+            let grouped = {};
+            keys.forEach(({ index, prop }, i) => {
+                if (!grouped[index]) grouped[index] = {};
+                grouped[index][prop] = obj[`list[${index}][${prop}]`];
+            });
+
+            // 将分组后的对象转换为数组
+            let res = Object.keys(grouped).map(index => grouped[index]);
+
+            return res;
+        }
+
+    });
+</script>

+ 783 - 0
app/admin/view/recruit/recruit_form.html

@@ -0,0 +1,783 @@
+<style>
+    .layui-form-pane .layui-form-label {width:125px;}
+    .layui-form-pane .layui-input-block {margin-left:125px;}
+</style>
+<div class="layui-fluid">
+    <div class="layui-row layui-col-space15">
+        <div class="layui-col-md12">
+            <div class="layui-form layui-form-pane">
+                <input type="hidden" name="id" value="{:array_get($info,'id')}">
+                <div class="layui-card layui-tab  layui-tab-brief" lay-filter="linktab">
+                    <ul class="layui-tab-title">
+                        <li class="layui-this">基础信息</li>
+                        <li>笔试</li>
+                        <li>上机</li>
+                        <li>面试</li>
+                        <li>复试</li>
+                    </ul>
+                    <div class="layui-tab-content">
+                        <div class="layui-tab-item layui-show">
+                            <div class="layui-card">
+                                <div class="layui-card-header">招考信息</div>
+                                <div class="layui-card-body" pad15>
+                                    <div class="layui-form-item">
+                                        <label class="layui-form-label"><span
+                                                style="color:#f90c05;">*</span>标题</label>
+                                        <div class="layui-input-block">
+                                            <input placeholder="请输入招考场次名称"
+                                                   name="name"
+                                                   value="{:array_get($info,'name')}"
+                                                   lay-verify="required"
+                                                   class="layui-input"/>
+                                        </div>
+                                    </div>
+                                    <div class="layui-form-item">
+                                        <label class="layui-form-label"><span
+                                                style="color:#f90c05;">*</span>招聘主体</label>
+                                        <div class="layui-input-block">
+                                            <input placeholder="请输入招聘主体"
+                                                   name="company"
+                                                   value="{:array_get($info,'company')}"
+                                                   lay-verify="required"
+                                                   class="layui-input"/>
+                                        </div>
+                                    </div>
+                                    <div class="layui-form-item">
+                                        <label class="layui-form-label">英文标识</label>
+                                        <div class="layui-input-block">
+                                            <input placeholder="自动生成英文标识"
+                                                   readonly
+                                                   name="name_en"
+                                                   value="{:array_get($info,'name_en')}"
+                                                   class="layui-input"/>
+                                            <div class="layui-form-mid layui-word-aux">
+                                                英文标识将自动生成,仅用于技术人员查看数据使用
+                                            </div>
+                                        </div>
+                                    </div>
+                                    <div class="layui-form-item">
+                                        <label class="layui-form-label"><span style="color:#f90c05;">*</span>表单模块</label>
+                                        <div class="layui-input-block">
+                                            {volist name="forms" id="v"}
+                                            <input type="checkbox" name="forms[]" value="{$key}" title="{$v}" {eq name=":in_array($key,array_get($info,'forms',['basic']))" value="$key" }checked{/eq}>
+                                            {/volist}
+                                        </div>
+                                    </div>
+                                    <div class="layui-form-item">
+                                        <label class="layui-form-label">缩略图</label>
+                                        <div class="layui-input-block">
+                                            <div class="layui-upload">
+                                                <button type="button" class="layui-btn attachment-upload-images"
+                                                        data-input="small_img" data-amount="1">上传图片
+                                                </button>
+                                                <div class="layui-inline layui-word-aux"> 最佳尺寸:220px*220px</div>
+                                                <div class="layui-upload-list echo-attachment-image-list" id="small_img">
+                                                    {notempty name="info.small_img"}
+                                                    <div>
+                                                        <input type="hidden" name="small_img"
+                                                               value="{:array_get($info,'small_img')}">
+                                                        <img src="{:array_get($info,'small_img')}">
+                                                    </div>
+                                                    {/notempty}
+                                                </div>
+                                            </div>
+                                        </div>
+                                    </div>
+                                    <div class="layui-form-item" pane>
+                                        <label class="layui-form-label"><span style="color:#f90c05;">*</span>是否开启</label>
+                                        <div class="layui-input-block">
+                                            {volist name="status_list" id="v"}
+                                            <input type="radio" name="status" value="{$key}" title="{$v}" {eq name=":array_get($info,'status',1)" value="$key" }checked{/eq}>
+                                            {/volist}
+                                        </div>
+                                    </div>
+                                    <div class="layui-form-item" pane>
+                                        <label class="layui-form-label"><span style="color:#f90c05;">*</span>显示报名人数</label>
+                                        <div class="layui-input-block">
+                                            {volist name="status_list" id="v"}
+                                            <input type="radio" name="show_report" value="{$key}" title="{$v}" {eq name=":array_get($info,'show_report',2)" value="$key" }checked{/eq}>
+                                            {/volist}
+                                        </div>
+                                    </div>
+                                    <div class="layui-form-item">
+                                        <label class="layui-form-label"><span style="color:#f90c05;">*</span>排序</label>
+                                        <div class="layui-input-block">
+                                            <input type="text" name="priority" value="{:array_get($info,'priority',255)}"
+                                                   lay-verify="required|number" placeholder="请输入排序"
+                                                   autocomplete="off" class="layui-input">
+                                            <div class="layui-form-mid layui-word-aux">倒序,值越大越靠前</div>
+                                        </div>
+                                    </div>
+                                    <div class="layui-form-item" id="form-content">
+                                        <label class="layui-form-label"><span style="color:#f90c05;">*</span>招考正文</label>
+                                        <div class="layui-input-block">
+                                            <script id="editor" type="text/plain">{:array_get($info,'introduction')}</script>
+                                        </div>
+                                    </div>
+                                </div>
+                                <div class="layui-card-header">流程与时间</div>
+                                <div class="layui-card-body" pad15>
+                                    <div class="layui-form-item" pane>
+                                        <label class="layui-form-label"><span style="color:#f90c05;">*</span>当前进展</label>
+                                        <div class="layui-input-block">
+                                            {volist name="status_step_list" id="v"}
+                                            <input type="radio" name="current" value="{$key}" title="{$v}" {eq name=":array_get($info,'current',1)" value="$key" }checked{/eq}>
+                                            {/volist}
+                                        </div>
+                                    </div>
+                                    <div class="layui-form-item" pane>
+                                        <label class="layui-form-label"><span style="color:#f90c05;">*</span>流程</label>
+                                        <div class="layui-input-block">
+                                            {volist name="status_step_list" id="v"}
+                                            <input type="checkbox" name="step[]" value="{$key}" title="{$v}" {eq name=":in_array($key,array_get($info,'step',[]))" value="$key" }checked{/eq}>
+                                            {/volist}
+                                        </div>
+                                    </div>
+                                    <div class="layui-form-item">
+                                        <label class="layui-form-label"><span
+                                                style="color:#f90c05;">*</span>报名日期</label>
+                                        <div class="layui-input-inline">
+                                            <input placeholder="请选择开始时间"
+                                                   readonly
+                                                   name="apply_start"
+                                                   value="{:array_get($info,'apply_start')}"
+                                                   lay-verify="required"
+                                                   id="apply_start"
+                                                   class="layui-input"/>
+                                        </div>
+                                        <div class="layui-input-inline">
+                                            <input placeholder="请选择结束时间"
+                                                   readonly
+                                                   name="apply_end"
+                                                   value="{:array_get($info,'apply_end')}"
+                                                   lay-verify="required"
+                                                   id="apply_end"
+                                                   class="layui-input"/>
+                                        </div>
+                                    </div>
+                                    <div class="layui-form-item">
+                                        <label class="layui-form-label">审核日期</label>
+                                        <div class="layui-input-inline">
+                                            <input placeholder="请选择开始时间"
+                                                   readonly
+                                                   name="audit_start"
+                                                   value="{:array_get($info,'audit_start')}"
+                                                   id="audit_start"
+                                                   class="layui-input"/>
+                                        </div>
+                                        <div class="layui-input-inline">
+                                            <input placeholder="请选择结束时间"
+                                                   readonly
+                                                   name="audit_end"
+                                                   value="{:array_get($info,'audit_end')}"
+                                                   id="audit_end"
+                                                   class="layui-input"/>
+                                        </div>
+                                    </div>
+                                    <div class="layui-form-item">
+                                        <label class="layui-form-label">笔试时间</label>
+                                        <div class="layui-input-inline">
+                                            <input placeholder="请选择开始时间"
+                                                   readonly
+                                                   name="preliminary_start"
+                                                   value="{:array_get($info,'preliminary_start')}"
+                                                   id="preliminary_start"
+                                                   class="layui-input"/>
+                                        </div>
+                                        <div class="layui-input-inline">
+                                            <input placeholder="请选择结束时间"
+                                                   readonly
+                                                   name="preliminary_end"
+                                                   value="{:array_get($info,'preliminary_end')}"
+                                                   id="preliminary_end"
+                                                   class="layui-input"/>
+                                        </div>
+                                    </div>
+                                    <div class="layui-form-item">
+                                        <label class="layui-form-label">上机时间</label>
+                                        <div class="layui-input-inline">
+                                            <input placeholder="请选择开始时间"
+                                                   readonly
+                                                   name="computer_start"
+                                                   value="{:array_get($info,'computer_start')}"
+                                                   id="computer_start"
+                                                   class="layui-input"/>
+                                        </div>
+                                        <div class="layui-input-inline">
+                                            <input placeholder="请选择结束时间"
+                                                   readonly
+                                                   name="computer_end"
+                                                   value="{:array_get($info,'computer_end')}"
+                                                   id="computer_end"
+                                                   class="layui-input"/>
+                                        </div>
+                                    </div>
+                                    <div class="layui-form-item">
+                                        <label class="layui-form-label">面试时间</label>
+                                        <div class="layui-input-inline">
+                                            <input placeholder="请选择开始时间"
+                                                   readonly
+                                                   name="interview_start"
+                                                   value="{:array_get($info,'interview_start')}"
+                                                   id="interview_start"
+                                                   class="layui-input"/>
+                                        </div>
+                                        <div class="layui-input-inline">
+                                            <input placeholder="请选择结束时间"
+                                                   readonly
+                                                   name="interview_end"
+                                                   value="{:array_get($info,'interview_end')}"
+                                                   id="interview_end"
+                                                   class="layui-input"/>
+                                        </div>
+                                    </div>
+                                    <div class="layui-form-item">
+                                        <label class="layui-form-label">复试时间</label>
+                                        <div class="layui-input-inline">
+                                            <input placeholder="请选择开始时间"
+                                                   readonly
+                                                   name="reexamine_start"
+                                                   value="{:array_get($info,'reexamine_start')}"
+                                                   id="reexamine_start"
+                                                   class="layui-input"/>
+                                        </div>
+                                        <div class="layui-input-inline">
+                                            <input placeholder="请选择结束时间"
+                                                   readonly
+                                                   name="reexamine_end"
+                                                   value="{:array_get($info,'reexamine_end')}"
+                                                   id="reexamine_end"
+                                                   class="layui-input"/>
+                                        </div>
+                                    </div>
+                                    <div class="layui-form-item">
+                                        <label class="layui-form-label">体检时间</label>
+                                        <div class="layui-input-inline">
+                                            <input placeholder="请选择开始时间"
+                                                   readonly
+                                                   name="health_start"
+                                                   value="{:array_get($info,'health_start')}"
+                                                   id="health_start"
+                                                   class="layui-input"/>
+                                        </div>
+                                        <div class="layui-input-inline">
+                                            <input placeholder="请选择结束时间"
+                                                   readonly
+                                                   name="health_end"
+                                                   value="{:array_get($info,'health_end')}"
+                                                   id="health_end"
+                                                   class="layui-input"/>
+                                        </div>
+                                    </div>
+                                    <div class="layui-form-item">
+                                        <label class="layui-form-label">政审时间</label>
+                                        <div class="layui-input-inline">
+                                            <input placeholder="请选择开始时间"
+                                                   readonly
+                                                   name="political_start"
+                                                   value="{:array_get($info,'political_start')}"
+                                                   id="political_start"
+                                                   class="layui-input"/>
+                                        </div>
+                                        <div class="layui-input-inline">
+                                            <input placeholder="请选择结束时间"
+                                                   readonly
+                                                   name="political_end"
+                                                   value="{:array_get($info,'political_end')}"
+                                                   id="political_end"
+                                                   class="layui-input"/>
+                                        </div>
+                                    </div>
+                                    <div class="layui-form-item">
+                                        <label class="layui-form-label">公示时间</label>
+                                        <div class="layui-input-inline">
+                                            <input placeholder="请选择开始时间"
+                                                   readonly
+                                                   name="publicity_start"
+                                                   value="{:array_get($info,'publicity_start')}"
+                                                   id="publicity_start"
+                                                   class="layui-input"/>
+                                        </div>
+                                        <div class="layui-input-inline">
+                                            <input placeholder="请选择结束时间"
+                                                   readonly
+                                                   name="publicity_end"
+                                                   value="{:array_get($info,'publicity_end')}"
+                                                   id="publicity_end"
+                                                   class="layui-input"/>
+                                        </div>
+                                    </div>
+                                </div>
+                                <div class="layui-card-header">负责人</div>
+                                <div class="layui-card-body" pad15>
+                                    <div class="layui-form-item">
+                                        <label class="layui-form-label">联系人</label>
+                                        <div class="layui-input-block">
+                                            <input placeholder="请输入联系人"
+                                                   name="contact"
+                                                   value="{:array_get($info,'contact')}"
+                                                   class="layui-input"/>
+                                        </div>
+                                    </div>
+                                    <div class="layui-form-item">
+                                        <label class="layui-form-label">联系电话</label>
+                                        <div class="layui-input-block">
+                                            <input placeholder="请输入联系电话"
+                                                   name="phone"
+                                                   value="{:array_get($info,'phone')}"
+                                                   class="layui-input"/>
+                                        </div>
+                                    </div>
+                                    <div class="layui-form-item">
+                                        <label class="layui-form-label">联系地址</label>
+                                        <div class="layui-input-block">
+                                            <input placeholder="请输入联系地址"
+                                                   name="host_address"
+                                                   value="{:array_get($info,'host_address')}"
+                                                   class="layui-input"/>
+                                        </div>
+                                    </div>
+                                    <div class="layui-form-item">
+                                        <label class="layui-form-label">联系人二维码</label>
+                                        <div class="layui-input-block">
+                                            <div class="layui-upload">
+                                                <button type="button" class="layui-btn attachment-upload-images"
+                                                        data-input="qr_code" data-amount="1">上传图片
+                                                </button>
+                                                <div class="layui-inline layui-word-aux"> 最佳尺寸:220px*220px</div>
+                                                <div class="layui-upload-list echo-attachment-image-list" id="qr_code">
+                                                    {notempty name="info.qr_code"}
+                                                    <div>
+                                                        <input type="hidden" name="qr_code"
+                                                               value="{:array_get($info,'qr_code')}">
+                                                        <img src="{:array_get($info,'qr_code')}">
+                                                    </div>
+                                                    {/notempty}
+                                                </div>
+                                            </div>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                        <div class="layui-tab-item">
+                            <div class="layui-card">
+                                <div class="layui-card-body" pad15>
+                                    <div class="layui-form-item">
+                                        <label class="layui-form-label">笔试确认</label>
+                                        <div class="layui-input-inline">
+                                            {volist name="status_list" id="v"}
+                                            <input type="radio" name="pen_confirm" value="{$key}" title="{$v}" {eq name=":array_get($info,'pen_confirm',2)" value="$key" }checked{/eq}>
+                                            {/volist}
+                                        </div>
+                                        <div class="layui-form-mid layui-word-aux">
+                                            打开后将会在设置时间段内开启笔试确认功能,有且仅当确认了才能打印准考证参加笔试{:array_get($info,'pen_confirm')}
+                                        </div>
+                                    </div>
+                                    <div class="layui-form-item">
+                                        <label class="layui-form-label">确认笔试时间</label>
+                                        <div class="layui-input-inline">
+                                            <input placeholder="请选择开始时间"
+                                                   readonly
+                                                   name="pen_confirm_start"
+                                                   value="{:array_get($info,'pen_confirm_start')}"
+                                                   id="pen_confirm_start"
+                                                   class="layui-input"/>
+                                        </div>
+                                        <div class="layui-input-inline">
+                                            <input placeholder="请选择结束时间"
+                                                   readonly
+                                                   name="pen_confirm_end"
+                                                   value="{:array_get($info,'pen_confirm_end')}"
+                                                   id="pen_confirm_end"
+                                                   class="layui-input"/>
+                                        </div>
+                                        <div class="layui-form-mid layui-word-aux">
+                                            设置该时间后,仅在该时间段内才可以确认参加笔试
+                                        </div>
+                                    </div>
+                                    <div class="layui-form-item" pane>
+                                        <label class="layui-form-label">准考证类型</label>
+                                        <div class="layui-input-block">
+                                            {volist name="status_ticket_type_list" id="v"}
+                                            <input type="radio" name="pen_ticket_type" value="{$key}" title="{$v}" {eq name=":array_get($info,'pen_ticket_type',-1)" value="$key" }checked{/eq}>
+                                            {/volist}
+                                        </div>
+                                    </div>
+                                    <div class="layui-form-item">
+                                        <label class="layui-form-label">准考证内容</label>
+                                        <div class="layui-input-block">
+                                            <input placeholder="请输入准考证内容"
+                                                   name="pen_ticket_content"
+                                                   value="{:array_get($info,'pen_ticket_content')}"
+                                                   class="layui-input"/>
+                                            <div class="layui-form-mid layui-word-aux">
+                                                当类型为纸质时,此处填入准考证模板的路径,当类型为短信时,此处填入短信标识,当类型为模板时,输入模板ID
+                                            </div>
+                                        </div>
+                                    </div>
+                                    <div class="layui-form-item">
+                                        <label class="layui-form-label">模板字段</label>
+                                        <div class="layui-input-block">
+                                            <div id="pen_field"></div>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                        <div class="layui-tab-item">
+                            <div class="layui-card">
+                                <div class="layui-card-body" pad15>
+                                    <div class="layui-form-item" pane>
+                                        <label class="layui-form-label">准考证类型</label>
+                                        <div class="layui-input-block">
+                                            {volist name="status_ticket_type_list" id="v"}
+                                            <input type="radio" name="computer_ticket_type" value="{$key}" title="{$v}" {eq name=":array_get($info,'computer_ticket_type',-1)" value="$key" }checked{/eq}>
+                                            {/volist}
+                                        </div>
+                                    </div>
+                                    <div class="layui-form-item">
+                                        <label class="layui-form-label">准考证内容</label>
+                                        <div class="layui-input-block">
+                                            <input placeholder="请输入准考证内容"
+                                                   name="computer_ticket_content"
+                                                   value="{:array_get($info,'computer_ticket_content')}"
+                                                   class="layui-input"/>
+                                            <div class="layui-form-mid layui-word-aux">
+                                                当类型为纸质时,此处填入准考证模板的路径,当类型为短信时,此处填入短信标识,当类型为模板时,输入模板ID
+                                            </div>
+                                        </div>
+                                    </div>
+                                    <div class="layui-form-item">
+                                        <label class="layui-form-label">模板字段</label>
+                                        <div class="layui-input-block">
+                                            <div id="computer_field"></div>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                        <div class="layui-tab-item">
+                            <div class="layui-card">
+                                <div class="layui-card-body" pad15>
+                                    <div class="layui-form-item" pane>
+                                        <label class="layui-form-label">准考证类型</label>
+                                        <div class="layui-input-block">
+                                            {volist name="status_ticket_type_list" id="v"}
+                                            <input type="radio" name="face_ticket_type" value="{$key}" title="{$v}" {eq name=":array_get($info,'face_ticket_type',-1)" value="$key" }checked{/eq}>
+                                            {/volist}
+                                        </div>
+                                    </div>
+                                    <div class="layui-form-item">
+                                        <label class="layui-form-label">准考证内容</label>
+                                        <div class="layui-input-block">
+                                            <input placeholder="请输入准考证内容"
+                                                   name="face_ticket_content"
+                                                   value="{:array_get($info,'face_ticket_content')}"
+                                                   class="layui-input"/>
+                                            <div class="layui-form-mid layui-word-aux">
+                                                当类型为纸质时,此处填入准考证模板的路径,当类型为短信时,此处填入短信标识,当类型为模板时,输入模板ID
+                                            </div>
+                                        </div>
+                                    </div>
+                                    <div class="layui-form-item">
+                                        <label class="layui-form-label">模板字段</label>
+                                        <div class="layui-input-block">
+                                            <div id="face_field"></div>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                        <div class="layui-tab-item">
+                            <div class="layui-card">
+                                <div class="layui-card-body" pad15>
+                                    <div class="layui-form-item" pane>
+                                        <label class="layui-form-label">准考证类型</label>
+                                        <div class="layui-input-block">
+                                            {volist name="status_ticket_type_list" id="v"}
+                                            <input type="radio" name="reexamine_ticket_type" value="{$key}" title="{$v}" {eq name=":array_get($info,'reexamine_ticket_type',-1)" value="$key" }checked{/eq}>
+                                            {/volist}
+                                        </div>
+                                    </div>
+                                    <div class="layui-form-item">
+                                        <label class="layui-form-label">准考证内容</label>
+                                        <div class="layui-input-block">
+                                            <input placeholder="请输入准考证内容"
+                                                   name="reexamine_ticket_content"
+                                                   value="{:array_get($info,'reexamine_ticket_content')}"
+                                                   class="layui-input"/>
+                                            <div class="layui-form-mid layui-word-aux">
+                                                当类型为纸质时,此处填入准考证模板的路径,当类型为短信时,此处填入短信标识,当类型为模板时,输入模板ID
+                                            </div>
+                                        </div>
+                                    </div>
+                                    <div class="layui-form-item">
+                                        <label class="layui-form-label">模板字段</label>
+                                        <div class="layui-input-block">
+                                            <div id="reexamine_field"></div>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+
+                <div class="layui-form-item">
+                    <div class="layui-input-block">
+                        <button class="layui-btn" lay-submit lay-filter="{$lay_btn}">确认修改</button>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+
+{include file="public/ueditor" /}
+<script>
+    layui.use(['index', 'form', 'layedit', 'upload', 'laydate', 'transfer'], function () {
+        UE.getEditor('editor');
+        const $ = layui.$;
+        const admin = layui.admin;
+        const form = layui.form;
+        const upload = layui.upload;
+        const laydate = layui.laydate;
+        const transfer = layui.transfer;
+
+        //时间
+        laydate.render({
+            elem: '#apply_start',
+            type: 'datetime',
+        });
+        laydate.render({
+            elem: '#apply_end',
+            type: 'datetime',
+        });
+        laydate.render({
+            elem: '#audit_start',
+            type: 'datetime',
+        });
+        laydate.render({
+            elem: '#audit_end',
+            type: 'datetime',
+        });
+        laydate.render({
+            elem: '#preliminary_start',
+            type: 'datetime',
+        });
+        laydate.render({
+            elem: '#preliminary_end',
+            type: 'datetime',
+        });
+        laydate.render({
+            elem: '#computer_start',
+            type: 'datetime',
+        });
+        laydate.render({
+            elem: '#computer_end',
+            type: 'datetime',
+        });
+        laydate.render({
+            elem: '#interview_start',
+            type: 'datetime',
+        });
+        laydate.render({
+            elem: '#interview_end',
+            type: 'datetime',
+        });
+        laydate.render({
+            elem: '#reexamine_start',
+            type: 'datetime',
+        });
+        laydate.render({
+            elem: '#reexamine_end',
+            type: 'datetime',
+        });
+        laydate.render({
+            elem: '#health_start',
+            type: 'datetime',
+        });
+        laydate.render({
+            elem: '#health_end',
+            type: 'datetime',
+        });
+        laydate.render({
+            elem: '#political_start',
+            type: 'datetime',
+        });
+        laydate.render({
+            elem: '#political_end',
+            type: 'datetime',
+        });
+        laydate.render({
+            elem: '#publicity_start',
+            type: 'datetime',
+        });
+        laydate.render({
+            elem: '#publicity_end',
+            type: 'datetime',
+        });
+        laydate.render({
+            elem: '#pen_confirm_start',
+            type: 'datetime',
+        });
+        laydate.render({
+            elem: '#pen_confirm_end',
+            type: 'datetime',
+        });
+
+        //穿梭框
+        transfer.render({
+            elem: '#pen_field',
+            title: ['系统字段','已选字段'],
+            data: [
+                {"value": "realname", "title": "变量名:${realname},标题:姓名"},
+                {"value": "card", "title": "变量名:${card},标题:身份证号"},
+                {"value": "avatar", "title": "变量名:${avatar},标题:头像"},
+                {"value": "post", "title": "变量名:${post},标题:报考岗位"},
+                {"value": "ex_title", "title": "变量名:${ex_title},标题:准考证标题"},
+                {"value": "ex_number", "title": "变量名:${ex_number},标题:准考证号"},
+                {"value": "ex_place", "title": "变量名:${ex_place},标题:考试地点"},
+                {"value": "ex_address", "title": "变量名:${ex_address},标题:考点地址"},
+                {"value": "ex_time", "title": "变量名:${ex_time},标题:考试时间"},
+                {"value": "ex_subject", "title": "变量名:${ex_subject},标题:考试科目"},
+                {"value": "ex_seat", "title": "变量名:${ex_seat},标题:座位号"},
+                {"value": "ex_room", "title": "变量名:${ex_room},标题:考室"},
+            ],
+            value: JSON.parse('{:json_encode(array_get($info,"pen_ticket_field",[]))}'),
+            id: 'pen_field',
+            width: '500',
+            height: '450',
+        });
+        transfer.render({
+            elem: '#computer_field',
+            title: ['系统字段','已选字段'],
+            data: [
+                {"value": "realname", "title": "变量名:${realname},标题:姓名"},
+                {"value": "card", "title": "变量名:${card},标题:身份证号"},
+                {"value": "post", "title": "变量名:${post},标题:报考岗位"},
+                {"value": "ex_title", "title": "变量名:${ex_title},标题:准考证标题"},
+                {"value": "ex_number", "title": "变量名:${ex_number},标题:准考证号"},
+            ],
+            value: JSON.parse('{:json_encode(array_get($info,"computer_ticket_field",[]))}'),
+            id: 'computer_field',
+            width: '500',
+            height: '450',
+        });
+        transfer.render({
+            elem: '#face_field',
+            title: ['系统字段','已选字段'],
+            data: [
+                {"value": "realname", "title": "变量名:${realname},标题:姓名"},
+                {"value": "card", "title": "变量名:${card},标题:身份证号"},
+                {"value": "post", "title": "变量名:${post},标题:报考岗位"},
+                {"value": "ex_title", "title": "变量名:${ex_title},标题:准考证标题"},
+                {"value": "ex_time", "title": "变量名:${ex_time},标题:面试时间"},
+                {"value": "ex_number", "title": "变量名:${ex_number},标题:准考证号"},
+                {"value": "ex_place", "title": "变量名:${ex_place},标题:面试地点"},
+                {"value": "ex_address", "title": "变量名:${ex_address},标题:面试地址"},
+                {"value": "ex_ready_time", "title": "变量名:${ex_ready_time},标题:报道时间"},
+                {"value": "extra1", "title": "变量名:${extra1},标题:备注1"},
+                {"value": "extra2", "title": "变量名:${extra2},标题:备注2"},
+                {"value": "extra3", "title": "变量名:${extra3},标题:备注3"},
+            ],
+            value: JSON.parse('{:json_encode(array_get($info,"face_ticket_field",[]))}'),
+            id: 'face_field',
+            width: '500',
+            height: '450',
+        });
+        transfer.render({
+            elem: '#reexamine_field',
+            title: ['系统字段','已选字段'],
+            data: [
+                {"value": "realname", "title": "变量名:${realname},标题:姓名"},
+                {"value": "card", "title": "变量名:${card},标题:身份证号"},
+                {"value": "post", "title": "变量名:${post},标题:报考岗位"},
+                {"value": "ex_title", "title": "变量名:${ex_title},标题:准考证标题"},
+                {"value": "ex_time", "title": "变量名:${ex_time},标题:面试时间"},
+                {"value": "ex_address", "title": "变量名:${ex_address},标题:面试地址"},
+                {"value": "ex_ready_time", "title": "变量名:${ex_ready_time},标题:报道时间"},
+                {"value": "extra1", "title": "变量名:${extra1},标题:备注1"},
+                {"value": "extra2", "title": "变量名:${extra2},标题:备注2"},
+                {"value": "extra3", "title": "变量名:${extra3},标题:备注3"},
+            ],
+            value: JSON.parse('{:json_encode(array_get($info,"reexamine_ticket_field",[]))}'),
+            id: 'reexamine_field',
+            width: '500',
+            height: '450',
+        });
+
+        //表单
+        form.render();
+        form.on('submit({$lay_btn})', function (obj) {
+            const index = parent.layer.getFrameIndex(window.name);
+            obj.field.introduction = UE.getEditor('editor').getContent();
+            obj.field.pen_ticket_field = getField('pen_field');
+            obj.field.computer_ticket_field = getField('computer_field');
+            obj.field.face_ticket_field = getField('face_field');
+            obj.field.reexamine_ticket_field = getField('reexamine_field');
+            admin.req({
+                url: "{:url('recruit/editRecruit')}",
+                type: 'post',
+                data: obj.field,
+                done: function (res) {
+                    layer.msg("提交成功", {icon: 1});
+                    parent.layui.table.reload('lay-recruit-index-table'); //重载表格
+                    parent.layer.close(index);
+                }
+            });
+
+        });
+
+        //模板字段
+        function getField(id)
+        {
+            let res = [];
+            const getData = transfer.getData(id);
+            if (getData.length > 0) {
+                getData.forEach(v => {
+                    res.push(v.value);
+                });
+            }
+
+            return res;
+        }
+
+        //图片上传
+        let upload_input = '';
+        let upload_amount = '';
+        upload.render({
+            elem: '.attachment-upload-images',
+            url: "{:url('upload/image')}",
+            accept: 'images',
+            exits: 'jpg|png|jpeg',
+            acceptMime: 'image/*',
+            size: 2048,
+            number: 1,
+            method: 'post',
+            before: function (obj) {
+                upload_input = $(this.item).data('input');
+                upload_amount = $(this.item).data('amount');
+                layer.load();
+            },
+            done: function (res, index, upload) {
+                layer.closeAll('loading');
+                let html = "";
+                if (upload_amount == 1) {
+                    html += '<div> <input type="hidden" name="' + upload_input + '" value="' + res.data.src + '"> ';
+                    html += '<img src="' + res.data.src + '"></div>';
+                    $("#" + upload_input).html(html);
+                } else {
+                    html += '<div> <input type="hidden" name="' + upload_input + '[]" value="' + res.data.src + '"> ';
+                    html += '<img src="' + res.data.src +
+                        '"> <button type="button" class="attachment-del layui-btn layui-btn-primary layui-btn-xs layui-btn-fluid">删除</button></div>';
+                    $("#" + upload_input).append(html);
+                }
+            },
+            error: function (index, upload) {
+                layer.closeAll('loading');
+            }
+        });
+    });
+</script>

+ 88 - 0
app/admin/view/select/recruit.html

@@ -0,0 +1,88 @@
+<div class="layui-fluid">
+    <div class="layui-card">
+        <div class="layui-form layui-form-pane  layui-card-header layuiadmin-card-header-auto">
+            <div class="layui-form-item">
+                <div class="layui-inline">
+                    <label class="layui-form-label">招考名称</label>
+                    <div class="layui-input-block">
+                        <input type="text" name="name" placeholder="请输入" autocomplete="off" class="layui-input">
+                    </div>
+                </div>
+                <div class="layui-inline">
+                    <label class="layui-form-label">开启</label>
+                    <div class="layui-input-block">
+                        <select name="status">
+                            <option value="">全部状态</option>
+                            {volist name="status_list" id="v"}
+                            <option value="{$key}">{$v}</option>
+                            {/volist}
+                        </select>
+                    </div>
+                </div>
+                <div class="layui-inline">
+                    <button class="layui-btn" lay-submit lay-filter="{$lay_btn}">
+                        <i class="layui-icon layui-icon-search layuiadmin-button-btn"></i>
+                    </button>
+                </div>
+            </div>
+        </div>
+        <div class="layui-form layui-card-header layuiadmin-card-header-auto">
+        </div>
+
+        <div class="layui-card-body">
+            <table id="{$lay_table}" lay-filter="{$lay_table}"></table>
+            <script type="text/html" id="setTpl">
+                <a class="layui-btn layui-btn-xs" lay-event="select">
+                    <i class="layui-icon layui-icon-delete"></i>选择
+                </a>
+            </script>
+        </div>
+    </div>
+</div>
+
+<script>
+    layui.use(['index', 'admin', 'form', 'table'], function () {
+        const $ = layui.$;
+        const admin = layui.admin;
+        const form = layui.form;
+        const table = layui.table;
+        form.render();
+
+        table.render({
+            elem: '#{$lay_table}',
+            url: "{:url('select/listRecruit')}",
+            cols: [
+                [
+                    {field: 'name', title: '标题'},
+                    {field: 'company', title: '招聘主体', width: 300},
+                    {field: 'priority', title: '排序', width: 160},
+                    {field: 'status_text', title: '开启', width: 80, align: 'center'},
+                    {field: 'update_time', title: '发布时间', width: 160, align: 'center'},
+                    {title: '操作', width: 120, align: 'center', fixed: 'right', toolbar: '#setTpl'}
+                ]
+            ],
+            page: true,
+            limit: 50,
+            cellMinWidth: 150,
+            text: '对不起,加载出现异常!'
+        });
+
+        form.on('submit({$lay_btn})', function (data) {
+            table.reload('{$lay_table}', {
+                where: data.field,
+                page: {
+                    curr: 1
+                }
+            });
+        });
+
+        //监听工具条
+        table.on('tool({$lay_table})', function (obj) {
+            const data = obj.data;
+            if (obj.event === 'select') {
+                parent.selectRecruit(data);
+                parent.layer.closeAll();
+            }
+        });
+    });
+</script>

+ 1 - 1
app/admin/view/slide/slide_form.html

@@ -32,7 +32,7 @@
                                     <button type="button" class="layui-btn attachment-upload-images" data-input="image"
                                             data-amount="1">上传图片
                                     </button>
-                                    <div class="layui-inline layui-word-aux"> 最佳尺寸:750px*280px</div>
+                                    <div class="layui-inline layui-word-aux"> 最佳尺寸:1200px*300px</div>
                                     <div class="layui-upload-list echo-attachment-image-list" id="image">
                                         {notempty name="info.image"}
                                         <div>

+ 31 - 1
app/common.php

@@ -26,7 +26,23 @@ function rand_str($len = 6)
 
 function array_get($array, $key, $default = null)
 {
-    return isset($array[$key]) ? $array[$key] : $default;
+    if (isset($array[$key])) {
+        return $array[$key];
+    }
+
+    if (strpos($key, '.') === false) {
+        return $array[$key] ?? $default;
+    }
+
+    foreach (explode('.', $key) as $segment) {
+        if (is_array($array) && array_key_exists($segment, $array)) {
+            $array = $array[$segment];
+        } else {
+            return $default;
+        }
+    }
+
+    return $array;
 }
 
 /**
@@ -118,4 +134,18 @@ function import_exl($file = '', $cell = [], $crop = 0, $sheet = 0)
 
     return array_values($data);
 
+}
+
+/**
+ * 删除空值
+ */
+function del_empty($array)
+{
+    foreach ($array as $k => $v) {
+        if (empty($v)) {
+            unset($array[$k]);
+        }
+    }
+
+    return $array;
 }

+ 9 - 0
app/common/model/NoticeModel.php

@@ -21,4 +21,13 @@ class NoticeModel extends BaseModel
         return self::STATUS[$data['status']];
     }
 
+    public function getSummaryAttr($value, $data)
+    {
+        return strip_tags($data['content']);
+    }
+
+    public function getUpdateShowAttr($value, $data)
+    {
+        return date('Y-m-d',($data['update_time']));
+    }
 }

+ 32 - 0
app/common/model/RecruitAppointBasicModel.php

@@ -0,0 +1,32 @@
+<?php
+
+namespace app\common\model;
+
+class RecruitAppointBasicModel extends BaseModel
+{
+    // 设置表名
+    protected $name = 'recruit_appoint_basic';
+
+    //自动完成
+    protected $autoWriteTimestamp = true;
+
+    //常量
+    const SEX = [1 => '男', 2 => '女'];
+
+    const SEX_MAN = 1;
+    const SEX_WOMAN = 2;
+
+    public function getSexTextAttr($value, $data)
+    {
+        return self::SEX[$data['sex']];
+    }
+
+    public function getFamilyAttr($value, $data)
+    {
+        if (empty($value)) {
+            return [];
+        } else {
+            return json_decode($value,true);
+        }
+    }
+}

+ 16 - 0
app/common/model/RecruitAppointInfoModel.php

@@ -0,0 +1,16 @@
+<?php
+
+namespace app\common\model;
+
+use think\model\concern\SoftDelete;
+
+class RecruitAppointInfoModel extends BaseModel
+{
+    use SoftDelete;
+
+    // 设置表名
+    protected $name = 'recruit_appoint_info';
+
+    //自动完成
+    protected $autoWriteTimestamp = true;
+}

+ 191 - 0
app/common/model/RecruitModel.php

@@ -0,0 +1,191 @@
+<?php
+
+namespace app\common\model;
+
+use think\model\concern\SoftDelete;
+
+class RecruitModel extends BaseModel
+{
+    use SoftDelete;
+
+    // 设置表名
+    protected $name = 'recruit';
+
+    //自动完成
+    protected $autoWriteTimestamp = true;
+
+    // 设置字段自动转换类型
+    protected $type = [
+        'forms'                 => 'array',
+        'step'                  => 'array',
+        'pen_ticket_field'      => 'array',
+        'computer_ticket_field' => 'array',
+        'face_ticket_field'     => 'array',
+        'reexamine_field'       => 'array',
+        /*'apply_start'           => 'timestamp:Y-m-d H:i:s',
+        'apply_end'             => 'timestamp:Y-m-d H:i:s',
+        'audit_start'           => 'timestamp:Y-m-d H:i:s',
+        'audit_end'             => 'timestamp:Y-m-d H:i:s',
+        'preliminary_start'     => 'timestamp:Y-m-d H:i:s',
+        'preliminary_end'       => 'timestamp:Y-m-d H:i:s',
+        'computer_start'        => 'timestamp:Y-m-d H:i:s',
+        'computer_end'          => 'timestamp:Y-m-d H:i:s',
+        'interview_start'       => 'timestamp:Y-m-d H:i:s',
+        'interview_end'         => 'timestamp:Y-m-d H:i:s',
+        'reexamine_start'       => 'timestamp:Y-m-d H:i:s',
+        'reexamine_end'         => 'timestamp:Y-m-d H:i:s',
+        'health_start'          => 'timestamp:Y-m-d H:i:s',
+        'health_end'            => 'timestamp:Y-m-d H:i:s',
+        'political_start'       => 'timestamp:Y-m-d H:i:s',
+        'political_end'         => 'timestamp:Y-m-d H:i:s',
+        'publicity_start'       => 'timestamp:Y-m-d H:i:s',
+        'publicity_end'         => 'timestamp:Y-m-d H:i:s',
+        'supplement_start'      => 'timestamp:Y-m-d H:i:s',
+        'supplement_end'        => 'timestamp:Y-m-d H:i:s',
+        'pen_confirm_start'     => 'timestamp:Y-m-d H:i:s',
+        'pen_confirm_end'       => 'timestamp:Y-m-d H:i:s',*/
+    ];
+
+    // 常量
+    const STATUS_INT = [1 => '是', 2 => '否'];
+    const STATUS_STEP = [1 => '报名', 2 => '审核', 3 => '笔试', 4 => '上机', 5 => '面试', 6 => '复试', 7 => '体检', 8 => '政审', 9 => '公示'];
+//    const STATUS_TICKET_TYPE = [-1 => '不需要', 1 => '纸质', 2 => '短信', 3 => '模板'];
+    const STATUS_TICKET_TYPE = [-1 => '不需要', 1 => '纸质'];
+    const STATUS_TICKET_STATUS = [-1 => '未生成', 1 => '生成中', 2 => '已生成'];
+    const STATUS_SCORE = [1 => '已上传', 2 => '未上传'];
+    const FORMS = ['basic' => '基础信息模块', 'expand_special' => '扩展模块-加分', 'detail' => '详情介绍模块', 'identification' => '身份证明上传模块', 'education_certification' => '学历证明上传模块', 'other_certification' => '其他证明上传模块', 'hj_certification' => '户籍证明上传模块', 'remark' => '备注模块', 'chengnuoshu' => '诚信承诺书', 'shixin' => '失信被执行人查询信息页扫描件', 'baokaoshengming' => '报考声明书'];
+
+    //状态
+    const STATUS_YES = 1;
+    const STATUS_NO = 2;
+
+    //流程
+    const STEP_SIGN = 1;
+    const STEP_EXAMINE = 2;
+    const STEP_WRITE = 3;
+    const STEP_COMPUTER = 4;
+    const STEP_FACE = 5;
+    const STEP_RETEST = 6;
+    const STEP_HEALTH = 7;
+    const STEP_POLITICS = 8;
+    const STEP_PUBLICITY = 9;
+
+    //准考证类型
+    const TICKET_TYPE_NO = -1;
+    const TICKET_TYPE_PAPER = 1;
+    const TICKET_TYPE_MESSAGE = 2;
+
+    //准考证状态
+    const TICKET_STATUS_No = -1;
+    const TICKET_STATUS_PROGRESS = 1;
+    const TICKET_STATUS_FINISH = 2;
+
+    //是否上传成绩
+    const TICKET_SCORE_YES = 1;
+    const TICKET_SCORE_NO = 2;
+
+    public function getStatusTextAttr($value, $data)
+    {
+        return self::STATUS_INT[$data['status']];
+    }
+
+    public function getCurrentTextAttr($value, $data)
+    {
+        return self::STATUS_STEP[$data['current']];
+    }
+
+    public function getStepArrAttr($value, $data)
+    {
+        $arr  = [];
+        $step = $data['step'];
+        if (is_string($step)) {
+            $step = explode('-', $step);
+        }
+        foreach ($step as $v) {
+            $arr[] = self::STATUS_STEP[$v];
+        }
+        return $arr;
+    }
+
+    public function getPredeterminedStatusTextAttr($value, $data)
+    {
+        return self::STATUS_INT[$data['predetermined_status']];
+    }
+
+    public function getShowReportTextAttr($value, $data)
+    {
+        return self::STATUS_INT[$data['show_report']];
+    }
+
+    public function getPenTicketTypeTextAttr($value, $data)
+    {
+        return self::STATUS_TICKET_TYPE[$data['pen_ticket_type']];
+    }
+
+    public function getPenTicketStatusTextAttr($value, $data)
+    {
+        return self::STATUS_TICKET_STATUS[$data['pen_ticket_status']];
+    }
+
+    public function getPenStatusTextAttr($value, $data)
+    {
+        return self::STATUS_SCORE[$data['pen_status']];
+    }
+
+    public function getPenConfirmTextAttr($value, $data)
+    {
+        return self::STATUS_INT[$data['pen_confirm']];
+    }
+
+    public function getComputerTypeTextAttr($value, $data)
+    {
+        return self::STATUS_TICKET_TYPE[$data['computer_ticket_type']];
+    }
+
+    public function getComputerTicketStatusTextAttr($value, $data)
+    {
+        return self::STATUS_TICKET_STATUS[$data['computer_ticket_status']];
+    }
+
+    public function getComputerStatusTextAttr($value, $data)
+    {
+        return self::STATUS_SCORE[$data['computer_status']];
+    }
+
+    public function getFaceTypeTextAttr($value, $data)
+    {
+        return self::STATUS_TICKET_TYPE[$data['face_ticket_type']];
+    }
+
+    public function getFaceTicketStatusTextAttr($value, $data)
+    {
+        return self::STATUS_TICKET_STATUS[$data['face_ticket_status']];
+    }
+
+    public function getFaceStatusTextAttr($value, $data)
+    {
+        return self::STATUS_SCORE[$data['face_status']];
+    }
+
+    public function getReexamineTypeTextAttr($value, $data)
+    {
+        return self::STATUS_TICKET_TYPE[$data['reexamine_ticket_type']];
+    }
+
+    public function getReexamineTicketStatusTextAttr($value, $data)
+    {
+        return self::STATUS_TICKET_STATUS[$data['reexamine_ticket_status']];
+    }
+
+    public function getReexamineStatusTextAttr($value, $data)
+    {
+        return self::STATUS_SCORE[$data['reexamine_status']];
+    }
+
+    public function getApplyTimeAttr($value, $data)
+    {
+        $start_time = date('Y-m-d', strtotime($data['apply_start']));
+        $end_time   = date('Y-m-d', strtotime($data['apply_end']));
+        return [$start_time, $end_time];
+    }
+}

+ 39 - 0
app/common/model/RecruitNewsModel.php

@@ -0,0 +1,39 @@
+<?php
+
+namespace app\common\model;
+
+class RecruitNewsModel extends BaseModel
+{
+    //设置表名
+    protected $name = 'recruit_news';
+
+    //自动完成
+    protected $autoWriteTimestamp = true;
+
+    //常量
+    const STATUS = [1 => '已发布', 2 => '待修改'];
+
+    const STATUS_PUBLISH = 1;
+    const STATUS_MODIFY  = 2;
+
+
+    public function getStatusTextAttr($value, $data)
+    {
+        return self::STATUS[$data['status']];
+    }
+
+    public function recruit()
+    {
+        return $this->hasOne(RecruitModel::class, "id", "recruit_id");
+    }
+
+    public function getSummaryAttr($value, $data)
+    {
+        return strip_tags($data['content']);
+    }
+
+    public function getUpdateShowAttr($value, $data)
+    {
+        return date('Y-m-d',($data['update_time']));
+    }
+}

+ 16 - 0
app/common/model/RecruitPostModel.php

@@ -0,0 +1,16 @@
+<?php
+
+namespace app\common\model;
+
+use think\model\concern\SoftDelete;
+
+class RecruitPostModel extends BaseModel
+{
+    use SoftDelete;
+
+    // 设置表名
+    protected $name = 'recruit_post';
+
+    //自动完成
+    protected $autoWriteTimestamp = true;
+}

+ 2 - 1
app/common/service/SmsService.php

@@ -2,6 +2,7 @@
 
 namespace app\common\service;
 
+use app\common\model\SettingModel;
 use app\common\model\SmsTemplateModel;
 use think\facade\Log;
 
@@ -22,7 +23,7 @@ class SmsService
             }
         }
 
-        $sms_obj = Config('mobile.sms_type');
+        $sms_obj = SettingModel::getConfigValue('sms_type');
         $sms = new $sms_obj();
         return $sms->send($mobile, ['message' => $msg]);
     }

+ 1 - 0
app/home/.htaccess

@@ -0,0 +1 @@
+deny from all

+ 67 - 0
app/home/HomeBaseController.php

@@ -0,0 +1,67 @@
+<?php
+
+namespace app\home;
+
+use app\BaseController;
+use app\common\model\SettingModel;
+use app\common\model\UserModel;
+use think\exception\HttpResponseException;
+use think\facade\View;
+use think\Response;
+
+/**
+ * 控制器基础类
+ */
+abstract class HomeBaseController extends BaseController
+{
+    protected $tab = '';
+    protected $user = null;
+
+    // 初始化
+    protected function initialize()
+    {
+        $setting = SettingModel::select();
+        $set     = [];
+        foreach ($setting as $v) {
+            $set[$v['code']] = $v['value'];
+        }
+        config($set, 'home');
+
+        $user_id = get_user_id();
+        if (!empty($user_id)) {
+            $this->user = UserModel::where('id',$user_id)->hidden(['password','salt'])->find();
+        }
+
+        $this->init();
+
+        View::assign('tab',$this->tab);
+        View::assign('user',$this->user);
+    }
+
+    protected function init()
+    {
+    }
+
+    protected function checkLogin()
+    {
+        $sessionUserId = session('home.user.id');
+        if (empty($sessionUserId)) {
+            if (request()->isAjax()) {
+                $res      = ['code' => 401, 'msg' => '请登录'];
+                $response = Response::create($res, 'json');
+                throw new HttpResponseException($response);
+            } else {
+                session('back_url',request()->url());
+                $response = redirect('/home/login/index');
+                throw new HttpResponseException($response);
+            }
+        }
+
+        if (empty($this->user)) {
+            jump('该用户已删除');
+        }
+        if ($this->user['status'] != UserModel::STATUS_PASS) {
+            jump('该账号暂时无法使用');
+        }
+    }
+}

+ 62 - 0
app/home/common.php

@@ -0,0 +1,62 @@
+<?php
+// 应用公共文件
+function jump($msg = '', $url = null, $wait = 3)
+{
+    if (is_null($url)) {
+        $url = 'javascript:history.back(-1);';
+    } else {
+        $url = "location.href = '" . url($url) . "'";
+    }
+
+    $result = [
+        'msg'  => $msg,
+        'url'  => $url,
+        'wait' => $wait,
+    ];
+
+    $html = view('/public/jump', $result);
+    throw new \think\exception\HttpResponseException($html);
+}
+
+function ajax_success($data)
+{
+    $res      = ['code' => 0, 'msg' => '成功', 'data' => $data];
+    $response = \think\Response::create($res, 'json');
+    throw new \think\exception\HttpResponseException($response);
+}
+
+function get_user()
+{
+    $sessionUserId = session('home.user.id');
+    if (empty($sessionUserId)) {
+        if (request()->isAjax()) {
+            $res      = ['code' => 401, 'msg' => '请登录'];
+            $response = \think\Response::create($res, 'json');
+            throw new \think\exception\HttpResponseException($response);
+        } else {
+            session('back_url',request()->url());
+            $response = redirect('/home/login/login');
+            throw new \think\exception\HttpResponseException($response);
+        }
+    }
+
+    $user = \app\common\model\UserModel::where('id',$sessionUserId)->hidden(['password','salt'])->find();
+    if (empty($user)) {
+        jump('该用户已删除');
+    }
+    if ($user['status'] != \app\common\model\UserModel::STATUS_PASS) {
+        jump('该账号暂时无法使用');
+    }
+
+    return $user;
+}
+
+function get_user_id()
+{
+    $sessionUserId = session('home.user.id');
+    if (empty($sessionUserId)) {
+        return 0;
+    }
+
+    return $sessionUserId;
+}

+ 18 - 0
app/home/config/view.php

@@ -0,0 +1,18 @@
+<?php
+// +----------------------------------------------------------------------
+// | 模板设置
+// +----------------------------------------------------------------------
+
+return [
+    // 模板常量
+    'tpl_replace_string' => [
+        '__STATIC__'        => '/static',
+        '__COMMON_IMAGES__' => '/static/common/images',
+        '__COMMON_CSS__'    => '/static/common/css',
+        '__COMMON_JS__'     => '/static/common/js',
+        '__HIMAGES__'       => '/static/home/images',
+        '__HCSS__'          => '/static/home/css',
+        '__HJS__'           => '/static/home/js',
+        '__COMPONENTS__'    => '/static/home/js/components',
+    ],
+];

+ 18 - 0
app/home/controller/About.php

@@ -0,0 +1,18 @@
+<?php
+
+namespace app\home\controller;
+
+use app\home\HomeBaseController;
+
+class About extends HomeBaseController
+{
+    protected function init()
+    {
+        $this->tab = 'about';
+    }
+
+    public function index()
+    {
+        return view();
+    }
+}

+ 18 - 0
app/home/controller/Help.php

@@ -0,0 +1,18 @@
+<?php
+
+namespace app\home\controller;
+
+use app\home\HomeBaseController;
+
+class Help extends HomeBaseController
+{
+    protected function init()
+    {
+        $this->tab = 'help';
+    }
+
+    public function index()
+    {
+        return view();
+    }
+}

+ 53 - 0
app/home/controller/Index.php

@@ -0,0 +1,53 @@
+<?php
+
+namespace app\home\controller;
+
+use app\common\model\NoticeModel;
+use app\common\model\RecruitModel;
+use app\common\model\RecruitNewsModel;
+use app\common\model\SlideModel;
+use app\home\HomeBaseController;
+
+class Index extends HomeBaseController
+{
+    protected function init()
+    {
+        $this->tab = 'index';
+    }
+
+    public function index()
+    {
+        //轮播图
+        $slide = SlideModel::where('tab', SlideModel::TAB_INDEX)
+            ->where('status', SlideModel::STATUS_SHOW)
+            ->order(['priority' => 'desc', 'id' => 'desc'])
+            ->select();
+
+        //政策
+        $notice = NoticeModel::where('status', NoticeModel::STATUS_PUBLISH)
+            ->order(['priority' => 'desc', 'update_time' => 'desc'])
+            ->limit(10)
+            ->select();
+
+        //公告
+        $news = RecruitNewsModel::with(['recruit'])
+            ->where('status', RecruitNewsModel::STATUS_PUBLISH)
+            ->order(['priority' => 'desc', 'update_time' => 'desc'])
+            ->append(['summary', 'update_show'])
+            ->limit(10)
+            ->select();
+
+        $recruit = RecruitModel::where('status', RecruitModel::STATUS_YES)
+            ->order(['priority' => 'desc', 'update_time' => 'desc'])
+            ->append(['current_text','apply_time'])
+            ->limit(9)
+            ->select();
+
+        return view('', [
+            'slide'   => $slide,
+            'notice'  => $notice,
+            'news'    => $news,
+            'recruit' => $recruit,
+        ]);
+    }
+}

+ 102 - 0
app/home/controller/Login.php

@@ -0,0 +1,102 @@
+<?php
+
+namespace app\home\controller;
+
+use app\common\model\UserModel;
+use app\common\service\SmsService;
+use app\home\HomeBaseController;
+use think\facade\Validate;
+
+class Login extends HomeBaseController
+{
+
+    public function index()
+    {
+        return view();
+    }
+
+    public function logout()
+    {
+        session('home.user.id',null);
+        return redirect('/');
+    }
+
+    public function sendSms()
+    {
+        $mobile = trim(input('post.mobile'));
+
+        $validate = Validate::rule('mobile', 'require|mobile');
+        if (!$validate->check(['mobile'=>$mobile])) {
+            ajax_return(1, '手机号错误');
+        }
+
+        //手机号验证
+        $user = UserModel::where('mobile', $mobile)->find();
+        empty($user) && ajax_return(1, '该手机号未注册');
+
+        $rand       = rand(100000, 999999);
+        $preg_phone = '/^1[34578]\d{9}$/ims';
+        if (preg_match($preg_phone, $mobile)) {
+            $sms = new SmsService();
+            $res = $sms->send($mobile, 'verification', [$rand]);
+
+            if ($res['code'] == 0) {
+                session('home.login.verify', $rand);
+                session('home.login.verify_expire', time());
+            } else {
+                ajax_return(1, '发送失败,请联系管理员');
+            }
+        } else {
+            ajax_return(1, '手机号格式不正确');
+        }
+
+        ajax_return();
+    }
+
+    public function doLogin()
+    {
+        $data = input('post.');
+        empty($data['mobile']) && ajax_return(1, '请输入手机号');
+
+        //手机号验证
+        $user = UserModel::where('mobile', $data['mobile'])->find();
+        empty($user) && ajax_return(1, '该手机号未注册');
+
+        if ($data['type'] == 'password') {
+            //密码登录
+            empty($data['password']) && ajax_return(1, '请输入密码');
+
+            //密码验证
+            if (md5(md5($user['salt']) . $data['password']) != $user['password'] ) {
+                ajax_return(1, '密码错误');
+            }
+        } elseif ($data['type'] == 'verify') {
+            //验证码登录
+            empty($data['verify']) && ajax_return(1, '请输入验证码');
+
+            //验证码校验
+            if (config('home.sms_verify_expire') > 0) {
+                $verify_expire = session('home.login.verify_expire');
+                if ($verify_expire + config('home.sms_verify_expire') < time()) {
+                    session('home.login.verify', null);
+                    session('home.login.verify_expire', null);
+                    ajax_return(1, '验证码已过期');
+                }
+            }
+            $verify = session('home.login.verify');
+            if ($verify != $data['verify']) {
+                ajax_return(1, '验证码不正确');
+            }
+        }
+
+        //登录
+        $user->last_login_time = time();
+        $user->last_login_ip   = request()->ip();
+        $user->save();
+        session('home.user.id', $user['id']);
+
+        $url = session('back_url') ?? url('/home/my/index')->build();
+        session('back_url', null);
+        ajax_success(['url' => $url]);
+    }
+}

+ 96 - 0
app/home/controller/My.php

@@ -0,0 +1,96 @@
+<?php
+
+namespace app\home\controller;
+
+use app\common\model\RecruitAppointBasicModel;
+use app\home\HomeBaseController;
+
+class My extends HomeBaseController
+{
+    protected function init()
+    {
+        $this->checkLogin();
+    }
+
+    public function index()
+    {
+        return view();
+    }
+
+    public function info()
+    {
+        $info = RecruitAppointBasicModel::where('user_id', $this->user['id'])->find();
+        if (empty($info)) {
+            $info = [
+                'realname'              => '',
+                'sex'                   => 1,
+                'birthday'              => '',
+                'avatar'                => '',
+                'nation'                => '',
+                'native_place'          => '',
+                'political_affiliation' => '',
+                'titles'                => '',
+                'work'                  => '',
+                'house_register'        => '',
+                'join_time'             => '',
+                'edu_type'              => '',
+                'education'             => '',
+                'school'                => '',
+                'degree'                => '',
+                'pro'                   => '',
+                'province'              => '',
+                'city'                  => '',
+                'area'                  => '',
+                'address'               => '',
+                'card'                  => '',
+                'mobile'                => '',
+                'email'                 => '',
+                'concat_name'           => '',
+                'concat_mobile'         => '',
+                'resume'                => '',
+                'family'                => [
+                    [
+                        'relation'              => '',
+                        'realname'              => '',
+                        'birthday'              => '',
+                        'political_affiliation' => '',
+                        'work'                  => '',
+                    ],
+                ],
+                'is_push'               => 1,
+            ];
+        }
+        return view('', ['info' => json_encode($info)]);
+    }
+
+    public function infoPost()
+    {
+        $basic = RecruitAppointBasicModel::where('user_id',$this->user['id'])->find();
+        $data = input('post.');
+        if ($basic) {
+            //更新基础信息
+            unset($data['create_time'],$data['update_time']);
+            RecruitAppointBasicModel::where('user_id', $this->user['id'])->update($data);
+        } else {
+            //新增基础信息
+            $data['user_id'] = $this->user['id'];
+            RecruitAppointBasicModel::create($data);
+        }
+        ajax_return(0,'个人基础信息保存成功,快去报名吧!');
+    }
+
+    public function account()
+    {
+        return view();
+    }
+
+    public function tip()
+    {
+        return view();
+    }
+
+    public function feedback()
+    {
+        return view();
+    }
+}

+ 75 - 0
app/home/controller/News.php

@@ -0,0 +1,75 @@
+<?php
+
+namespace app\home\controller;
+
+use app\common\model\RecruitNewsModel;
+use app\home\HomeBaseController;
+
+class News extends HomeBaseController
+{
+    protected function init()
+    {
+        $this->tab = 'news';
+    }
+
+    public function index()
+    {
+        $keyword = input('keyword', '');
+
+        $limit = 10;
+        $where = [
+            ['status', '=', RecruitNewsModel::STATUS_PUBLISH],
+            ['title', 'like', "%$keyword%"],
+        ];
+        $total = RecruitNewsModel::where($where)
+            ->count();
+        return view('', [
+            'keyword' => $keyword,
+            'total'   => $total,
+            'limit'   => $limit,
+        ]);
+    }
+
+    public function list()
+    {
+        $page    = input('page', 1);
+        $limit   = input('limit', 10);
+        $keyword = input('keyword', '');
+
+        $where = [
+            ['status', '=', RecruitNewsModel::STATUS_PUBLISH],
+        ];
+        if (!empty($keyword)) {
+            $where[] = ['title', 'like', "%$keyword%"];
+        }
+        $list  = RecruitNewsModel::with(['recruit'])
+            ->where($where)
+            ->page($page, $limit)
+            ->order(['priority' => 'desc', 'update_time' => 'desc'])
+            ->append(['summary', 'update_show'])
+            ->select();
+
+        ajax_success($list);
+    }
+
+    public function detail()
+    {
+        $id = input('id', 0);
+        if (empty($id)) {
+            jump('该招聘公告不存在或已经删除');
+        }
+
+        $info = RecruitNewsModel::with(['recruit'])
+            ->where('status', RecruitNewsModel::STATUS_PUBLISH)
+            ->find($id);
+        if (empty($info)) {
+            jump('该招聘公告不存在或已经删除');
+        }
+        $info->volume++;
+        $info->save();
+
+        return view('', [
+            'info' => $info,
+        ]);
+    }
+}

+ 69 - 0
app/home/controller/Notice.php

@@ -0,0 +1,69 @@
+<?php
+
+namespace app\home\controller;
+
+use app\common\model\NoticeModel;
+use app\home\HomeBaseController;
+
+class Notice extends HomeBaseController
+{
+    protected function init()
+    {
+        $this->tab = 'notice';
+    }
+
+    public function index()
+    {
+        $keyword = input('keyword', '');
+
+        $limit = 10;
+        $where = [
+            ['status', '=', NoticeModel::STATUS_PUBLISH],
+            ['title', 'like', "%$keyword%"],
+        ];
+        $total = NoticeModel::where($where)->count();
+        return view('', [
+            'keyword' => $keyword,
+            'total'   => $total,
+            'limit'   => $limit,
+        ]);
+    }
+
+    public function list()
+    {
+        $page    = input('page', 1);
+        $limit   = input('limit', 10);
+        $keyword = input('keyword', '');
+
+        $where = [
+            ['status', '=', NoticeModel::STATUS_PUBLISH],
+        ];
+        if (!empty($keyword)) {
+            $where[] = ['title', 'like', "%$keyword%"];
+        }
+        $list  = NoticeModel::where($where)
+            ->page($page, $limit)
+            ->order(['priority' => 'desc', 'update_time' => 'desc'])
+            ->append(['summary', 'update_show'])
+            ->select();
+
+        ajax_success($list);
+    }
+
+    public function detail()
+    {
+        $id = input('id', 0);
+        if (empty($id)) {
+            jump('该系统通知不存在或已经删除');
+        }
+
+        $info = NoticeModel::where('status', NoticeModel::STATUS_PUBLISH)->find($id);
+        if (empty($info)) {
+            jump('该系统通知不存在或已经删除');
+        }
+
+        return view('', [
+            'info' => $info,
+        ]);
+    }
+}

+ 72 - 0
app/home/controller/Recruit.php

@@ -0,0 +1,72 @@
+<?php
+
+namespace app\home\controller;
+
+use app\common\model\RecruitModel;
+use app\common\model\RecruitNewsModel;
+use app\home\HomeBaseController;
+
+class Recruit extends HomeBaseController
+{
+    protected function init()
+    {
+        $this->tab = 'recruit';
+    }
+
+    public function index()
+    {
+        $keyword = input('keyword', '');
+
+        $limit = 10;
+        $where = [
+            ['status', '=', RecruitModel::STATUS_YES],
+            ['name', 'like', "%$keyword%"],
+        ];
+        $total = RecruitModel::where($where)
+            ->count();
+        return view('', [
+            'keyword' => $keyword,
+            'total'   => $total,
+            'limit'   => $limit,
+        ]);
+    }
+
+    public function list()
+    {
+        $page    = input('page', 1);
+        $limit   = input('limit', 10);
+        $keyword = input('keyword', '');
+
+        $where = [
+            ['status', '=', RecruitModel::STATUS_YES],
+        ];
+        if (!empty($keyword)) {
+            $where[] = ['name', 'like', "%$keyword%"];
+        }
+        $list  = RecruitModel::where($where)
+            ->page($page, $limit)
+            ->order(['priority' => 'desc', 'update_time' => 'desc'])
+            ->append(['current_text','apply_time'])
+            ->select();
+
+        ajax_success($list);
+    }
+
+    public function detail()
+    {
+        $id = input('id', 0);
+        if (empty($id)) {
+            jump('该招聘不存在或已经删除');
+        }
+
+        $info = RecruitModel::where('status', RecruitModel::STATUS_YES)
+            ->find($id);
+        if (empty($info)) {
+            jump('该招聘不存在或已经删除');
+        }
+
+        return view('', [
+            'info' => $info,
+        ]);
+    }
+}

+ 98 - 0
app/home/controller/Register.php

@@ -0,0 +1,98 @@
+<?php
+
+namespace app\home\controller;
+
+use app\common\model\UserModel;
+use app\common\service\SmsService;
+use app\home\HomeBaseController;
+use think\facade\Validate;
+
+class Register extends HomeBaseController
+{
+
+    public function index()
+    {
+        return view();
+    }
+
+    public function sendSms()
+    {
+        $mobile = trim(input('post.mobile'));
+
+        $validate = Validate::rule('mobile', 'require|mobile');
+        if (!$validate->check(['mobile' => $mobile])) {
+            ajax_return(1, '手机号错误');
+        }
+
+        //手机号验证
+        $user = UserModel::where('mobile', $mobile)->find();
+        !empty($user) && ajax_return(1, '该手机号已注册');
+
+        $rand       = rand(100000, 999999);
+        $preg_phone = '/^1[34578]\d{9}$/ims';
+        if (preg_match($preg_phone, $mobile)) {
+            $sms = new SmsService();
+            $res = $sms->send($mobile, 'verification', [$rand]);
+
+            if ($res['code'] == 0) {
+                session('home.register.verify', $rand);
+                session('home.register.verify_expire', time());
+            } else {
+                ajax_return(1, '发送失败,请联系管理员');
+            }
+        } else {
+            ajax_return(1, '手机号格式不正确');
+        }
+
+        ajax_return();
+    }
+
+    public function doRegister()
+    {
+        $data     = input('post.');
+        $validate = Validate::rule('mobile', 'require|mobile');
+        if (!$validate->check($data)) {
+            ajax_return(1, '手机号错误');
+        }
+        empty($data['verify']) && ajax_return(1, '请输入验证码');
+        empty($data['password']) && ajax_return(1, '请输入密码');
+        empty($data['re_password']) && ajax_return(1, '请输入确认密码');
+
+        if (strlen($data['password']) < 6) {
+            ajax_return(1, '密码需要至少6位');
+        }
+        if ($data['password'] != $data['re_password']) {
+            ajax_return(1, '密码和确认密码不同');
+        }
+
+        //手机号验证
+        $user = UserModel::where('mobile', $data['mobile'])->find();
+        !empty($user) && ajax_return(1, '该手机号已注册');
+
+        //验证码校验
+        if (config('home.sms_verify_expire') > 0) {
+            $verify_expire = session('home.register.verify_expire');
+            if ($verify_expire + config('home.sms_verify_expire') < time()) {
+                session('home.register.verify', null);
+                session('home.register.verify_expire', null);
+                ajax_return(1, '验证码已过期');
+            }
+        }
+        $verify = session('home.register.verify');
+        if ($verify != $data['verify']) {
+            ajax_return(1, '验证码不正确');
+        }
+
+        $add             = [];
+        $add['salt']     = rand_str();
+        $add['password'] = md5(md5($add['salt']) . $data['password']);
+        $add['mobile']   = $add['account'] = $add['nickname'] = $add['realname'] = $data['mobile'];
+        $add['status']   = UserModel::STATUS_PASS;
+        UserModel::create($add);
+
+        session('home.register.verify', null);
+        session('home.register.verify_expire', null);
+
+        ajax_return();
+    }
+}

+ 47 - 0
app/home/controller/Upload.php

@@ -0,0 +1,47 @@
+<?php
+
+namespace app\home\controller;
+
+
+use app\home\HomeBaseController;
+
+class Upload extends HomeBaseController
+{
+
+    /**
+     * 文件上传
+     */
+    public function file()
+    {
+        $file     = request()->file("file");
+        $savename = \think\facade\Filesystem::disk('public')->putFile('files', $file);
+        if ($file) {
+            $filename = str_replace(strrchr($_FILES['file']['name'], "."), "", $_FILES['file']['name']);
+            ajax_return(0, '', [
+                'src'   => request()->domain() . "/storage/" . str_replace("\\", "/", $savename),
+                'path'  => "./storage/" . str_replace("\\", "/", $savename),
+                'title' => $filename,
+            ]);
+        } else {
+            ajax_return(1, '上传失败,请稍后重试');
+        }
+    }
+
+    /**
+     * 图片上传
+     */
+    public function image()
+    {
+        $file     = request()->file("file");
+        $savename = \think\facade\Filesystem::disk('public')->putFile('images', $file);
+        if ($file) {
+            $filename = str_replace(strrchr($_FILES['file']['name'], "."), "", $_FILES['file']['name']);
+            ajax_return(0, '', [
+                'src'   => request()->domain() . "/storage/" . str_replace("\\", "/", $savename),
+                'title' => $filename,
+            ]);
+        } else {
+            ajax_return(1, '上传失败,请稍后重试');
+        }
+    }
+}

+ 17 - 0
app/home/event.php

@@ -0,0 +1,17 @@
+<?php
+// 事件定义文件
+return [
+    'bind'      => [
+    ],
+
+    'listen'    => [
+        'AppInit'  => [],
+        'HttpRun'  => [],
+        'HttpEnd'  => [],
+        'LogLevel' => [],
+        'LogWrite' => [],
+    ],
+
+    'subscribe' => [
+    ],
+];

+ 10 - 0
app/home/middleware.php

@@ -0,0 +1,10 @@
+<?php
+// 全局中间件定义文件
+return [
+    // 全局请求缓存
+    // \think\middleware\CheckRequestCache::class,
+    // 多语言加载
+    // \think\middleware\LoadLangPack::class,
+    // Session初始化
+     \think\middleware\SessionInit::class
+];

+ 9 - 0
app/home/provider.php

@@ -0,0 +1,9 @@
+<?php
+use app\ExceptionHandle;
+use app\Request;
+
+// 容器Provider定义文件
+return [
+    'think\Request'          => Request::class,
+    'think\exception\Handle' => ExceptionHandle::class,
+];

+ 147 - 0
app/home/view/about/index.html

@@ -0,0 +1,147 @@
+{extend name="public/base"/}
+{block name="css"}
+<style>
+    .explain {
+        border-radius: 4px;
+        border: 1px solid #f1eded;
+        overflow: hidden;
+        margin: 0 auto;
+        width: 1200px;
+        margin-top: 40px;
+    }
+
+    .explain .panel-body {
+        padding: 20px;
+        background-color: #FFF;
+    }
+
+    .explain .panel-body .title {
+        text-align: center;
+        font-size: 20px;
+        padding-bottom: 20px;
+    }
+
+    .explain .panel-body .content {
+        text-indent: 2em;
+        line-height: 2em;
+        font-size: 16px;
+    }
+</style>
+{/block}
+{block name="body"}
+<div class="explain">
+    <div class="panel-body">
+        <h3 class="title">福建省晋江人力资本有限公司</h3>
+        <p class="content">
+            福建省晋江人力资本有限公司为国有独资企业,业务主管部门为晋江市委组织部(人才办),主要开展晋江全市范围内的各类人才引进、培育和服务工作,以及各类人才平台建设、项目孵化经营管理工作,并且承接晋江市委、市政府、市委人才工作领导小组授权的人才相关业务,是一家致力于提供全链条专业人力资源方案解决服务机构。公司负责运营泉州(晋江)国际人才港、泉州市(晋江)人力资源服务产业园、晋江经济开发区人才交流服务共享中心三大人才服务实体平台和一体化人才服务大数据平台聚才网(www.jucai.gov.cn)。</p>
+        <section style="padding-top:20px;box-sizing: border-box;max-width: 100% !important;overflow-wrap: break-word;" label="Powered by xmyeditor.com" data-md5="ed92b">
+            <section style="box-sizing: border-box;max-width: 100% !important;overflow-wrap: break-word;" data-type="lspecial01" data-md5="ed92b">
+                <section style="width: 48%;vertical-align: middle;display: inline-block;box-sizing: border-box;max-width: 100% !important;overflow-wrap: break-word;" data-md5="ed92b">
+                    <section style="box-sizing: border-box;max-width: 100% !important;overflow-wrap: break-word;" data-md5="ed92b">
+                        <section style="box-sizing: border-box;max-width: 100% !important;overflow-wrap: break-word;" data-md5="ed92b">
+                            <section style="box-sizing: border-box;max-width: 100% !important;overflow-wrap: break-word;" data-md5="ed92b">
+                                <section style="text-align: center;box-sizing: border-box;max-width: 100% !important;overflow-wrap: break-word;" data-md5="ed92b">
+                                    <section style="background: none rgba(75, 44, 52, 0.99); width: 50px;height:50px;border-radius:50%;line-height:50px;display: inline-block;box-sizing: border-box;max-width: 100% !important;overflow-wrap: break-word;" data-md5="ed92b">
+                                        <span style="color: rgb(75, 44, 52);display: inline-block;width: 100%;max-width: 100%;overflow-wrap: break-word;box-sizing: border-box;">
+                                            <img data-ratio="1" data-type="png" data-w="65"
+                                                 style="border-radius: 50%; width: 25px;height: 25px; color: rgb(0, 0, 0); font-size: 16px; vertical-align: middle; z-index: -1; cursor: pointer; box-sizing: border-box; overflow-wrap: break-word; max-width: 100% !important; visibility: visible !important;"
+                                                 title="" width="65"
+                                                 data-original-style="background: none rgb(75, 44, 52);border-radius: 50%;width: 49.98px;color: rgba(75, 44, 52, 0.99);font-size: 16px;vertical-align: middle;z-index: -1;cursor: pointer;box-sizing: border-box;overflow-wrap: break-word;max-width: 100% !important;"
+                                                 data-index="13"
+                                                 src="/static/home/images/location.png"
+                                                 class="" _width="49.98px" crossorigin="anonymous" alt="图片"
+                                                 data-fail="0">
+                                        </span>
+                                    </section>
+                                </section>
+                                <section style="margin-top: 10px;box-sizing: border-box;max-width: 100% !important;overflow-wrap: break-word;" data-md5="ed92b">
+                                    <section style="text-align: center;color: rgba(208, 16, 76, 0.99);line-height: 26.25px;letter-spacing: 0.2px;box-sizing: border-box;max-width: 100% !important;overflow-wrap: break-word;" data-md5="ed92b">
+                                        <section style="box-sizing: border-box;max-width: 100% !important;overflow-wrap: break-word;" data-type="lspecial01" data-md5="ed92b">
+                                            <p style="font-size: 13px;max-width: 100%;overflow-wrap: break-word;box-sizing: border-box;" data-md5="ed92b">
+                                                <span style="color: rgb(75, 44, 52);max-width: 100%;overflow-wrap: break-word;box-sizing: border-box;">
+                                                    <strong>公司地址</strong>
+                                                </span>
+                                            </p>
+                                        </section>
+                                        <section style="box-sizing: border-box;max-width: 100% !important;overflow-wrap: break-word;" data-type="lspecial01" data-md5="ed92b">
+                                            <p style="max-width: 100%;overflow-wrap: break-word;box-sizing: border-box;">
+                                                <span style="color: rgb(75, 44, 52);font-size: 13px;max-width: 100%;overflow-wrap: break-word;box-sizing: border-box;">福建省泉州市晋江市</span>
+                                            </p>
+                                            <p style="max-width: 100%;overflow-wrap: break-word;box-sizing: border-box;">
+                                                <span style="color: rgb(75, 44, 52);font-size: 13px;max-width: 100%;overflow-wrap: break-word;box-sizing: border-box;">梅岭街道聚兴小区一区11号</span>
+                                            </p>
+                                            <p style="font-size: 12px;max-width: 100%;overflow-wrap: break-word;box-sizing: border-box;" data-md5="ed92b">
+                                                <br style="max-width: 100%;overflow-wrap: break-word;box-sizing: border-box;">
+                                            </p>
+                                        </section>
+                                    </section>
+                                </section>
+                            </section>
+                        </section>
+                    </section>
+                </section>
+                <section style="margin-left: 4%;width: 48%;vertical-align: middle;display: inline-block;box-sizing: border-box;max-width: 100% !important;overflow-wrap: break-word;" data-md5="ed92b">
+                    <section style="box-sizing: border-box;max-width: 100% !important;overflow-wrap: break-word;" data-md5="ed92b">
+                        <section style="text-align: center;box-sizing: border-box;max-width: 100% !important;overflow-wrap: break-word;" data-md5="ed92b">
+                            <section style="background: none rgba(75, 44, 52, 0.99); width: 50px;height:50px;border-radius:50%;line-height:50px;display: inline-block;box-sizing: border-box;max-width: 100% !important;overflow-wrap: break-word;" data-md5="ed92b">
+                                <span style="color: rgb(75, 44, 52);display: inline-block;width: 100%;max-width: 100%;overflow-wrap: break-word;box-sizing: border-box;">
+                                    <img
+                                        class="rich_pages wxw-img" data-ratio="1"
+                                        data-type="png" data-w="65"
+                                        style="border-radius: 50%; width: 25px;height: 25px; color: rgb(0, 0, 0); font-size: 16px; vertical-align: middle; z-index: -1; cursor: pointer; box-sizing: border-box; overflow-wrap: break-word; max-width: 100% !important; visibility: visible !important;"
+                                        title="" vspace="0" width="65"
+                                        data-original-style="background: none rgba(75, 44, 52, 0.99);border-radius: 50%;width: 49.98px;color: rgb(0, 0, 0);font-size: 16px;vertical-align: middle;z-index: -1;cursor: pointer;box-sizing: border-box;overflow-wrap: break-word;max-width: 100% !important;"
+                                        data-index="14"
+                                        src="/static/home/images/tel.png"
+                                        _width="49.98px" crossorigin="anonymous" alt="图片" data-fail="0">
+                                </span>
+                            </section>
+                        </section>
+                        <section
+                                style="margin-top: 10px;box-sizing: border-box;max-width: 100% !important;overflow-wrap: break-word;"
+                                data-md5="ed92b">
+                            <section
+                                    style="text-align: center;color: rgba(208, 16, 76, 0.99);line-height: 26.25px;letter-spacing: 0.2px;box-sizing: border-box;max-width: 100% !important;overflow-wrap: break-word;"
+                                    data-md5="ed92b">
+                                <section
+                                        style="box-sizing: border-box;max-width: 100% !important;overflow-wrap: break-word;"
+                                        data-type="lspecial01" data-md5="ed92b"><p
+                                        style="font-size: 13px;max-width: 100%;overflow-wrap: break-word;box-sizing: border-box;"
+                                        data-md5="ed92b"><span
+                                        style="color: rgb(75, 44, 52);max-width: 100%;overflow-wrap: break-word;box-sizing: border-box;"><strong>联系方式</strong></span>
+                                </p></section>
+                                <section
+                                        style="box-sizing: border-box;max-width: 100% !important;overflow-wrap: break-word;"
+                                        data-type="lspecial01" data-md5="ed92b"><p
+                                        style="max-width: 100%;overflow-wrap: break-word;box-sizing: border-box;"><span
+                                        style="color: rgb(75, 44, 52);font-size: 13px;max-width: 100%;overflow-wrap: break-word;box-sizing: border-box;">0595-82008295</span>
+                                </p>
+                                    <p style="max-width: 100%;overflow-wrap: break-word;box-sizing: border-box;"><span
+                                            style="color: rgb(75, 44, 52);font-size: 13px;max-width: 100%;overflow-wrap: break-word;box-sizing: border-box;">0595-82008296</span>
+                                    </p>
+                                    <p style="font-size: 12px;max-width: 100%;overflow-wrap: break-word;box-sizing: border-box;"
+                                       data-md5="ed92b"><br
+                                            style="max-width: 100%;overflow-wrap: break-word;box-sizing: border-box;">
+                                    </p></section>
+                            </section>
+                        </section>
+                    </section>
+                </section>
+            </section>
+        </section>
+        <p style="text-align: center;max-width: 100%;overflow-wrap: break-word;box-sizing: border-box;"><span style="color: rgb(75, 44, 52);display: inline-block;max-width: 100%;overflow-wrap: break-word;box-sizing: border-box;">
+            <strong>人才服务热线:</strong>0595-88223456</span>
+        </p>
+    </div>
+    <div class="clear"></div>
+</div>
+{/block}
+{block name="script"}
+<script>
+    function v_setup() {
+        let base = {};
+
+        return base;
+    }
+</script>
+{/block}

+ 53 - 0
app/home/view/help/index.html

@@ -0,0 +1,53 @@
+{extend name="public/base"/}
+{block name="css"}
+<style>
+    .help_banner{ width:100%;}
+    .help{margin:0 auto; width:1200px; margin-top:40px;background: white;}
+    .questions-title {color: #333333;font-size: 16px;font-weight: bold;margin-bottom: 10px;}
+    .questions-title .diamond {display: inline-block;width: 10px;height: 10px;transform: rotateZ(45deg) skew(10deg, 10deg);background-color: #333333;margin-right: 10px;}
+    .questions ul{padding-left: 30px;padding-right: 30px;}
+    .questions .panel-body li {padding: 25px 0;}
+    .questions .more {padding-left: 30px;}
+
+    .questions-box {display: flex;flex-flow: row;flex-direction: row;}
+    .questions-item {flex: 1;word-wrap:break-word;word-break:normal;font-size: 18px;}
+</style>
+{/block}
+{block name="body"}
+<img src="/static/home/images/about.jpg" class="help_banner">
+<div class="help">
+    <div class="main questions">
+        <div class="panel lbox link_gray6">
+            <ul class="panel-body">
+                <li>
+                    <div class="questions-title">
+                        <span class="diamond"></span><span>常见问题</span>
+                    </div>
+                    <div class="questions-box">
+                        <div class="questions-item">
+                            <p><span><strong>1、忘记密码怎么办?</strong></span></p>
+                            <p style="text-indent: 2em;"><span>确认之前是否已经注册,如果已经注册但是忘记密码可点击个人登录页面的忘记密码,使用手机验证码找回密码。</span></p>
+                            <br>
+                            <p><span><strong>2、报名资料无法上传,如何处理?</strong></span></p>
+                            <p style="text-indent: 2em;"><span>上传图片小于限制在2M以内,图片须为jpg/jpeg/png格式。</span></p>
+                            <br>
+                            <p><strong><span>3、为便于顺利完成报名确认,请报考人员尽量避开首日及结束日等高峰时段。</span></strong></p>
+                        </div>
+                    </div>
+                </li>
+            </ul>
+        </div>
+
+        <div class="clear"></div>
+    </div>
+</div>
+{/block}
+{block name="script"}
+<script>
+    function v_setup() {
+        let base = {};
+
+        return base;
+    }
+</script>
+{/block}

+ 16 - 0
app/home/view/index/base.html

@@ -0,0 +1,16 @@
+{extend name="public/base"/}
+{block name="css"}
+
+{/block}
+{block name="body"}
+
+{/block}
+{block name="script"}
+<script>
+    function v_setup() {
+        let base = {};
+
+        return base;
+    }
+</script>
+{/block}

+ 107 - 0
app/home/view/index/index.html

@@ -0,0 +1,107 @@
+{extend name="public/base"/}
+{block name="css"}
+<link rel="stylesheet" href="/static/home/css/index.css">
+{/block}
+{block name="body"}
+<div class="content-box">
+    <div class="block text-center">
+        <el-carousel height="300px">
+            {volist name="slide" id="item"}
+            <el-carousel-item>
+                <img class="carousel-image" src="{$item.image}" @click="toUrl('{$item.url}')">
+            </el-carousel-item>
+            {/volist}
+        </el-carousel>
+    </div>
+    <div class="home_title">
+        <div class="title_ch">最新招聘</div>
+        <div class="title_en">RECRUIT</div>
+    </div>
+    <el-row :gutter="20">
+        {volist name="recruit" id="item"}
+        <el-col style="margin-top:20px;" :span="8">
+            <div class="famousbox">
+                <div class="famousbox_top">
+                    <a href="{:url('recruit/detail')}?id={$item.id}" target="_blank" class="famousbox_top_title">
+                        {$item.name}
+                    </a>
+                </div>
+                <div class="famousbox_bottom">
+                    <a href="{:url('recruit/detail')}?id={$item.id}" target="_blank" class="famousbox_middle">
+                        报名时间:{$item['apply_time'][0]} - {$item['apply_time'][1]}
+                    </a>
+                    <div class="recruit-btn-group">
+                        <a href="{:url('recruit/detail')}?id={$item.id}" class="btn btn-primary" target="_blank">{$item.current_text}</a>
+                        <!--<a href="http://hxks.hxrc-app.com/content/recruit/show?id=428" class="btn btn-primary-plain" target="_blank">公告</a>
+                        <a href="http://hxks.hxrc-app.com/content/recruit/post?recruit_id=428" class="btn btn-primary-plain" target="_blank">岗位</a>
+                        <a href="http://hxks.hxrc-app.com/content/recruit/statistics?recruit_id=428" class="btn btn-primary-plain btn-big" target="_blank">人数统计</a>-->
+                    </div>
+                </div>
+            </div>
+        </el-col>
+        {/volist}
+    </el-row>
+    <a href="{:url('recruit/index')}" class="box_more" target="_blank">查看更多&gt;</a>
+    <div class="home_title">
+        <div class="title_ch">通知公告</div>
+        <div class="title_en">NOTICE</div>
+    </div>
+    <el-row :gutter="20">
+        <el-col :span="16">
+            <div class="title-box">
+                <h3>招考公告</h3>
+                <a href="{:url('news/index')}">更多+</a>
+            </div>
+            <el-row :gutter="20">
+                {volist name="news" id="item"}
+                <el-col style="margin-top:20px;" :span="24">
+                    <div class="famousbox">
+                        <div class="famousbox_top">
+                            <a href="{:url('news/detail')}?id={$item.id}" target="_blank" class="famousbox_top_title">
+                                {$item.title}
+                            </a>
+                        </div>
+                        <div class="famousbox_bottom">
+                            <a href="{:url('news/detail')}?id={$item.id}" target="_blank" class="famousbox_middle">
+                                日期:{$item.update_show} 浏览 {$item.volume}次
+                            </a>
+                            <div class="recruit-btn-group">
+                                <a href="{:url('recruit/detail')}?id={$item.recruit.id}" target="_blank" class="notice-recruit">
+                                    {$item.recruit.name}
+                                </a>
+                            </div>
+                        </div>
+                    </div>
+                </el-col>
+                {/volist}
+            </el-row>
+        </el-col>
+        <el-col :span="8">
+            <div class="title-box">
+                <h3>系统通知</h3>
+                <a href="{:url('notice/index')}">更多+</a>
+            </div>
+            <ul class="notice" style="margin-top:5px; ">
+                {volist name="notice" id="item"}
+                <li><el-icon><bell-filled></bell-filled></el-icon><a href="{:url('notice/detail')}?id={$item.id}" target="_blank">{$item.title}</a></li>
+                {/volist}
+            </ul>
+        </el-col>
+    </el-row>
+</div>
+
+{/block}
+{block name="script"}
+<script>
+    function v_setup() {
+        let base = {};
+        base.toUrl = (url) => {
+            if (url) {
+                open(url);
+            }
+        };
+
+        return base;
+    }
+</script>
+{/block}

+ 279 - 0
app/home/view/login/index.html

@@ -0,0 +1,279 @@
+<!DOCTYPE html>
+<html lang="zh-cn">
+<head>
+    {include file="public/meta_header"/}
+    <style>
+        :root {
+            --el-color-primary: #dd4250;
+        }
+
+        .el-tabs__item {
+            font-size: 18px;
+        }
+
+        .el-form-item {
+            padding-top: 15px;
+        }
+
+        .el-input__inner, .el-form-item__label {
+            height: 40px;
+            line-height: 40px;
+        }
+
+        .el-form-item__label {
+            font-size: 16px;
+        }
+
+        .login-box {
+            width: 472px;
+            margin: 0 auto;
+            padding-bottom: 150px;
+            padding-top: 90px;
+        }
+
+        .login-box .login-logo {
+            margin-bottom: 35px;
+            margin-left: 50px;
+        }
+
+        .login-box .login-logo a {
+            display: inline-block;
+            vertical-align: middle;
+        }
+
+        .login-box .login-logo img {
+            width: 40px;
+        }
+
+        .login-box .login-logo .backhome {
+            background: url("/static/home/images/bg_back.png") no-repeat center;
+            padding: 5px 15px;
+            font-size: 13px;
+            color: #616887;
+            margin-top: 10px;
+        }
+
+        .login-box .login-box-per-body {
+            background-color: #fff;
+            width: 472px;
+            border-radius: 5px;
+            -moz-box-shadow: 2px 2px 10px 0 rgba(0, 0, 0, 0.1);
+            -webkit-box-shadow: 2px 2px 10px 0 rgba(0, 0, 0, 0.1);
+            box-shadow: 2px 2px 10px 0 rgba(0, 0, 0, 0.1);
+        }
+
+        .form-box {
+            width: 332px;
+            margin: 0 auto;
+            padding: 30px 0;
+        }
+
+        .login-box .txt-group {
+            font-size: 12px;
+            width: 332px;
+            margin-bottom: 30px;
+            height: 16px;
+        }
+
+        .login-box .txt-group .txt-r {
+            float: right;
+            color: #9ea1b1;
+            text-decoration: none;
+        }
+
+        .login-box .btn-group {
+            width: 332px;
+            height: 50px;
+            background-color: #dd4250;
+            border-radius: 25px;
+            text-align: center;
+            line-height: 42px;
+            color: #ffffff;
+            margin-bottom: 20px;
+            cursor: pointer;
+            border: none;
+            font-size: 20px;
+            letter-spacing: 10px;
+        }
+
+        .login-box .reg-link-box {
+            width: 332px;
+            color: #9ea1b1;
+            font-size: 14px;
+            margin-bottom: 15px;
+            line-height: 26px;
+            height: 26px;
+            text-align: right;
+        }
+
+        .login-box .reg-link-box .reg-txt {
+            display: inline-block;
+            color: #fe2e5f;
+            text-decoration: none;
+        }
+
+        .bind_code {
+            position: relative;
+        }
+
+        .bind_code .el-button {
+            border: 0;
+            background: none;
+            padding: 0;
+            border-radius: 0;
+        }
+
+        .bind_code .bind_code_gain {
+            position: absolute;
+            right: 10px;
+            font-size: 14px;
+            color: #dd4250;
+            cursor: pointer;
+            padding-left: 10px;
+            border-left: 1px solid #d8d8d8;
+            height: 40px;
+            line-height: 40px;
+        }
+    </style>
+</head>
+<body>
+<div id="app">
+    <div class="login-box">
+        <div class="login-logo">
+            <a href="{:url('/')}"><img src="/static/home/images/img_logo.png" alt="logo"></a>
+            <a href="{:url('/')}" class="backhome">返回首页</a>
+        </div>
+
+        <div class="login-box-per-body">
+            <div class="form-box">
+                <el-tabs class="demo-tabs" :stretch="true">
+                    <el-tab-pane label="密码登录">
+                        <el-form :model="password_form" label-width="auto" style="max-width: 600px">
+                            <el-form-item label="手机号">
+                                <el-input v-model="password_form.mobile" placeholder="请输入手机号"></el-input>
+                            </el-form-item>
+                            <el-form-item label="密码" prop="password">
+                                <el-input v-model="password_form.password" type="password" autocomplete="off"
+                                          placeholder="请输入密码"></el-input>
+                            </el-form-item>
+                            <div class="txt-group">
+                                <span class="txt-r">忘记密码,请用验证码登录</span>
+                            </div>
+                            <input class="btn-group login-account" type="button" @click="doLogin('password_form')" value="登录">
+                        </el-form>
+                    </el-tab-pane>
+                    <el-tab-pane label="验证码登录">
+                        <el-form :model="verify_form" label-width="auto" style="max-width: 600px">
+                            <el-form-item label="手机号">
+                                <el-input v-model="verify_form.mobile" placeholder="请输入手机号"></el-input>
+                            </el-form-item>
+                            <el-form-item label="验证码">
+                                <div class="bind_code margin_top">
+                                    <el-input
+                                            class="bind_code_input code"
+                                            v-model="verify_form.verify"
+                                            type="text"
+                                            placeholder="请输入验证码"
+                                    ></el-input>
+                                    <el-button
+                                            @click="bindSendCode"
+                                            class="bind_code_gain"
+                                            :disabled="disabled"
+                                    >{{ btnText }}
+                                    </el-button>
+                                </div>
+                            </el-form-item>
+                            <input style="margin-top:20px;" class="btn-group login-account" @click="doLogin('verify_form')" type="button" value="登录">
+                        </el-form>
+                    </el-tab-pane>
+                </el-tabs>
+                <div class="reg-link-box">
+                    还没有账号?<a class="reg-txt register_user" href="{:url('register/index')}">立刻注册</a>
+                </div>
+            </div>
+        </div>
+    </div>
+    <div class="index_footer">
+        <div class="index_footer_links">
+            <a target="_blank" href="http://hxks.hxrc-app.com">网站首页</a>
+            <a target="_blank" href="http://hxks.hxrc-app.com/content/explain/show/9">单位介绍</a>
+            <a target="_blank" href="http://hxks.hxrc-app.com/content/suggest/index">意见建议</a>
+        </div>
+        <div class="index_footertxt">
+            <p>招考服务热线:0595-82008296</p>
+            <p>地址:福建省泉州市晋江市梅岭街道碧山社区梅岭路273号</p>
+            <p>网站备案:<a href="https://beian.miit.gov.cn/" target="_blank">闽ICP备17033895号</a></p>
+
+        </div>
+    </div>
+</div>
+
+<script>
+    function v_setup() {
+        let base = {};
+
+        base.password_form = Vue.reactive({mobile: '', password: '',type:'password'});
+        base.verify_form = Vue.reactive({mobile: '', verify: '',type:'verify'});
+
+        base.btnText = Vue.ref('获取验证码');
+        base.disabled = Vue.ref(false);
+        base.bindSendCode = () => {
+            //手机号 为空的话
+            if (base.verify_form.mobile != '') {
+                postJson('/login/sendSms',base.verify_form).then(() => {
+                    ElMessage.success('已发送验证码,请查收')
+                    base.disabled.value = true;
+                    base.btnText.value = "请稍候...";
+                    setTimeout(() => {
+                        base.doLoop(60);
+                    }, 500);
+                });
+            } else {
+                ElMessage.error('请输入手机号')
+            }
+        }
+        base.doLoop = (seconds) => {
+            seconds = seconds ? seconds : 60;
+            base.btnText.value = seconds + "s后获取";
+            let countdown = setInterval(() => {
+                if (seconds > 0) {
+                    base.btnText.value = seconds + "s后获取";
+                    --seconds;
+                } else {
+                    base.btnText.value = "获取验证码";
+                    base.disabled.value = false;
+                    clearInterval(countdown);
+                }
+            }, 1000);
+        }
+
+        base.doLogin = (type) => {
+            postJson('/login/doLogin',base[type]).then(({data}) => {
+                location.href = data.url;
+            });
+        }
+
+        return base;
+    }
+</script>
+<script>
+    const vue3 = {
+        setup() {
+            return v_setup();
+        }
+    };
+
+    const app = Vue.createApp(vue3);
+    for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
+        app.component(key, component);
+    }
+    app.use(ElementPlus);
+    const ElMessageBox = ElementPlus.ElMessageBox;
+    const ElMessage = ElementPlus.ElMessage;
+</script>
+{block name="vue"}{/block}
+<script>
+    app.mount('#app');
+</script>
+</body>
+</html>

+ 75 - 0
app/home/view/my/account.html

@@ -0,0 +1,75 @@
+{extend name="public/base"/}
+{block name="css"}
+<style>
+    .content-box{width:1200px;}
+    .bg-white {background: white;}
+    .user_main {margin-top:20px;}
+    .user_panel_header {display: flex;align-items: flex-end;padding:20px;box-sizing: border-box;border-bottom: 1px solid #ebebeb;}
+    .user_panel_header .username {color: #333333;font-size: 16px;}
+    .user_panel_header h4 {margin-left: 10px;color: #999999;font-size: 14px;font-weight: normal;}
+    .user_main .nav_list {overflow: hidden;margin-top: 20px;padding-bottom: 40px;display: flex;align-items: center;justify-content: center;flex-direction: column;}
+    .user_main .list {height: 45px;display: flex;align-items: center;justify-content: center;}
+    .user_main .list .arrow {cursor: pointer;color: #d6d6d6;font-size: 20px;margin-left:50px;}
+    .user_main .list a {color: #999999;font-size: 16px;margin-left:10px;}
+    .user_main .list a.select,.user_main .list .arrow.select {color:#dd4250;}
+
+    .main-box {min-height: 600px;}
+</style>
+{/block}
+{block name="body"}
+<div class="content-box">
+    <el-row class="user_main" :gutter="20">
+        <el-col :span="6">
+            <div class="bg-white" >
+                <div class="user_panel_header">
+                    <div class="username substring">Hi,{:empty($user) ? '' : $user['realname']}</div>
+                    <h4>欢迎回来</h4>
+                </div>
+                <div class="nav_list">
+                    <div class="list">
+                        <el-icon size="24" color="#999999"><Document></Document></el-icon>
+                        <a href="{:url('my/index')}">报考记录</a>
+                        <div class="arrow">&gt;</div>
+                    </div>
+                    <div class="list">
+                        <el-icon size="24" color="#999999"><User></User></el-icon>
+                        <a href="{:url('my/info')}">个人资料</a>
+                        <div class="arrow">&gt;</div>
+                    </div>
+                    <div class="list">
+                        <el-icon size="24" color="#dd4250"><Hide></Hide></el-icon>
+                        <a class="select" href="{:url('my/account')}">账号安全</a>
+                        <div class="arrow select">&gt;</div>
+                    </div>
+                    <div class="list">
+                        <el-icon size="24" color="#999999"><Bell></Bell></el-icon>
+                        <a class="msg_count" href="{:url('my/tip')}">我的消息</a>
+                        <div class="arrow">&gt;</div>
+                    </div>
+                    <div class="list">
+                        <el-icon size="24" color="#999999"><Message></Message></el-icon>
+                        <a class="" href="{:url('my/feedback')}">意见反馈</a>
+                        <div class="arrow">&gt;</div>
+                    </div>
+                </div>
+            </div>
+        </el-col>
+        <el-col :span="18">
+            <div class="bg-white main-box">
+                <el-empty description="您暂时还没有招报记录" ></el-empty>
+            </div>
+        </el-col>
+    </el-row>
+</div>
+
+{/block}
+{block name="script"}
+<script>
+    function v_setup() {
+        let base = {};
+
+
+        return base;
+    }
+</script>
+{/block}

+ 75 - 0
app/home/view/my/feedback.html

@@ -0,0 +1,75 @@
+{extend name="public/base"/}
+{block name="css"}
+<style>
+    .content-box{width:1200px;}
+    .bg-white {background: white;}
+    .user_main {margin-top:20px;}
+    .user_panel_header {display: flex;align-items: flex-end;padding:20px;box-sizing: border-box;border-bottom: 1px solid #ebebeb;}
+    .user_panel_header .username {color: #333333;font-size: 16px;}
+    .user_panel_header h4 {margin-left: 10px;color: #999999;font-size: 14px;font-weight: normal;}
+    .user_main .nav_list {overflow: hidden;margin-top: 20px;padding-bottom: 40px;display: flex;align-items: center;justify-content: center;flex-direction: column;}
+    .user_main .list {height: 45px;display: flex;align-items: center;justify-content: center;}
+    .user_main .list .arrow {cursor: pointer;color: #d6d6d6;font-size: 20px;margin-left:50px;}
+    .user_main .list a {color: #999999;font-size: 16px;margin-left:10px;}
+    .user_main .list a.select,.user_main .list .arrow.select {color:#dd4250;}
+
+    .main-box {min-height: 600px;}
+</style>
+{/block}
+{block name="body"}
+<div class="content-box">
+    <el-row class="user_main" :gutter="20">
+        <el-col :span="6">
+            <div class="bg-white" >
+                <div class="user_panel_header">
+                    <div class="username substring">Hi,{:empty($user) ? '' : $user['realname']}</div>
+                    <h4>欢迎回来</h4>
+                </div>
+                <div class="nav_list">
+                    <div class="list">
+                        <el-icon size="24" color="#999999"><Document></Document></el-icon>
+                        <a href="{:url('my/index')}">报考记录</a>
+                        <div class="arrow">&gt;</div>
+                    </div>
+                    <div class="list">
+                        <el-icon size="24" color="#999999"><User></User></el-icon>
+                        <a href="{:url('my/info')}">个人资料</a>
+                        <div class="arrow">&gt;</div>
+                    </div>
+                    <div class="list">
+                        <el-icon size="24" color="#999999"><Hide></Hide></el-icon>
+                        <a class="" href="{:url('my/account')}">账号安全</a>
+                        <div class="arrow">&gt;</div>
+                    </div>
+                    <div class="list">
+                        <el-icon size="24" color="#999999"><Bell></Bell></el-icon>
+                        <a class="msg_count" href="{:url('my/tip')}">我的消息</a>
+                        <div class="arrow">&gt;</div>
+                    </div>
+                    <div class="list">
+                        <el-icon size="24" color="#dd4250"><Message></Message></el-icon>
+                        <a class="select" href="{:url('my/feedback')}">意见反馈</a>
+                        <div class="arrow select">&gt;</div>
+                    </div>
+                </div>
+            </div>
+        </el-col>
+        <el-col :span="18">
+            <div class="bg-white main-box">
+                <el-empty description="您暂时还没有招报记录" ></el-empty>
+            </div>
+        </el-col>
+    </el-row>
+</div>
+
+{/block}
+{block name="script"}
+<script>
+    function v_setup() {
+        let base = {};
+
+
+        return base;
+    }
+</script>
+{/block}

+ 75 - 0
app/home/view/my/index.html

@@ -0,0 +1,75 @@
+{extend name="public/base"/}
+{block name="css"}
+<style>
+    .content-box{width:1200px;}
+    .bg-white {background: white;}
+    .user_main {margin-top:20px;}
+    .user_panel_header {display: flex;align-items: flex-end;padding:20px;box-sizing: border-box;border-bottom: 1px solid #ebebeb;}
+    .user_panel_header .username {color: #333333;font-size: 16px;}
+    .user_panel_header h4 {margin-left: 10px;color: #999999;font-size: 14px;font-weight: normal;}
+    .user_main .nav_list {overflow: hidden;margin-top: 20px;padding-bottom: 40px;display: flex;align-items: center;justify-content: center;flex-direction: column;}
+    .user_main .list {height: 45px;display: flex;align-items: center;justify-content: center;}
+    .user_main .list .arrow {cursor: pointer;color: #d6d6d6;font-size: 20px;margin-left:50px;}
+    .user_main .list a {color: #999999;font-size: 16px;margin-left:10px;}
+    .user_main .list a.select,.user_main .list .arrow.select {color:#dd4250;}
+
+    .main-box {min-height: 600px;}
+</style>
+{/block}
+{block name="body"}
+<div class="content-box">
+    <el-row class="user_main" :gutter="20">
+        <el-col :span="6">
+            <div class="bg-white" >
+                <div class="user_panel_header">
+                    <div class="username substring">Hi,{:empty($user) ? '' : $user['realname']}</div>
+                    <h4>欢迎回来</h4>
+                </div>
+                <div class="nav_list">
+                    <div class="list">
+                        <el-icon size="24" color="#dd4250"><Document></Document></el-icon>
+                        <a class="but2 select" href="{:url('my/index')}">报考记录</a>
+                        <div class="arrow select">&gt;</div>
+                    </div>
+                    <div class="list">
+                        <el-icon size="24" color="#999999"><User></User></el-icon>
+                        <a href="{:url('my/info')}">个人资料</a>
+                        <div class="arrow">&gt;</div>
+                    </div>
+                    <div class="list">
+                        <el-icon size="24" color="#999999"><Hide></Hide></el-icon>
+                        <a class="but9" href="{:url('my/account')}">账号安全</a>
+                        <div class="arrow">&gt;</div>
+                    </div>
+                    <div class="list">
+                        <el-icon size="24" color="#999999"><Bell></Bell></el-icon>
+                        <a class="but8 msg_count" href="{:url('my/tip')}">我的消息</a>
+                        <div class="arrow">&gt;</div>
+                    </div>
+                    <div class="list">
+                        <el-icon size="24" color="#999999"><Message></Message></el-icon>
+                        <a class="but2" href="{:url('my/feedback')}">意见反馈</a>
+                        <div class="arrow">&gt;</div>
+                    </div>
+                </div>
+            </div>
+        </el-col>
+        <el-col :span="18">
+            <div class="bg-white main-box">
+                <el-empty description="您暂时还没有招报记录" ></el-empty>
+            </div>
+        </el-col>
+    </el-row>
+</div>
+
+{/block}
+{block name="script"}
+<script>
+    function v_setup() {
+        let base = {};
+
+
+        return base;
+    }
+</script>
+{/block}

+ 646 - 0
app/home/view/my/info.html

@@ -0,0 +1,646 @@
+{extend name="public/base"/}
+{block name="css"}
+<style>
+    .content-box{width:1200px;}
+    .bg-white {background: white;}
+    .user_main {margin-top:20px;}
+    .user_main .table-box {overflow: hidden;margin-top: 20px;padding:20px;box-sizing: border-box;}
+    .res_add_title{height:50px;font-size:20px;padding-left:50px;}
+
+    table.lw-table,table.lw-table tr th,table.lw-table tr td { border:1px solid #ccc; padding: 0 7px}
+    table.lw-table { width: 100%; min-height: 25px; line-height: 25px; text-align: center; border-collapse: collapse; padding:2px;}
+    .el-form-item{
+        margin-bottom: 0;
+    }
+    .require{
+        color: #f56c6c;
+    }
+    .avatar-uploader{
+        padding: 15px;
+    }
+    .avatar-uploader .el-upload {
+
+        border-radius: 6px;
+        cursor: pointer;
+        position: relative;
+        overflow: hidden;
+        display: inline-block;
+        vertical-align: middle;
+    }
+    .avatar-uploader .el-upload:hover {
+        border-color: #409EFF;
+    }
+    .avatar-uploader-icon {
+        font-size: 28px;
+        color: #8c939d;
+        width: 120px;
+        height: 120px;
+        line-height: 120px;
+        text-align: center;
+        border: 1px dashed #d9d9d9;
+    }
+    .avatar {
+        width: 120px;
+        height: 120px;
+        display: block;
+    }
+</style>
+{/block}
+{block name="body"}
+<div class="content-box">
+    <el-row class="user_main">
+        <el-col :span="24">
+            <div class="bg-white" style="padding:20px;">
+                <el-page-header @back="goBack" title="返回">
+                    <template #content>
+                        <span class="text-large font-600 mr-3"> 个人资料 </span>
+                    </template>
+                </el-page-header>
+            </div>
+        </el-col>
+        <el-col :span="24">
+            <div class="bg-white table-box">
+                <div class="res_add_title font_blue"><el-icon size="40" style="margin-right: 10px;"><Platform></Platform></el-icon>应招前请先完善个人基本信息!</div>
+                <el-form :model="user" ref="userForm" :show-message="false"  :rules="rules" v-loading="loading">
+                    <table class="lw-table" cellspacing="0" width="100%">
+                        <tbody>
+                        <tr style="height:52px;page-break-inside:avoid" class="firstRow">
+                            <td valign="center" width="100">
+                                <span class="require">*</span>姓名
+                            </td>
+                            <td valign="center" colspan="4" width="200">
+                                <el-form-item prop="realname" >
+                                    <el-input v-model="user.realname" placeholder="请输入您的姓名" style="width: 200px"></el-input>
+                                </el-form-item>
+                            </td>
+                            <td valign="center" width="80">
+                                <span class="require">*</span>性别
+                            </td>
+                            <td valign="center" colspan="2">
+                                <el-form-item prop="sex" >
+                                    <el-radio label="1" v-model="user.sex">男</el-radio>
+                                    <el-radio label="0" v-model="user.sex">女</el-radio>
+                                </el-form-item>
+                            </td>
+                            <td valign="center" width="100">
+                                <span class="require">*</span>出生年月
+                            </td>
+                            <td valign="center" colspan="2" width="200">
+                                <el-form-item prop="birthday">
+                                    <el-date-picker
+                                            v-model="user.birthday"
+                                            type="date"
+                                            placeholder="选择您的出生年月日"
+                                            value-format="YYYY-MM-DD">
+                                    </el-date-picker>
+                                </el-form-item>
+                            </td>
+                            <td valign="center" rowspan="3" width="200">
+                                <el-form-item prop="avatar" ref="avatar">
+                                    <el-upload
+                                            class="avatar-uploader"
+                                            action="{:url('upload/image')}"
+                                            ref="user_avatar"
+                                            :show-file-list="false"
+                                            :before-upload="beforeImageUpload"
+                                            :on-success="uploadSuccess">
+                                        <img v-if="user.avatar" :src="user.avatar" class="avatar">
+                                        <div v-else style="text-align: center;width: 150px;height: 100px;line-height: 100px;">
+                                            <el-icon :size="40"><Upload></Upload></el-icon>
+                                        </div>
+                                    </el-upload>
+                                </el-form-item>
+                                <div><span><span class="require">*</span>近期免冠证件照(2M以内)</span></div>
+                            </td>
+                        </tr>
+                        <tr style="height:52px;page-break-inside:avoid">
+                            <td valign="center" >
+                                <span class="require">*</span>身份证号码
+                            </td>
+                            <td valign="center" colspan="4" >
+                                <el-form-item prop="card">
+                                    <el-input v-model="user.card" placeholder="请输入身份证号码 "></el-input>
+                                </el-form-item>
+                            </td>
+                            <td valign="center">
+                                <span class="require">*</span>民族
+                            </td>
+                            <td valign="center" colspan="2" >
+                                <el-form-item prop="nation">
+                                    <el-input v-model="user.nation" placeholder="请输入您的民族"></el-input>
+                                </el-form-item>
+                            </td>
+                            <td valign="center" >
+                                <span class="require">*</span>籍贯
+                            </td>
+                            <td valign="center" colspan="2">
+                                <el-cascader placeholder="请选择" v-model="native_place" :options="options" :props="{value:'label'}" @change="nativeChange"></el-cascader>
+                            </td>
+                        </tr>
+                        <tr style="height:52px;page-break-inside:avoid">
+                            <td valign="center" >
+                                <span class="require">*</span>政治面貌
+                            </td>
+                            <td valign="center" colspan="4" >
+                                <el-form-item prop="political_affiliation">
+                                    <el-select v-model="user.political_affiliation" placeholder="请选择">
+                                        <el-option
+                                                v-for="item in political_affiliation"
+                                                :key="item.value"
+                                                :label="item.label"
+                                                :value="item.value">
+                                        </el-option>
+                                    </el-select>
+                                </el-form-item>
+                            </td>
+                            <td valign="center" >
+                                入党时间
+                            </td>
+                            <td valign="center" colspan="2">
+                                <el-input v-model="user.join_time" placeholder="非党员请放空"></el-input>
+                            </td>
+                            <td valign="center" >
+                                <span class="require">*</span>户籍所在地
+                            </td>
+                            <td valign="center" colspan="2" >
+                                <el-cascader placeholder="请选择" v-model="house_register" :options="options" :props="{value:'label'}" @change="houseChange"></el-cascader>
+                            </td>
+                        </tr>
+                        <tr style="height:52px;page-break-inside:avoid">
+                            <td valign="center" colspan="8" >
+                                <el-form-item prop="education" label="学历" label-width="70px">
+                                    <el-select v-model="user.education" placeholder="请选择学历">
+                                        <el-option label="小学" value="小学"></el-option>
+                                        <el-option label="初中" value="初中"></el-option>
+                                        <el-option label="技校" value="技校"></el-option>
+                                        <el-option label="职高" value="职高"></el-option>
+                                        <el-option label="高中" value="高中"></el-option>
+                                        <el-option label="中专" value="中专"></el-option>
+                                        <el-option label="专科" value="专科"></el-option>
+                                        <el-option label="本科" value="本科"></el-option>
+                                        <el-option label="硕士" value="硕士"></el-option>
+                                        <el-option label="博士" value="博士"></el-option>
+                                    </el-select>
+                                </el-form-item>
+                            </td>
+                            <td valign="center" >
+                                <span class="require">*</span>毕业院校
+                            </td>
+                            <td valign="center" colspan="4" >
+                                <el-form-item prop="school">
+                                    <el-input v-model="user.school" autosize placeholder="请输入毕业院校"></el-input>
+                                </el-form-item>
+                            </td>
+                        </tr>
+                        <tr style="height:52px;page-break-inside:avoid">
+                            <td valign="center" colspan="8" >
+                                <el-form-item prop="degree" label="学位" label-width="70px">
+                                    <el-select v-model="user.degree" placeholder="请选择学位">
+                                        <el-option label="无" value="无">无</el-option>
+                                        <el-option label="学士学位" value="学士学位"></el-option>
+                                        <el-option label="硕士学位" value="硕士学位"></el-option>
+                                        <el-option label="博士学位" value="博士学位"></el-option>
+                                    </el-select>
+                                </el-form-item>
+                            </td>
+                            <td valign="center" >
+                                <span class="require">*</span>所学专业
+                            </td>
+                            <td valign="center" colspan="4" >
+                                <el-form-item prop="pro">
+                                    <el-input v-model="user.pro" autosize placeholder="多专业请用;分隔"></el-input>
+                                </el-form-item>
+                            </td>
+                        </tr>
+
+                        <tr style="height:52px;page-break-inside:avoid">
+                            <td valign="center" >
+                                <span class="require">*</span>联系电话
+                            </td>
+                            <td valign="center" colspan="7" >
+                                <el-form-item prop="mobile">
+                                    <el-input v-model="user.mobile" placeholder="请输入联系电话"></el-input>
+                                </el-form-item>
+                            </td>
+                            <td valign="center" colspan="1" >
+                                <span class="require">*</span>电子邮箱
+                            </td>
+                            <td valign="center" colspan="3" >
+                                <el-form-item prop="email">
+                                    <el-input v-model="user.email" placeholder="请输入电子邮箱"></el-input>
+                                </el-form-item>
+                            </td>
+                        </tr>
+                        <tr style="height:52px;page-break-inside:avoid">
+                            <td valign="center">
+                                <span class="require">*</span>通信地址
+                            </td>
+                            <td valign="center" colspan="11" >
+                                <el-form-item prop="address">
+                                    <el-input v-model="user.address" placeholder="请输入地址"></el-input>
+                                </el-form-item>
+                            </td>
+                        </tr>
+                        <tr style="height:52px;page-break-inside:avoid">
+                            <td valign="center">
+                                现工作单位及职务
+                            </td>
+                            <td valign="center" colspan="7" >
+                                <el-input v-model="user.work" placeholder="请输入现工作单位及职务"></el-input>
+                            </td>
+                            <td valign="center" >
+                                职称或职业资格
+                            </td>
+                            <td valign="center" colspan="3">
+                                <el-input v-model="user.titles" placeholder="请输入职称或职业资格"></el-input>
+                            </td>
+                        </tr>
+                        <tr style="height:52px;page-break-inside:avoid">
+                            <td valign="center" >
+                                <span class="require">*</span>紧急联系人<br />姓名
+                            </td>
+                            <td valign="center" colspan="7" >
+                                <el-form-item prop="concat_name">
+                                    <el-input v-model="user.concat_name" placeholder="请输入紧急联系人姓名"></el-input>
+                                </el-form-item>
+                            </td>
+                            <td valign="center">
+                                <span class="require">*</span>联系电话
+                            </td>
+                            <td valign="center" colspan="7" >
+                                <el-form-item prop="concat_mobile">
+                                    <el-input v-model="user.concat_mobile" placeholder="请输入联系电话"></el-input>
+                                </el-form-item>
+                            </td>
+                        </tr>
+                        <tr style="height:275px;page-break-inside:avoid">
+                            <td valign="center" >
+                                <span class="require">*</span>个<br />
+                                &nbsp;人<br />
+                                &nbsp;简<br />
+                                &nbsp;历
+                            </td>
+                            <td valign="center" colspan="11" >
+                                <el-form-item prop="resume">
+                                    <el-input
+                                            type="textarea"
+                                            :rows="12"
+                                            :placeholder="resume_tip"
+                                            v-model="user.resume">
+                                    </el-input>
+                                </el-form-item>
+                            </td>
+                        </tr>
+                        <tr style="height:47px;page-break-inside:avoid">
+                            <td valign="center" :rowspan="user.family.length+1">
+                                家庭成员及其主要社会关系<br />
+                                <el-button type="primary" icon="el-icon-plus" size="small" @click="addFamily">添加家庭成员</el-button><br />
+                                <el-button type="danger" icon="el-icon-minus" size="small" @click="delFamily">删除最后一行</el-button>
+                            </td>
+                            <td valign="center" >
+                                <p style=";text-align:center">
+                                    <span style=";font-family:宋体;font-size:14px">称谓</span>
+                                </p>
+                            </td>
+                            <td width="200" valign="center" colspan="4" >
+                                <p style=";text-align:center">
+                                    <span style=";font-family:宋体;font-size:14px">姓名</span>
+                                </p>
+                            </td>
+                            <td width="87" valign="center" colspan="2" >
+                                <p style=";text-align:center">
+                                    <span style=";font-family:宋体;font-size:14px">出生年月</span>
+                                </p>
+                            </td>
+                            <td width="98" valign="center" colspan="1" >
+                                <p style=";text-align:center">
+                                    <span style=";font-family:宋体;font-size:14px">政治面貌</span>
+                                </p>
+                            </td>
+                            <td width="241" valign="center" colspan="3" >
+                                <p style=";text-align:center">
+                                    <span style=";font-family:宋体;font-size:14px">工作单位及职位</span>
+                                </p>
+                            </td>
+                        </tr>
+                        <tr style="height:52px;page-break-inside:avoid" v-for="(item,index) in user.family">
+                            <td width="85" valign="center">
+                                <el-form-item :prop="'family.'+index+'.relation'" :rules="{required: true, message: '称谓不能为空', trigger: 'blur'}">
+                                    <el-input v-model="item.relation" placeholder="请输入称谓"></el-input>
+                                </el-form-item>
+                            </td>
+                            <td width="200" valign="center" colspan="4" >
+                                <el-form-item :prop="'family.'+index+'.realname'" :rules="{required: true, message: '真实姓名不能为空', trigger: 'blur'}">
+                                    <el-input v-model="item.realname" placeholder="请输入真实姓名"></el-input>
+                                </el-form-item>
+                            </td>
+                            <td width="87" valign="center" colspan="2" >
+                                <el-form-item :prop="'family.'+index+'.birthday'" :rules="{required: true, message: '出生年月日不能为空', trigger: 'blur'}">
+                                    <el-date-picker
+                                            v-model="item.birthday"
+                                            type="date"
+                                            placeholder="选择出生年月日"
+                                            value-format="YYYY-MM-DD">
+                                    </el-date-picker>
+                                </el-form-item>
+                            </td>
+                            <td width="98" valign="center" colspan="1" >
+                                <el-form-item :prop="'family.'+index+'.political_affiliation'" :rules="{required: true, message: '请选择您的政治面貌', trigger: 'blur'}">
+                                    <el-select v-model="item.political_affiliation" placeholder="请选择">
+                                        <el-option
+                                                v-for="select in political_affiliation"
+                                                :key="select.value"
+                                                :label="select.label"
+                                                :value="select.value">
+                                        </el-option>
+                                    </el-select>
+                                </el-form-item>
+                            </td>
+                            <td width="241" valign="center" colspan="3" >
+                                <el-form-item :prop="'family.'+index+'.work'" :rules="{required: true, message: '请输入工作单位及职位', trigger: 'blur'}">
+                                    <el-input v-model="item.work" placeholder="请输入工作单位及职位"></el-input>
+                                </el-form-item>
+                            </td>
+                        </tr>
+                        <tfoot>
+                        <tr>
+                            <td height="52" colspan="12" >
+                                <el-form-item size="large">
+                                    <el-button type="primary" @click="save(userForm)">保存</el-button>
+                                </el-form-item>
+                            </td>
+                        </tr>
+                        </tfoot>
+                        </tbody>
+                    </table>
+                </el-form>
+            </div>
+        </el-col>
+    </el-row>
+</div>
+
+{/block}
+{block name="script"}
+<script src="/static/home/js/qs.min.js"></script>
+<script src="/static/home/js/area.js"></script>
+<script>
+    function v_setup() {
+        let base = {};
+
+        base.goBack = () => {
+            history.back();
+        };
+
+        base.loading = Vue.ref(false);
+        base.userForm = Vue.ref();
+
+        base.resume_tip = "个人简历请严格按如下格式填写:\r\n1993.09-1996.07 \t××市××中学(高中) 学生;\r\n1996.09-2000.07 \t×××××大学(本科)工商管理专业 学生;\r\n2000.09-2001.03\t待业;\r\n2001.04-2004.08\t×××××有限公司(私营企业)总务科 后勤;\r\n2004.09-2007.06\t××市××单位(事业单位)非编 经办;\r\n2005.09-2008.07\t××省×××大学(在职研究生)工商管理专业  学生;\r\n2007.08-至今\t ××省×××单位(参公事业单位)在编  科员。\r\n说明:1.从高中写起到至今,中间不可中断。\r\n \t  2.对在职学习的,应注明。";
+        base.user = Vue.ref({$info});
+        native_place = base.user.value.native_place.split(',');
+        if (native_place[3] === 'undefined') {
+            native_place.splice(3,1);
+        }
+        base.native_place = Vue.ref(native_place);
+        house_register = base.user.value.house_register.split(',');
+        if (house_register[3] === 'undefined') {
+            house_register.splice(3,1);
+        }
+        base.house_register = Vue.ref(house_register);
+
+
+        base.addFamily = () => {
+            base.user.value.family.push({
+                relation:'',
+                realname:'',
+                birthday:'',
+                political_affiliation:'',
+                work:''
+            });
+        };
+        base.delFamily = () => {
+            var length = base.user.value.family.length;
+            if (length > 1) {
+                base.user.value.family.pop()
+            }else{
+                ElMessage.error('只剩一条记录,不能再删啦');
+            }
+        };
+        base.nativeChange = (native) => {
+            if(native[3] != "undefined"){
+                base.user.value.native_place = native[0] + ',' + native[1] + ',' + native[2] + ',' + native[3]
+            }else{
+                base.user.value.native_place = native[0] + ',' + native[1] + ',' + native[2]
+            }
+        };
+        base.houseChange = (house) => {
+            if(house[3] != "undefined"){
+                base.user.value.house_register = house[0] + ',' + house[1] + ',' + house[2] + ',' + house[3]
+            }else{
+                base.user.value.house_register = house[0] + ',' + house[1] + ',' + house[2]
+            }
+        };
+
+        base.beforeImageUpload = (file) => {
+            const isJPG = (file.type === 'image/jpeg' || file.type === 'image/png');
+            const isLt2M = file.size / 1024 / 1024 < 2;
+
+            if (!isJPG) {
+                ElMessage.error('上传图片只能是 JPG、PNG 格式!');
+            }
+            if (!isLt2M) {
+                ElMessage.error('上传图片大小不能超过 2MB!');
+            }
+            return isJPG && isLt2M;
+        };
+        base.uploadSuccess = (response, file, fileList) => {
+            if(!response.code){
+                base.user.value.avatar = response.data.src;
+            }else{
+                ElMessage.error(response.msg);
+            }
+            console.log(base.user.value.avatar)
+        };
+
+        base.validID = (rule, value, callback) => {
+            if(value.length == 18){
+                if(!base.checkIDCard(value)) {
+                    callback(new Error('身份证号不正确'));
+                }
+            }
+            callback();
+        };
+        base.checkIDCard = (cardNo) => {
+            var weight_factor = [7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2];
+            var check_code = ['1', '0', 'X' , '9', '8', '7', '6', '5', '4', '3', '2'];
+            var code = cardNo + "";
+            var last = cardNo[17];//最后一位
+            var seventeen = code.substring(0,17);
+            var arr = seventeen.split("");
+            var len = arr.length;
+            var num = 0;
+            for(var i = 0; i < len; i++){
+                num = num + arr[i] * weight_factor[i];
+            }
+            var resisue = num%11;
+            var last_no = check_code[resisue];
+            var idcard_patter = /^[1-9][0-9]{5}([1][9][0-9]{2}|[2][0][0|1][0-9])([0][1-9]|[1][0|1|2])([0][1-9]|[1|2][0-9]|[3][0|1])[0-9]{3}([0-9]|[X])$/;
+            var format = idcard_patter.test(cardNo);
+            return last === last_no && format ? true : false;
+        };
+        base.validMobile = (rule, value, callback) => {
+            if(!(/^1[3456789]\d{9}$/.test(value))){
+                callback(new Error('手机号码不正确'));
+            }
+            callback();
+        };
+        base.validEmail = (rule, value, callback) => {
+            if(!(/^([a-zA-Z0-9]+[_|\_|\.]?)*[a-zA-Z0-9]+@([a-zA-Z0-9]+[_|\_|\.]?)*[a-zA-Z0-9]+\.[a-zA-Z]{2,3}$/.test(value))){
+                callback(new Error('电子邮箱不正确'));
+            }
+            callback();
+        };
+        base.rules = {
+            realname:[
+                {required: true, message: '请输入您的姓名', trigger: 'blur'}
+            ],
+            sex:[
+                { required: true, message: '请选择性别', trigger: 'change' }
+            ],
+            birthday:[
+                {required: true, message: '请选择出生日期', trigger: 'change' }
+            ],
+            avatar:[
+                {required: true, message: '请上传蓝底/红底近期证件照', trigger: 'change'}
+            ],
+            card:[
+                {required: true, message: '请输入证件号', trigger: 'blur'},
+                {validator:base.validID, trigger: 'blur'}
+            ],
+            nation:[
+                {required: true, message: '请输入您的民族', trigger: 'blur'}
+            ],
+            native_place:[
+                {required: true, message: '请输入您的籍贯', trigger: 'blur'}
+            ],
+            political_affiliation:[
+                {required: true, message: '请选择您的政治面貌', trigger: 'blur'}
+            ],
+            house_register:[
+                {required: true, message: '请输入您的户籍所在地', trigger: 'blur'}
+            ],
+            school:[
+                {required: true, message: '请输入学校信息', trigger: 'blur'}
+            ],
+            education:[
+                {required: true, message: '请输入学历信息', trigger: 'blur'}
+            ],
+            pro:[
+                {required: true, message: '请输入所学专业', trigger: 'blur'}
+            ],
+            mobile:[
+                {required: true, message: '请输入手机号', trigger: 'blur'},
+                {validator:base.validMobile, trigger: 'blur'}
+            ],
+            email:[
+                {required: true, message: '请输入电子邮箱', trigger: 'blur'},
+                {validator:base.validEmail, trigger: 'blur'}
+            ],
+            address:[
+                {required: true, message: '请输入地址', trigger: 'blur'}
+            ],
+            is_push:[
+                { required: true, message: '请选择是否接送推送', trigger: 'change' }
+            ],
+            concat_name:[
+                {required: true, message: '请输入紧急联系人姓名', trigger: 'blur'}
+            ],
+            concat_mobile:[
+                {required: true, message: '请输入紧急联系人电话', trigger: 'blur'}
+            ],
+            resume:[
+                {required: true, message: '请输入个人简历', trigger: 'blur'},
+            ]
+
+        };
+
+        base.political_affiliation = [ //国家关于政治面貌的分类标准
+            {
+                value: '01',
+                label: '中共党员'
+            },
+            {
+                value: '02',
+                label: '中共预备党员'
+            },
+            {
+                value: '03',
+                label: '共青团员'
+            },
+            {
+                value: '04',
+                label: '民革党员'
+            },
+            {
+                value: '05',
+                label: '民盟盟员'
+            },
+            {
+                value: '06',
+                label: '民建会员'
+            },
+            {
+                value: '07',
+                label: '民进会员'
+            },
+            {
+                value: '08',
+                label: '农工党党员'
+            },
+            {
+                value: '09',
+                label: '致公党党员'
+            },
+            {
+                value: '10',
+                label: '九三学社社员'
+            },
+            {
+                value: '11',
+                label: '台盟盟员'
+            },
+            {
+                value: '12',
+                label: '无党派人士'
+            },
+            {
+                value: '13',
+                label: '群众'
+            }
+        ];
+
+        base.save = (formEl) => {
+            if (!formEl) return;
+            formEl.validate((valid,object) => {
+                if (valid) {
+                    base.loading.value = true;
+                    postJson('/my/infoPost',base.user.value).then(({msg}) => {
+                        ElMessage.success(msg);
+                        base.loading.value = false;
+                    });
+                } else {
+                    for(let key in object){
+                        ElMessage.error(object[key][0].message);
+                        return false;
+                    }
+
+                    return false;
+                }
+            })
+        },
+
+        base.options = area_list;
+
+        return base;
+    }
+</script>
+{/block}

+ 75 - 0
app/home/view/my/tip.html

@@ -0,0 +1,75 @@
+{extend name="public/base"/}
+{block name="css"}
+<style>
+    .content-box{width:1200px;}
+    .bg-white {background: white;}
+    .user_main {margin-top:20px;}
+    .user_panel_header {display: flex;align-items: flex-end;padding:20px;box-sizing: border-box;border-bottom: 1px solid #ebebeb;}
+    .user_panel_header .username {color: #333333;font-size: 16px;}
+    .user_panel_header h4 {margin-left: 10px;color: #999999;font-size: 14px;font-weight: normal;}
+    .user_main .nav_list {overflow: hidden;margin-top: 20px;padding-bottom: 40px;display: flex;align-items: center;justify-content: center;flex-direction: column;}
+    .user_main .list {height: 45px;display: flex;align-items: center;justify-content: center;}
+    .user_main .list .arrow {cursor: pointer;color: #d6d6d6;font-size: 20px;margin-left:50px;}
+    .user_main .list a {color: #999999;font-size: 16px;margin-left:10px;}
+    .user_main .list a.select,.user_main .list .arrow.select {color:#dd4250;}
+
+    .main-box {min-height: 600px;}
+</style>
+{/block}
+{block name="body"}
+<div class="content-box">
+    <el-row class="user_main" :gutter="20">
+        <el-col :span="6">
+            <div class="bg-white" >
+                <div class="user_panel_header">
+                    <div class="username substring">Hi,{:empty($user) ? '' : $user['realname']}</div>
+                    <h4>欢迎回来</h4>
+                </div>
+                <div class="nav_list">
+                    <div class="list">
+                        <el-icon size="24" color="#999999"><Document></Document></el-icon>
+                        <a href="{:url('my/index')}">报考记录</a>
+                        <div class="arrow">&gt;</div>
+                    </div>
+                    <div class="list">
+                        <el-icon size="24" color="#999999"><User></User></el-icon>
+                        <a href="{:url('my/info')}">个人资料</a>
+                        <div class="arrow">&gt;</div>
+                    </div>
+                    <div class="list">
+                        <el-icon size="24" color="#999999"><Hide></Hide></el-icon>
+                        <a class="" href="{:url('my/account')}">账号安全</a>
+                        <div class="arrow">&gt;</div>
+                    </div>
+                    <div class="list">
+                        <el-icon size="24" color="#dd4250"><Bell></Bell></el-icon>
+                        <a class="select" href="{:url('my/tip')}">我的消息</a>
+                        <div class="arrow select">&gt;</div>
+                    </div>
+                    <div class="list">
+                        <el-icon size="24" color="#999999"><Message></Message></el-icon>
+                        <a class="" href="{:url('my/feedback')}">意见反馈</a>
+                        <div class="arrow">&gt;</div>
+                    </div>
+                </div>
+            </div>
+        </el-col>
+        <el-col :span="18">
+            <div class="bg-white main-box">
+                <el-empty description="您暂时还没有招报记录" ></el-empty>
+            </div>
+        </el-col>
+    </el-row>
+</div>
+
+{/block}
+{block name="script"}
+<script>
+    function v_setup() {
+        let base = {};
+
+
+        return base;
+    }
+</script>
+{/block}

+ 49 - 0
app/home/view/news/detail.html

@@ -0,0 +1,49 @@
+{extend name="public/base"/}
+{block name="css"}
+<style>
+    .news{width: 1200px;margin:0 auto;margin-top:50px;background: white;border: 1px #ebeef5 solid;box-sizing: border-box;}
+    .news .ntitle {font-size: 28px;padding-top: 18px;line-height: 180%;text-align: center;padding-bottom: 10px;}
+    .news .time {border-bottom: 1px #EEEEEE solid;border-top: 1px #EEEEEE solid;width: 800px;margin: 0 auto;height: 40px;line-height: 40px;text-align: center;}
+    .news .time span {padding-left: 15px;padding-right: 15px;}
+    .news .txt {font-size: 14px;line-height: 200%;padding: 50px;padding-top: 20px;word-break: break-all;}
+
+    .r {width: 1200px;margin:0 auto;margin-top: 20px;}
+    .r .listbox {border: 1px #ebeef5 solid;margin-top: 10px;background-color: #fff;}
+    .r .listbox .ntit {height: 36px;padding-top: 10px;}
+    .r .listbox .ntit .tlh {margin-left: 30px;height: 20px;line-height: 20px;font-size: 17px;color: #000000;padding-left: 10px;border-left: 3px #fe2e5f solid;}
+    .r .listbox li {background-image: url("/static/home/images/123.png");background-repeat: no-repeat;background-position: 8px 19px;margin: 10px 23px;height: 30px;line-height: 30px;padding: 5px 15px;overflow: hidden;white-space: nowrap;text-overflow: ellipsis;border-bottom: 1px solid #ebeef5;}
+    .r .listbox li:last-child {border-bottom-color: transparent;}
+    .link_gray6 a {color: #666666;text-decoration: none;}
+</style>
+{/block}
+{block name="body"}
+<div class="news">
+    <div class="ntitle">{$info.title}</div>
+    <div class="time">
+        <span>日期:{$info.update_show}</span>
+        <span>浏览 {$info.volume} 次</span>
+    </div>
+    <div class="txt">
+        {$info.content}
+    </div>
+</div>
+<div class="r">
+    <div class="listbox">
+        <div class="ntit">
+            <div class="tlh">相关招考</div>
+        </div>
+        <ul class="link_gray6">
+            <li><a target="_blank" href="{:url('recruit/detail')}?id={$info.recruit.id}">{$info.recruit.name}</a></li>
+        </ul>
+    </div>
+</div>
+{/block}
+{block name="script"}
+<script>
+    function v_setup() {
+        let base = {};
+
+        return base;
+    }
+</script>
+{/block}

+ 96 - 0
app/home/view/news/index.html

@@ -0,0 +1,96 @@
+{extend name="public/base"/}
+{block name="css"}
+<style>
+    .substring {overflow: hidden;white-space: nowrap;text-overflow: ellipsis;}
+
+    .new-se-group {width: 100%;height: 155px;background-image: url("/static/home/images/nebanner.jpg");background-repeat: no-repeat;background-position: center center;}
+    .new-se-group .new-se-main {width: 1200px;height: 99px;padding-top: 56px;margin: 0 auto;}
+    .new-se-group .new-se-main .ip-group {margin: 0 auto;background: #FFF;height: 45px;width: 770px;-webkit-border-radius: 3px;-moz-border-radius: 3px;border-radius: 3px;}
+    .new-se-group .new-se-main .ip-box {float: left;padding-top: 7px;width: 626px;padding-left: 17px;}
+    .new-se-main .ip-box input {width: 616px;height: 28px;line-height: 36px;border: 0;}
+    .new-se-group .new-se-main .ip-btn {float: left;width: 127px;height: 45px;background-color: #dd4250;font-size: 18px;text-align: center;line-height: 45px;border: 0;color: #FFFFFF;cursor: pointer;border-radius: 0 3px 3px 0;}
+
+    .list_content {background-color: #FFF;margin: 0 auto;width: 1200px;margin-top: 40px;}
+    .list_content .ul {padding: 0 35px;}
+    .list_content .ul .li {padding: 25px 0;border-bottom: 1px solid #ECECEC;}
+    .list_content .ul .li .notice {font-size: 16px;font-weight: bold;}
+    .list_content .ul .li .date {margin: 5px 0;font-size: 12px;color: rgb(156, 155, 155);}
+    .list_content .article {position: relative;line-height: 14px;}
+    .list_content .article .article-content {display: inline-block;width: 1053px;font-size: 12px;color: #999999;overflow: hidden;white-space: nowrap;text-overflow: ellipsis;}
+    .list_content .article .article-all {font-size: 12px;color: #2C619D;position: absolute;right: 0;}
+
+    .el-pagination {justify-content: center;padding:10px 0;}
+    .el-pagination.is-background .el-pager li.is-active {background-color: #dd4250;color: var(--el-color-white);}
+</style>
+{/block}
+{block name="body"}
+<div class="new-se-group">
+    <div class="new-se-main">
+        <div class="ip-group">
+            <div class="ip-box"><input type="text" v-model="keyword" placeholder="请输入关键字" /></div>
+            <div class="ip-btn" @click="onSubmit">搜公告</div>
+            <div class="clear"></div>
+        </div>
+    </div>
+</div>
+<div class="list_content" v-loading="loading">
+    <ul class="ul">
+        <li class="li" v-for="item in list" @click="toDetail(item.id)">
+            <div class="notice">
+                <a href="https://hxks.hxrc-app.com/content/recruit/article/show?id=702" style="color:black">
+                    {{item.title}}
+                </a>
+            </div>
+            <div class="date">日期:{{item.update_show}}&nbsp;浏览{{item.volume}}次
+            </div>
+            <div class="article">
+                <div class="article-content">{{item.summary}}</div>
+
+                <a href="https://hxks.hxrc-app.com/content/recruit/article/show?id=702" class="article-all">查看全文</a>
+            </div>
+        </li>
+    </ul>
+    <el-empty description="暂无更多数据" v-show="list.length == 0"></el-empty>
+    <el-pagination background :hide-on-single-page="false" :page-size="limit" layout="prev, pager, next" :total="total" @current-change="onPage"></el-pagination>
+</div>
+{/block}
+{block name="script"}
+<script>
+    function v_setup() {
+        let base = {};
+
+        base.keyword = Vue.ref("{$keyword}");
+        base.onSubmit = () => {
+            location.href = "{:url('news/index')}?keyword=" + base.keyword.value;
+        };
+
+        base.loading = Vue.ref(true);
+        base.page = Vue.ref(1);
+        base.total = Vue.ref({$total});
+        base.limit = Vue.ref({$limit});
+        base.list = Vue.reactive([]);
+        base.onPage = num => {
+            base.page.value = num;
+            base.getList();
+        };
+        base.getList = () => {
+            base.loading.value = true;
+            postJson("{:url('news/list')}", {page:base.page.value,limit:base.limit.value,keyword:base.keyword.value}).then(function ({data}) {
+                base.loading.value = false;
+                if (data.length === 0) {
+                    base.finished.value = true;
+                } else {
+                    base.list = data;
+                }
+            });
+        };
+        base.getList();
+
+        base.toDetail = id => {
+            location.href = "{:url('news/detail')}?id=" + id;
+        };
+
+        return base;
+    }
+</script>
+{/block}

+ 29 - 0
app/home/view/notice/detail.html

@@ -0,0 +1,29 @@
+{extend name="public/base"/}
+{block name="css"}
+<style>
+    .news{width: 1200px;margin:0 auto;margin-top:50px;background: white;border: 1px #ebeef5 solid;box-sizing: border-box;}
+    .news .ntitle {font-size: 28px;padding-top: 18px;line-height: 180%;text-align: center;padding-bottom: 10px;}
+    .news .time {border-bottom: 1px #EEEEEE solid;border-top: 1px #EEEEEE solid;width: 800px;margin: 0 auto;height: 40px;line-height: 40px;text-align: center;}
+    .news .txt {font-size: 14px;line-height: 200%;padding: 50px;padding-top: 20px;word-break: break-all;}
+</style>
+{/block}
+{block name="body"}
+    <div class="news">
+        <div class="ntitle">{$info.title}</div>
+        <div class="time">
+            <span>日期:{$info.update_show}</span>
+        </div>
+        <div class="txt">
+            {$info.content}
+        </div>
+    </div>
+{/block}
+{block name="script"}
+<script>
+    function v_setup() {
+        let base = {};
+
+        return base;
+    }
+</script>
+{/block}

+ 100 - 0
app/home/view/notice/index.html

@@ -0,0 +1,100 @@
+{extend name="public/base"/}
+{block name="css"}
+<style>
+    .substring {overflow: hidden;white-space: nowrap;text-overflow: ellipsis;}
+
+    .new-se-group {width: 100%;height: 155px;background-image: url("/static/home/images/nebanner.jpg");background-repeat: no-repeat;background-position: center center;}
+    .new-se-group .new-se-main {width: 1200px;height: 99px;padding-top: 56px;margin: 0 auto;}
+    .new-se-group .new-se-main .ip-group {margin: 0 auto;background: #FFF;height: 45px;width: 770px;-webkit-border-radius: 3px;-moz-border-radius: 3px;border-radius: 3px;}
+    .new-se-group .new-se-main .ip-box {float: left;padding-top: 7px;width: 626px;padding-left: 17px;}
+    .new-se-main .ip-box input {width: 616px;height: 28px;line-height: 36px;border: 0;}
+    .new-se-group .new-se-main .ip-btn {float: left;width: 127px;height: 45px;background-color: #dd4250;font-size: 18px;text-align: center;line-height: 45px;border: 0;color: #FFFFFF;cursor: pointer;border-radius: 0 3px 3px 0;}
+
+    .panel {background-color: #FFF;margin: 0 auto;width: 1200px;margin-top: 40px;}
+    .newslist{width:100%;background-color: #fff;border-bottom: 1px #ebeef5 solid;margin-top: 10px;padding: 5px 30px 50px 30px;box-sizing: border-box;}
+    .newslist .list {border-bottom: 1px #ebeef5 solid;padding-top: 20px;padding-bottom: 20px;cursor: pointer;}
+    .newslist .list:hover .t span {text-decoration: underline;}
+    .newslist .list .br {padding-left:10px;width: 100%;}
+    .newslist .list .br .t {color:#000;font-size: 18px;height: 30px;float: left;width: 500px;overflow: hidden;white-space: nowrap;text-overflow: ellipsis;font-weight: bold;}
+
+    .newslist .list .br .time {width: 300px;float: right;text-align: right;height: 25px;color: #dd4250;padding-top: 2px;}
+    .newslist .list .br .time .time1 {padding-left: 10px;position: relative;}
+    .newslist .list .br .summary {width: 100%;font-size: 12px;color: #a0a0a0;-ms-word-break: break-all;word-break: break-all;line-height: 24px;}
+    .ellipsis-multiline {display: -webkit-box;-webkit-box-orient: vertical;-webkit-line-clamp: 3;overflow: hidden;text-overflow: ellipsis;}
+
+    .el-pagination {justify-content: center;padding:10px 0;}
+    .el-pagination.is-background .el-pager li.is-active {background-color: #dd4250;color: var(--el-color-white);}
+</style>
+{/block}
+{block name="body"}
+<div class="new-se-group">
+    <div class="new-se-main">
+        <div class="ip-group">
+            <div class="ip-box"><input type="text" v-model="keyword" placeholder="请输入关键字"></div>
+            <div class="ip-btn" @click="onSubmit">搜通知</div>
+            <div class="clear"></div>
+        </div>
+    </div>
+</div>
+<div class="panel" v-loading="loading">
+    <div class="l" style="width: 100%;">
+        <div class="newslist">
+            <div class="list" v-for="item in list" @click="toDetail(item.id)">
+                <div class="br link_gray6">
+                    <div class="t substring">
+                        <span>{{item.title}}</span>
+                    </div>
+                    <div class="time substring">
+                        <span class="time1">{{item.update_show}}</span>
+                    </div>
+                    <div class="clear"></div>
+                    <div class="summary ellipsis-multiline">{{item.summary}}</div>
+                </div>
+            </div>
+            <el-empty description="暂无更多数据" v-show="list.length == 0"></el-empty>
+        </div>
+        <el-pagination background :hide-on-single-page="true" :page-size="limit" layout="prev, pager, next" :total="total" @current-change="onPage"></el-pagination>
+    </div>
+</div>
+{/block}
+{block name="script"}
+<script>
+    function v_setup() {
+        let base = {};
+
+        base.keyword = Vue.ref("{$keyword}");
+        base.onSubmit = () => {
+            location.href = "{:url('notice/index')}?keyword=" + base.keyword.value;
+        };
+
+        base.loading = Vue.ref(true);
+        base.page = Vue.ref(1);
+        base.total = Vue.ref({$total});
+        base.limit = Vue.ref({$limit});
+        base.list = Vue.reactive([]);
+        base.onPage = num => {
+            base.page.value = num;
+            base.getList();
+        };
+        base.getList = () => {
+            base.loading.value = true;
+            postJson("{:url('notice/list')}", {page:base.page.value,limit:base.limit.value,keyword:base.keyword.value}).then(function ({data}) {
+                base.loading.value = false;
+                if (data.length === 0) {
+                    base.finished.value = true;
+                } else {
+                    base.list = data;
+                }
+            });
+        };
+        base.getList();
+
+        base.toDetail = id => {
+            location.href = "{:url('notice/detail')}?id=" + id;
+        };
+
+
+        return base;
+    }
+</script>
+{/block}

+ 108 - 0
app/home/view/public/base.html

@@ -0,0 +1,108 @@
+<!DOCTYPE html>
+<html lang="zh-cn">
+<head>
+    {include file="public/meta_header"/}
+    {block name="meta"}{/block}
+    {block name="css"}{/block}
+</head>
+<body>
+<div id="app">
+    <div class="index_header">
+        <div class="header_top">
+            <div class="font_gray6">
+                <a href="/">
+                    <img style="width: 60px;" src="/static/home/images/img_logo.png">
+                </a>
+            </div>
+            <div class="itopr font_gray9" style="width: 400px;position: relative;">
+                {if condition="get_user_id()"}
+                <ul class="link">
+                    <li class="link_yellow">
+                        <a href="{:url('center/index')}" class="head_min_username" id="username_id">
+                            {:empty($user) ? '' : $user['realname']}
+                        </a>
+                        <a href="{:url('center/msg')}" class="head_my_msg"> <span class="msg_count">我的消息 </span></a>
+                        <a href="{:url('login/logout')}">退出</a>
+                    </li>
+                </ul>
+                {/if}
+                {if condition="!get_user_id()"}
+                <ul class="link">
+                    <li class="per">
+                        <el-icon size="20" color="#dd4250"><User></User></el-icon>
+                        <a href="{:url('login/index')}" class="login-text">个人登录</a>
+                        <span class="login-text-line">|</span>
+                        <a href="{:url('register/index')}" class="register_user">注册</a>
+                    </li>
+                </ul>
+                {/if}
+            </div>
+        </div>
+    </div>
+
+    <?php $tab = $tab ?? '';?>
+    <div class="index_navlist">
+        <div class="index_navlist_list">
+            <ul>
+                <li class="nli J_hoverbut {eq name='tab' value='index'}select{/eq}">
+                    <a href="{:url('/')}">首页</a>
+                </li>
+                <li class="nli J_hoverbut {eq name='tab' value='recruit'}select{/eq}">
+                    <a class="navlist_item_name" href="{:url('recruit/index')}">招聘</a>
+                </li>
+                <li class="nli J_hoverbut {eq name='tab' value='news'}select{/eq}">
+                    <a class="navlist_item_name" href="{:url('news/index')}">公告</a>
+                </li>
+                <li class="nli J_hoverbut {eq name='tab' value='notice'}select{/eq}">
+                    <a class="navlist_item_name" href="{:url('notice/index')}">系统通知</a>
+                </li>
+                <li class="nli J_hoverbut {eq name='tab' value='help'}select{/eq}">
+                    <a href="{:url('help/index')}">常见问题</a>
+                </li>
+                <li class="nli J_hoverbut {eq name='tab' value='about'}select{/eq}">
+                    <a class="navlist_item_name" href="{:url('about/index')}">单位介绍</a>
+                </li>
+            </ul>
+        </div>
+        <div class="clear"></div>
+    </div>
+    {block name="body"}{/block}
+    <div class="index_footer">
+        <div class="index_footer_links">
+            <a target="_blank" href="http://hxks.hxrc-app.com">网站首页</a>
+            <a target="_blank" href="http://hxks.hxrc-app.com/content/explain/show/9">单位介绍</a>
+            <a target="_blank" href="http://hxks.hxrc-app.com/content/suggest/index">意见建议</a>
+        </div>
+        <div class="index_footertxt">
+            <p>招考服务热线:0595-82008296</p>
+            <p>地址:福建省泉州市晋江市梅岭街道碧山社区梅岭路273号</p>
+            <p>网站备案:<a href="https://beian.miit.gov.cn/" target="_blank">闽ICP备17033895号</a></p>
+
+        </div>
+    </div>
+</div>
+
+{block name="script"}{/block}
+<script>
+    const vue3 = {
+        setup() {
+            return v_setup();
+        }
+    };
+
+    const app = Vue.createApp(vue3);
+    for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
+        app.component(key, component);
+    }
+    app.use(ElementPlus,{
+        locale: ElementPlusLocaleZhCn,
+    });
+    const ElMessageBox = ElementPlus.ElMessageBox;
+    const ElMessage = ElementPlus.ElMessage;
+</script>
+{block name="vue"}{/block}
+<script>
+    app.mount('#app');
+</script>
+</body>
+</html>

+ 47 - 0
app/home/view/public/jump.html

@@ -0,0 +1,47 @@
+{extend name="public/base"/}
+{block name="css"}
+<style>
+    .msg{
+        text-align:center;
+        margin:0;
+        padding:0;
+        font-size:24px;
+        margin-top: 100px;
+    }
+
+    .tips{
+        text-align:center;
+        margin-top:50px;
+        font-size:14px;
+        color:#aaa;
+    }
+
+    .tips .num{
+        color:#FF589B;
+    }
+</style>
+{/block}
+{block name="body"}
+<h3 class="msg">{$msg}</h3>
+<p class="tips">还有<span class="num">{{wait}}</span>秒后自动跳转</p>
+{/block}
+{block name="script"}
+<script>
+    function v_setup() {
+        let base = {};
+
+        base.wait = Vue.ref({$wait});
+        const created = () => {
+            setInterval(function () {
+                base.wait.value--;
+                if (base.wait.value === 0) {
+                    {$url}
+                }
+            }, 1000);
+        }
+        created();
+
+        return base;
+    }
+</script>
+{/block}

+ 51 - 0
app/home/view/public/list_load.html

@@ -0,0 +1,51 @@
+<van-pull-refresh v-model="refreshing" @refresh="onRefresh">
+    <van-list
+            v-model:loading="loading"
+            :finished="finished"
+            finished-text="没有更多了"
+            @load="onList"
+    >
+        [list]
+    </van-list>
+</van-pull-refresh>
+
+<script>
+    function list_load(url, form = {}) {
+        let base = {};
+
+        base.form = Vue.reactive(form);
+        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 = {...base.form};
+            param.page = base.page.value;
+            base.page.value++;
+
+            postJson(url, param).then(function ({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();
+        };
+
+        return base;
+    }
+</script>

+ 19 - 0
app/home/view/public/meta_header.html

@@ -0,0 +1,19 @@
+<meta charset="utf-8">
+<title>{:config('home.site_name')}</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" />
+<meta name="apple-mobile-web-app-status-bar-style" content="black" />
+<!--<link rel="stylesheet" href="__HCSS__/element.css">-->
+<link rel="stylesheet" href="__HCSS__/element_plus_2.8.2.css">
+<link rel="stylesheet" href="__HCSS__/style.css">
+<!--<script src="__HJS__/vue.min.js"></script>-->
+<script src="__COMMON_JS__/vue3.3.4.min.js"></script>
+<script src="__HJS__/element_plus_2.8.2.js"></script>
+<script src="__HJS__/element_plus_icons.js"></script>
+<script src="__HJS__/element_plus_zhcn.js"></script>
+<script src="__COMMON_JS__/axios1.4.min.js"></script>
+<script>
+    const baseUrl = "{:url('/')}";
+</script>
+<script src="__HJS__/axios_instance.js"></script>

+ 96 - 0
app/home/view/recruit/index.html

@@ -0,0 +1,96 @@
+{extend name="public/base"/}
+{block name="css"}
+<style>
+    .substring {overflow: hidden;white-space: nowrap;text-overflow: ellipsis;}
+
+    .new-se-group {width: 100%;height: 155px;background-image: url("/static/home/images/nebanner.jpg");background-repeat: no-repeat;background-position: center center;}
+    .new-se-group .new-se-main {width: 1200px;height: 99px;padding-top: 56px;margin: 0 auto;}
+    .new-se-group .new-se-main .ip-group {margin: 0 auto;background: #FFF;height: 45px;width: 770px;-webkit-border-radius: 3px;-moz-border-radius: 3px;border-radius: 3px;}
+    .new-se-group .new-se-main .ip-box {float: left;padding-top: 7px;width: 626px;padding-left: 17px;}
+    .new-se-main .ip-box input {width: 616px;height: 28px;line-height: 36px;border: 0;}
+    .new-se-group .new-se-main .ip-btn {float: left;width: 127px;height: 45px;background-color: #dd4250;font-size: 18px;text-align: center;line-height: 45px;border: 0;color: #FFFFFF;cursor: pointer;border-radius: 0 3px 3px 0;}
+
+    .panel {margin: 0 auto;width: 1200px;margin-top: 40px;}
+    .newslist{width:100%;background-color: #fff;border-bottom: 1px #ebeef5 solid;margin-top: 10px;padding: 5px 30px 50px 30px;box-sizing: border-box;}
+    .newslist .list {border-bottom: 1px #ebeef5 solid;padding-top: 20px;padding-bottom: 20px;}
+    .newslist .list .br {padding-left:10px;width: 100%;}
+    .newslist .list .br .t {color:#000;font-size: 18px;height: 30px;float: left;width: 1000px;overflow: hidden;white-space: nowrap;text-overflow: ellipsis;font-weight: bold;}
+    .newslist .list .br .date {margin: 5px 0;font-size: 12px;color: rgb(156, 155, 155);}
+
+    .newslist .list .br .time {width: 120px;float: right;text-align: right;height: 25px;color: #dd4250;padding-top: 2px;}
+    .newslist .list .br .time .time1 {padding-left: 10px;position: relative;}
+
+    .el-pagination {justify-content: center;padding:10px 0;}
+    .el-pagination.is-background .el-pager li.is-active {background-color: #dd4250;color: var(--el-color-white);}
+</style>
+{/block}
+{block name="body"}
+<div class="new-se-group">
+    <div class="new-se-main">
+        <div class="ip-group">
+            <div class="ip-box"><input type="text" v-model="keyword" placeholder="请输入关键字"></div>
+            <div class="ip-btn" @click="onSubmit">搜招聘</div>
+            <div class="clear"></div>
+        </div>
+    </div>
+</div>
+<div class="panel" v-loading="loading">
+    <div class="l" style="width: 100%;">
+        <div class="newslist">
+            <div class="list" v-for="item in list">
+                <div class="br link_gray6">
+                    <div class="t substring">
+                        <span>{{item.name}}</span>
+                    </div>
+                    <div class="time substring">
+                        <span class="time1">{{item.current_text}}</span>
+                    </div>
+                    <div class="clear"></div>
+                    <div class="date">报名日期:{{item['apply_time'][0]}} - {{item['apply_time'][1]}}
+                    <div class="article" style="margin-top:10px;">
+                        <a @click="toDetail(item.id)" style="color:#1280DD;cursor: pointer;">[查看全文]</a>
+                    </div>
+                </div>
+            </div>
+            <el-empty description="暂无更多数据" v-show="list.length == 0"></el-empty>
+        </div>
+        <el-pagination background :hide-on-single-page="true" :page-size="limit" layout="prev, pager, next" :total="total" @current-change="onPage"></el-pagination>
+    </div>
+</div>
+{/block}
+{block name="script"}
+<script>
+    function v_setup() {
+        let base = {};
+
+        base.keyword = Vue.ref("{$keyword}");
+        base.onSubmit = () => {
+            location.href = "{:url('recruit/index')}?keyword=" + base.keyword.value;
+        };
+
+        base.loading = Vue.ref(true);
+        base.page = Vue.ref(1);
+        base.total = Vue.ref({$total});
+        base.limit = Vue.ref({$limit});
+        base.list = Vue.reactive([]);
+        base.onPage = num => {
+            base.page.value = num;
+            base.getList();
+        };
+        base.getList = () => {
+            base.loading.value = true;
+            postJson("{:url('recruit/list')}", {page:base.page.value,limit:base.limit.value,keyword:base.keyword.value}).then(function ({data}) {
+                base.loading.value = false;
+                if (data.length === 0) {
+                    base.finished.value = true;
+                } else {
+                    base.list = data;
+                }
+            });
+        };
+        base.getList();
+
+        return base;
+    }
+</script>
+{/block}

+ 283 - 0
app/home/view/register/index.html

@@ -0,0 +1,283 @@
+<!DOCTYPE html>
+<html lang="zh-cn">
+<head>
+    {include file="public/meta_header"/}
+    <style>
+        :root {
+            --el-color-primary: #dd4250;
+        }
+
+        .el-tabs__item {
+            font-size: 18px;
+        }
+
+        .el-form-item {
+            padding-top: 15px;
+        }
+
+        .el-input__inner, .el-form-item__label {
+            height: 40px;
+            line-height: 40px;
+        }
+
+        .el-form-item__label {
+            font-size: 16px;
+        }
+
+        .login-box {
+            width: 472px;
+            margin: 0 auto;
+            padding-bottom: 150px;
+            padding-top: 90px;
+        }
+
+        .login-box .login-logo {
+            margin-bottom: 35px;
+            margin-left: 50px;
+        }
+
+        .login-box .login-logo a {
+            display: inline-block;
+            vertical-align: middle;
+        }
+
+        .login-box .login-logo img {
+            width: 40px;
+        }
+
+        .login-box .login-logo .backhome {
+            background: url("/static/home/images/bg_back.png") no-repeat center;
+            padding: 5px 15px;
+            font-size: 13px;
+            color: #616887;
+            margin-top: 10px;
+        }
+
+        .login-box .login-box-per-body {
+            background-color: #fff;
+            width: 472px;
+            border-radius: 5px;
+            -moz-box-shadow: 2px 2px 10px 0 rgba(0, 0, 0, 0.1);
+            -webkit-box-shadow: 2px 2px 10px 0 rgba(0, 0, 0, 0.1);
+            box-shadow: 2px 2px 10px 0 rgba(0, 0, 0, 0.1);
+        }
+
+        .form-box {
+            width: 332px;
+            margin: 0 auto;
+            padding: 30px 0;
+        }
+
+        .login-box .txt-group {
+            font-size: 12px;
+            width: 332px;
+            margin-bottom: 30px;
+            height: 16px;
+        }
+
+        .login-box .txt-group .txt-r {
+            float: right;
+            color: #9ea1b1;
+            text-decoration: none;
+        }
+
+        .login-box .btn-group {
+            width: 332px;
+            height: 50px;
+            background-color: #dd4250;
+            border-radius: 25px;
+            text-align: center;
+            line-height: 42px;
+            color: #ffffff;
+            margin-bottom: 20px;
+            cursor: pointer;
+            border: none;
+            font-size: 20px;
+            letter-spacing: 10px;
+        }
+
+        .login-box .reg-link-box {
+            width: 332px;
+            color: #9ea1b1;
+            font-size: 14px;
+            margin-bottom: 15px;
+            line-height: 26px;
+            height: 26px;
+            text-align: right;
+        }
+
+        .login-box .reg-link-box .reg-txt {
+            display: inline-block;
+            color: #fe2e5f;
+            text-decoration: none;
+        }
+
+        .bind_code {
+            position: relative;
+        }
+
+        .bind_code .el-button {
+            border: 0;
+            background: none;
+            padding: 0;
+            border-radius: 0;
+        }
+
+        .bind_code .bind_code_gain {
+            position: absolute;
+            right: 10px;
+            font-size: 14px;
+            color: #dd4250;
+            cursor: pointer;
+            padding-left: 10px;
+            border-left: 1px solid #d8d8d8;
+            height: 40px;
+            line-height: 40px;
+        }
+    </style>
+</head>
+<body>
+<div id="app">
+    <div class="login-box">
+        <div class="login-logo">
+            <a href="{:url('/')}"><img src="/static/home/images/img_logo.png" alt="logo"></a>
+            <a href="{:url('/')}" class="backhome">返回首页</a>
+        </div>
+
+        <div class="login-box-per-body">
+            <div class="form-box">
+                <el-form :model="form" label-width="auto" style="max-width: 600px">
+                    <el-form-item label="手机号">
+                        <el-input v-model="form.mobile" placeholder="请输入手机号"></el-input>
+                    </el-form-item>
+                    <el-form-item label="验证码">
+                        <div class="bind_code margin_top">
+                            <el-input
+                                    class="bind_code_input code"
+                                    v-model="form.verify"
+                                    type="text"
+                                    placeholder="请输入验证码"
+                            ></el-input>
+                            <el-button
+                                    @click="bindSendCode"
+                                    class="bind_code_gain"
+                                    :disabled="disabled"
+                            >{{ btnText }}
+                            </el-button>
+                        </div>
+                    </el-form-item>
+                    <el-form-item label="密码" prop="password">
+                        <el-input v-model="form.password" type="password" autocomplete="off"
+                                  placeholder="请输入密码"></el-input>
+                    </el-form-item>
+                    <el-form-item label="确认密码" prop="re_password">
+                        <el-input v-model="form.re_password" type="password" autocomplete="off"
+                                  placeholder="请确认密码"></el-input>
+                    </el-form-item>
+                    <input style="margin-top:20px;" class="btn-group login-account" @click="doRegister" type="button" value="注册">
+                    <div class="reg-link-box">
+                        已有账号?<a class="reg-txt register_user" href="{:url('login/index')}">立刻登录</a>
+                    </div>
+                </el-form>
+            </div>
+        </div>
+    </div>
+    <div class="index_footer">
+        <div class="index_footer_links">
+            <a target="_blank" href="http://hxks.hxrc-app.com">网站首页</a>
+            <a target="_blank" href="http://hxks.hxrc-app.com/content/explain/show/9">单位介绍</a>
+            <a target="_blank" href="http://hxks.hxrc-app.com/content/suggest/index">意见建议</a>
+        </div>
+        <div class="index_footertxt">
+            <p>招考服务热线:0595-82008296</p>
+            <p>地址:福建省泉州市晋江市梅岭街道碧山社区梅岭路273号</p>
+            <p>网站备案:<a href="https://beian.miit.gov.cn/" target="_blank">闽ICP备17033895号</a></p>
+
+        </div>
+    </div>
+</div>
+
+<script>
+    function v_setup() {
+        let base = {};
+
+        base.form = Vue.reactive({mobile: '', verify: '', password: '', re_password: ''});
+
+        base.btnText = Vue.ref('获取验证码');
+        base.disabled = Vue.ref(false);
+        base.bindSendCode = () => {
+            //手机号 为空的话
+            if (base.form.mobile != '') {
+                postJson('/register/sendSms',base.form).then(() => {
+                    ElMessage.success('已发送验证码,请查收')
+                    base.disabled.value = true;
+                    base.btnText.value = "请稍候...";
+                    setTimeout(() => {
+                        base.doLoop(60);
+                    }, 500);
+                });
+            } else {
+                ElMessage.error('请输入手机号')
+            }
+        }
+        base.doLoop = function (seconds) {
+            seconds = seconds ? seconds : 60;
+            base.btnText.value = seconds + "s后获取";
+            let countdown = setInterval(() => {
+                if (seconds > 0) {
+                    base.btnText.value = seconds + "s后获取";
+                    --seconds;
+                } else {
+                    base.btnText.value = "获取验证码";
+                    base.disabled.value = false;
+                    clearInterval(countdown);
+                }
+            }, 1000);
+        }
+
+        base.doRegister = () => {
+            if (base.form.mobile === '') {
+                ElMessage.error('请输入手机号');
+                return false;
+            }
+            if (base.form.verify === '') {
+                ElMessage.error('请输入验证码');
+                return false;
+            }
+            if (base.form.password === '') {
+                ElMessage.error('请输入密码');
+                return false;
+            }
+            if (base.form.password !== base.form.re_password) {
+                ElMessage.error('两次密码不一致');
+                return false;
+            }
+            postJson('/register/doRegister',base.form).then(({data}) => {
+                location.href = "{:url('login/index')}";
+            });
+        }
+
+        return base;
+    }
+</script>
+<script>
+    const vue3 = {
+        setup() {
+            return v_setup();
+        }
+    };
+
+    const app = Vue.createApp(vue3);
+    for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
+        app.component(key, component);
+    }
+    app.use(ElementPlus);
+    const ElMessageBox = ElementPlus.ElMessageBox;
+    const ElMessage = ElementPlus.ElMessage;
+</script>
+{block name="vue"}{/block}
+<script>
+    app.mount('#app');
+</script>
+</body>
+</html>

+ 1 - 1
config/app.php

@@ -13,7 +13,7 @@ return [
     // 自动多应用模式
     'auto_multi_app'   => true,
     // 默认应用
-    'default_app'      => 'mobile',
+    'default_app'      => 'home',
     // 默认时区
     'default_timezone' => 'Asia/Shanghai',
 

+ 4 - 2
extend/chuanglan/Chuanglan.php

@@ -5,6 +5,7 @@
 
 namespace chuanglan;
 
+use app\common\model\SettingModel;
 use think\facade\Log;
 
 class Chuanglan
@@ -18,8 +19,9 @@ class Chuanglan
      */
     public function send($mobile, $smslog_param)
     {
-        $appkey = config('mobile.sms_chuanglan_appkey');
-        $secret = config('mobile.sms_chuanglan_secret');
+
+        $appkey = SettingModel::getConfigValue('sms_chuanglan_appkey');
+        $secret = SettingModel::getConfigValue('sms_chuanglan_secret');
         if (empty($appkey) || empty($secret)) {
             return ['code' => 1, 'msg' => '请绑定模板参数'];
         }

二进制
public/favicon.ico


文件差异内容过多而无法显示
+ 0 - 0
public/static/home/css/element.css


文件差异内容过多而无法显示
+ 0 - 0
public/static/home/css/element_plus_2.8.2.css


二进制
public/static/home/css/fonts/element-icons.woff


+ 26 - 0
public/static/home/css/index.css

@@ -0,0 +1,26 @@
+.carousel-image {width: 100%;height:auto;}
+
+.home_title {text-align: center;position: relative;padding-top: 30px;}
+.home_title .title_ch {font-size: 36px;font-weight: bold;color: #333333;}
+.home_title .title_en {font-size: 18px;color: #999999;margin-top: 5px;}
+
+.famousbox {display: flex;flex-direction: column;justify-content: space-between;background-image: linear-gradient(180deg, #FFFFFF 0%, #F9F9F9 100%);border: 1px solid #DEDEDE;box-shadow: 0 1px 3px 0 rgba(0,0,0,0.03);border-radius: 4px;position: relative;cursor: pointer;width: 100%;padding: 10px 15px;transition: box-shadow 0.5s;-webkit-transition: box-shadow 0.5s;box-sizing: border-box;}
+.famousbox_top, .famousbox_bottom {position: relative;line-height: 25px;z-index: 100;}
+.famousbox_top_title {flex: 1;font-size: 16px;font-weight: bold;color: #333333;}
+.famousbox_middle {display: block;font-size: 12px;color: #999999;padding-bottom: 13px;border-bottom: 1px solid #D9D9D9;}
+.recruit-btn-group {padding-top: 14px;font-size: 13px;margin-right: 15px;position: relative;top: 3px;}
+.notice-recruit {color:#dd4250;}
+.btn {display: inline-block;text-align: center;border-radius: 3px;width: 53px;padding: 0;line-height: 22px;margin-bottom: 5px;margin-right: 5px;font-size: 12px;}
+.btn-primary {background-image: linear-gradient(180deg, #ff6878 1%, #db3947 100%);color: #FFF;border: 1px solid #dd4250;}
+.btn-primary-plain {background-color: #FFF;color: #dd4250;border: 1px solid #dd4250;}
+
+.box_more{width:320px;height:45px;line-height:45px;text-align:center;border-radius:3px;font-size:16px;display:block;margin:30px auto 0;border-color:#bc2c34;background:#bc2c34;color:#fff;}
+
+.title-box {display: flex;padding-left: 10px;padding-right: 30px;height: 30px;}
+.title-box a {font-size: 14px;color: #bc2c34;margin-left: auto;}
+
+.notice {width:100%;}
+.notice li {line-height: 25px;overflow: hidden;display: flex;margin-top:10px;}
+.notice li .el-icon{margin-top:7px;}
+.notice li a {margin-left:10px;width: calc(100% - 10px)}
+.notice li a:hover {text-decoration: underline;color:#dd4250;}

+ 47 - 0
public/static/home/css/style.css

@@ -0,0 +1,47 @@
+body {margin-left: 0px;margin-top: 0px;margin-right: 0px;margin-bottom: 0px;font-size: 14px;color: #666666;font-family: "microsoft yahei","宋体";background-color: #F2F4F8;}
+h1,h2,h3,h4,h5,form,p,ul,input {margin: 0;padding: 0;}
+li {padding: 0;margin: 0;list-style-type: none;}
+:focus {outline: 0}
+.substring {overflow: hidden;white-space: nowrap;text-overflow: ellipsis;}
+.nowrap {white-space: nowrap;}
+.clear {clear: both;height: 0;font-size: 0;line-height: 0;}
+.font_gray3 {color: #333333}
+.font_gray6 {color: #666666}
+.font_gray9 {color: #999999}
+.font_blue {color: #5F8DC9}
+.font_green {color: #61AF00}
+.font_red {color: #CC3300}
+.font_yellow {color: #FF6600}
+u {text-decoration: none;}
+.phcolor {color: #999;}
+.gt_mask {z-index: 1050;}
+a {color: #333;text-decoration: none;}
+
+.index_header {width: 100%;background-color: #F2F4F8;}
+.index_header .header_top {display: flex;justify-content: space-between;align-items: center;margin: 0 auto;width: 1200px;font-size: 12px;height: 70px;}
+.index_header .header_top .itopr {text-align: right;font-size: 18px;text-decoration: none;}
+.index_header .header_top ul.link {float: right;}
+.index_header .header_top ul.link li {float: left;height: 70px;line-height: 70px;padding: 0 5px;}
+.index_header .header_top ul.link li a {color: #333333;vertical-align: top;}
+.index_header .header_top ul.link .link_yellow a {color: #dd4250;}
+.index_header .header_top .itopr .head_min_username {display: inline-block;width: 250px;overflow: hidden;white-space: nowrap;text-overflow: ellipsis;margin-right:10px;}
+.index_header .header_top .itopr li .head_my_msg {color: #666666 !important;margin: 0 3px;position: relative;}
+.index_header .header_top ul.link li span {vertical-align: top;}
+.index_header .header_top ul.link .per {display: flex;align-items: center;}
+.index_header .header_top .itopr .login-text {color: #dd4250;margin-left:5px;}
+.login-text-line {display: inline-block;color: #D9D9D9;margin: 0 20px;}
+
+.index_navlist {z-index: 1000;background-color: #dd4250;width: 100%;height: 60px;margin: 0 auto;}
+.index_navlist_list {margin: 0 auto;width: 1200px;overflow: visible;position: relative;z-index: 1000;text-align: center;font-size: 20px;height: 60px;}
+.index_navlist_list li {cursor: pointer;float: left;min-width: 180px;z-index: 1000;font-size: 20px;height: 60px;line-height: 60px;}
+.index_navlist_list li.select {background-color: #f95f6d;}
+.index_navlist_list li:hover {background-color: #f95f6d;}
+.index_navlist_list li a {display: block;color: white;}
+
+
+.index_footer {width: 100%;background-color: #353535;text-align: center;color: #fff;padding-top: 15px;padding-bottom: 30px;margin-top: 40px;}
+.index_footer a {color:white;}
+.index_footer a:hover {text-decoration: underline;color:#dd4250;}
+.index_footer_links a {display: inline-block;white-space: nowrap;margin-right: 20px;}
+.index_footer_links, .index_footertxt {line-height: 26px;}
+.content-box {margin: 0 auto;width: 1200px;}

二进制
public/static/home/images/123.png


二进制
public/static/home/images/about.jpg


二进制
public/static/home/images/bg_back.png


二进制
public/static/home/images/img_logo.png


二进制
public/static/home/images/index_logo.png


二进制
public/static/home/images/location.png


二进制
public/static/home/images/logo.png


二进制
public/static/home/images/nebanner.jpg


二进制
public/static/home/images/tel.png


文件差异内容过多而无法显示
+ 0 - 0
public/static/home/js/area.js


+ 50 - 0
public/static/home/js/axios_instance.js

@@ -0,0 +1,50 @@
+const axios_instance = axios.create({
+    baseURL: baseUrl + "home",
+    timeout: 30000,
+    headers: {'X-Requested-With':'XMLHttpRequest'},
+    withCredentials: true,
+});
+
+async function postJson(url,data,error) {
+    return  new Promise((resolve, reject) => {
+        axios_instance.post(url, JSON.stringify(data), {
+            headers: { 'Content-Type': 'application/json;charset=UTF-8' }
+        }).then((result) => {
+            const data = result.data;
+            if (data.code === 1) {
+                if (typeof error === 'function') {
+                    error(data);
+                    return false;
+                } else {
+                    ElMessage.error(data.msg);
+                    return false;
+                }
+            } else if (data.code === 401) {
+                location.href = '/home/login/login';
+            }
+            return resolve(data);
+        });
+    });
+}
+
+async function postFile(url,data,error) {
+    return  new Promise((resolve, reject) => {
+        axios_instance.post(url, data, {
+            headers: { 'Content-Type': 'application/form-data' }
+        }).then((result) => {
+            const data = result.data;
+            if (data.code === 1) {
+                if (typeof error === 'function') {
+                    error(data);
+                    return false;
+                } else {
+                    ElMessage.error(data.msg);
+                    return false;
+                }
+            } else if (data.code === 401) {
+                location.href = '/home/login/login';
+            }
+            return resolve(data);
+        });
+    });
+}

文件差异内容过多而无法显示
+ 0 - 0
public/static/home/js/element.js


文件差异内容过多而无法显示
+ 9388 - 0
public/static/home/js/element_plus_2.8.2.js


文件差异内容过多而无法显示
+ 2 - 0
public/static/home/js/element_plus_icons.js


+ 190 - 0
public/static/home/js/element_plus_zhcn.js

@@ -0,0 +1,190 @@
+/*! Element Plus v2.8.7 */
+
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+        typeof define === 'function' && define.amd ? define(factory) :
+            (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.ElementPlusLocaleZhCn = factory());
+})(this, (function () { 'use strict';
+
+    var zhCn = {
+        name: "zh-cn",
+        el: {
+            breadcrumb: {
+                label: "\u9762\u5305\u5C51"
+            },
+            colorpicker: {
+                confirm: "\u786E\u5B9A",
+                clear: "\u6E05\u7A7A",
+                defaultLabel: "\u989C\u8272\u9009\u62E9\u5668",
+                description: "\u5F53\u524D\u989C\u8272 {color}\uFF0C\u6309 Enter \u952E\u9009\u62E9\u65B0\u989C\u8272",
+                alphaLabel: "\u9009\u62E9\u900F\u660E\u5EA6\u7684\u503C"
+            },
+            datepicker: {
+                now: "\u6B64\u523B",
+                today: "\u4ECA\u5929",
+                cancel: "\u53D6\u6D88",
+                clear: "\u6E05\u7A7A",
+                confirm: "\u786E\u5B9A",
+                dateTablePrompt: "\u4F7F\u7528\u65B9\u5411\u952E\u4E0E Enter \u952E\u53EF\u9009\u62E9\u65E5\u671F",
+                monthTablePrompt: "\u4F7F\u7528\u65B9\u5411\u952E\u4E0E Enter \u952E\u53EF\u9009\u62E9\u6708\u4EFD",
+                yearTablePrompt: "\u4F7F\u7528\u65B9\u5411\u952E\u4E0E Enter \u952E\u53EF\u9009\u62E9\u5E74\u4EFD",
+                selectedDate: "\u5DF2\u9009\u65E5\u671F",
+                selectDate: "\u9009\u62E9\u65E5\u671F",
+                selectTime: "\u9009\u62E9\u65F6\u95F4",
+                startDate: "\u5F00\u59CB\u65E5\u671F",
+                startTime: "\u5F00\u59CB\u65F6\u95F4",
+                endDate: "\u7ED3\u675F\u65E5\u671F",
+                endTime: "\u7ED3\u675F\u65F6\u95F4",
+                prevYear: "\u524D\u4E00\u5E74",
+                nextYear: "\u540E\u4E00\u5E74",
+                prevMonth: "\u4E0A\u4E2A\u6708",
+                nextMonth: "\u4E0B\u4E2A\u6708",
+                year: "\u5E74",
+                month1: "1 \u6708",
+                month2: "2 \u6708",
+                month3: "3 \u6708",
+                month4: "4 \u6708",
+                month5: "5 \u6708",
+                month6: "6 \u6708",
+                month7: "7 \u6708",
+                month8: "8 \u6708",
+                month9: "9 \u6708",
+                month10: "10 \u6708",
+                month11: "11 \u6708",
+                month12: "12 \u6708",
+                weeks: {
+                    sun: "\u65E5",
+                    mon: "\u4E00",
+                    tue: "\u4E8C",
+                    wed: "\u4E09",
+                    thu: "\u56DB",
+                    fri: "\u4E94",
+                    sat: "\u516D"
+                },
+                weeksFull: {
+                    sun: "\u661F\u671F\u65E5",
+                    mon: "\u661F\u671F\u4E00",
+                    tue: "\u661F\u671F\u4E8C",
+                    wed: "\u661F\u671F\u4E09",
+                    thu: "\u661F\u671F\u56DB",
+                    fri: "\u661F\u671F\u4E94",
+                    sat: "\u661F\u671F\u516D"
+                },
+                months: {
+                    jan: "\u4E00\u6708",
+                    feb: "\u4E8C\u6708",
+                    mar: "\u4E09\u6708",
+                    apr: "\u56DB\u6708",
+                    may: "\u4E94\u6708",
+                    jun: "\u516D\u6708",
+                    jul: "\u4E03\u6708",
+                    aug: "\u516B\u6708",
+                    sep: "\u4E5D\u6708",
+                    oct: "\u5341\u6708",
+                    nov: "\u5341\u4E00\u6708",
+                    dec: "\u5341\u4E8C\u6708"
+                }
+            },
+            inputNumber: {
+                decrease: "\u51CF\u5C11\u6570\u503C",
+                increase: "\u589E\u52A0\u6570\u503C"
+            },
+            select: {
+                loading: "\u52A0\u8F7D\u4E2D",
+                noMatch: "\u65E0\u5339\u914D\u6570\u636E",
+                noData: "\u65E0\u6570\u636E",
+                placeholder: "\u8BF7\u9009\u62E9"
+            },
+            dropdown: {
+                toggleDropdown: "\u5207\u6362\u4E0B\u62C9\u9009\u9879"
+            },
+            mention: {
+                loading: "\u52A0\u8F7D\u4E2D"
+            },
+            cascader: {
+                noMatch: "\u65E0\u5339\u914D\u6570\u636E",
+                loading: "\u52A0\u8F7D\u4E2D",
+                placeholder: "\u8BF7\u9009\u62E9",
+                noData: "\u6682\u65E0\u6570\u636E"
+            },
+            pagination: {
+                goto: "\u524D\u5F80",
+                pagesize: "\u6761/\u9875",
+                total: "\u5171 {total} \u6761",
+                pageClassifier: "\u9875",
+                page: "\u9875",
+                prev: "\u4E0A\u4E00\u9875",
+                next: "\u4E0B\u4E00\u9875",
+                currentPage: "\u7B2C {pager} \u9875",
+                prevPages: "\u5411\u524D {pager} \u9875",
+                nextPages: "\u5411\u540E {pager} \u9875",
+                deprecationWarning: "\u4F60\u4F7F\u7528\u4E86\u4E00\u4E9B\u5DF2\u88AB\u5E9F\u5F03\u7684\u7528\u6CD5\uFF0C\u8BF7\u53C2\u8003 el-pagination \u7684\u5B98\u65B9\u6587\u6863"
+            },
+            dialog: {
+                close: "\u5173\u95ED\u6B64\u5BF9\u8BDD\u6846"
+            },
+            drawer: {
+                close: "\u5173\u95ED\u6B64\u5BF9\u8BDD\u6846"
+            },
+            messagebox: {
+                title: "\u63D0\u793A",
+                confirm: "\u786E\u5B9A",
+                cancel: "\u53D6\u6D88",
+                error: "\u8F93\u5165\u7684\u6570\u636E\u4E0D\u5408\u6CD5!",
+                close: "\u5173\u95ED\u6B64\u5BF9\u8BDD\u6846"
+            },
+            upload: {
+                deleteTip: "\u6309 delete \u952E\u53EF\u5220\u9664",
+                delete: "\u5220\u9664",
+                preview: "\u67E5\u770B\u56FE\u7247",
+                continue: "\u7EE7\u7EED\u4E0A\u4F20"
+            },
+            slider: {
+                defaultLabel: "\u6ED1\u5757\u4ECB\u4E8E {min} \u81F3 {max}",
+                defaultRangeStartLabel: "\u9009\u62E9\u8D77\u59CB\u503C",
+                defaultRangeEndLabel: "\u9009\u62E9\u7ED3\u675F\u503C"
+            },
+            table: {
+                emptyText: "\u6682\u65E0\u6570\u636E",
+                confirmFilter: "\u7B5B\u9009",
+                resetFilter: "\u91CD\u7F6E",
+                clearFilter: "\u5168\u90E8",
+                sumText: "\u5408\u8BA1"
+            },
+            tour: {
+                next: "\u4E0B\u4E00\u6B65",
+                previous: "\u4E0A\u4E00\u6B65",
+                finish: "\u7ED3\u675F\u5BFC\u89C8"
+            },
+            tree: {
+                emptyText: "\u6682\u65E0\u6570\u636E"
+            },
+            transfer: {
+                noMatch: "\u65E0\u5339\u914D\u6570\u636E",
+                noData: "\u65E0\u6570\u636E",
+                titles: ["\u5217\u8868 1", "\u5217\u8868 2"],
+                filterPlaceholder: "\u8BF7\u8F93\u5165\u641C\u7D22\u5185\u5BB9",
+                noCheckedFormat: "\u5171 {total} \u9879",
+                hasCheckedFormat: "\u5DF2\u9009 {checked}/{total} \u9879"
+            },
+            image: {
+                error: "\u52A0\u8F7D\u5931\u8D25"
+            },
+            pageHeader: {
+                title: "\u8FD4\u56DE"
+            },
+            popconfirm: {
+                confirmButtonText: "\u786E\u5B9A",
+                cancelButtonText: "\u53D6\u6D88"
+            },
+            carousel: {
+                leftArrow: "\u4E0A\u4E00\u5F20\u5E7B\u706F\u7247",
+                rightArrow: "\u4E0B\u4E00\u5F20\u5E7B\u706F\u7247",
+                indicator: "\u5E7B\u706F\u7247\u5207\u6362\u81F3\u7D22\u5F15 {index}"
+            }
+        }
+    };
+
+    return zhCn;
+
+}));

文件差异内容过多而无法显示
+ 0 - 0
public/static/home/js/qs.min.js


文件差异内容过多而无法显示
+ 1 - 0
public/static/home/js/vue.min.js


部分文件因为文件数量过多而无法显示