瀏覽代碼

人才工作登记

linwu 8 月之前
父節點
當前提交
7323b022cb

+ 341 - 0
app/admin/controller/Talent.php

@@ -0,0 +1,341 @@
+<?php
+
+namespace app\admin\controller;
+
+use app\admin\AdminBaseController;
+use app\common\model\TalentUserModel;
+use app\common\model\TalentWorkModel;
+use app\common\validate\TalentUserValidate;
+use think\App;
+use think\exception\ValidateException;
+
+class Talent extends AdminBaseController
+{
+    /**
+     * 用户列表
+     */
+    public function user()
+    {
+        return view('', [
+            'status_list' => TalentUserModel::STATUS,
+        ]);
+    }
+
+    public function listUser()
+    {
+        $map   = $this->dealEqualInput(['status'], $this->dealLikeInput(['name', 'mobile', 'department']));
+        $list  = TalentUserModel::where($map)
+            ->limit(input('limit'))
+            ->page(input('page'))
+            ->append(['status_text'])->select();
+        $count = TalentUserModel::where($map)->count();
+        if ($count == 0) {
+            ajax_return(1, '未查询到数据');
+        }
+        list_return($list, $count);
+    }
+
+    public function delUser()
+    {
+        $id = input('id/d', 0);
+        if (empty($id)) {
+            ajax_return(1, '未查询到数据');
+        }
+
+        $check = TalentWorkModel::where('user_id', $id)->find();
+        if (!empty($check)) {
+            ajax_return(1, '该用户已有人才挂钩工作登记表记录,无法删除');
+        }
+
+        TalentUserModel::destroy($id);
+
+        ajax_return();
+    }
+
+    /**
+     * 编辑用户
+     */
+    public function userForm()
+    {
+        $id   = input('id/d', 0);
+        $info = TalentUserModel::find($id);
+        return view('', [
+            'info'        => $info,
+            'status_list' => TalentUserModel::STATUS,
+        ]);
+    }
+
+    public function editUser()
+    {
+        $data = input('post.');
+        try {
+            validate(TalentUserValidate::class)->check($data);
+        } catch (ValidateException $e) {
+            ajax_return(1, $e->getError());
+        }
+
+        //手机号
+        $check_mobile_where = [['mobile', '=', $data['mobile']]];
+        if (!empty($data['id'])) {
+            $check_mobile_where[] = ['id', '<>', $data['id']];
+        }
+        $check_mobile = TalentUserModel::where($check_mobile_where)->find();
+        if (!empty($check_mobile)) {
+            ajax_return(1, '手机号已存在');
+        }
+
+        //密码
+        if (empty($data['id']) && empty($data['password'])) {
+            ajax_return(1, '请输入一个初始密码');
+        }
+        if (empty($data['password'])) {
+            unset($data['password']);
+        } else {
+            $data['salt']     = rand_str();
+            $data['password'] = md5(md5($data['salt']) . $data['password']);
+        }
+
+        if (empty($data['id'])) {
+            TalentUserModel::create($data);
+        } else {
+            TalentUserModel::update($data, ['id' => $data['id']]);
+        }
+
+        ajax_return();
+    }
+
+    /**
+     * 用户导入
+     */
+    public function importUser()
+    {
+        return view('public/import', [
+            'url'           => url('talent/importUserPost'),
+            'last_table'    => 'lay-talent-user-table',
+            'template_file' => '/static/common/exl/talent_user.xls',
+        ]);
+    }
+
+    /**
+     * 用户导入提交
+     */
+    public function importUserPost()
+    {
+        $file_url = input('file_url/s', "");
+        if (!file_exists($file_url)) {
+            ajax_return(1, '文件不存在');
+        }
+
+        //初始化数据
+        $data = ['name', 'department', 'mobile'];
+        $list = import_exl($file_url, $data, 1);
+        if (empty($list)) {
+            ajax_return(1, '请上传有数据的文件');
+        }
+        $empty_check = [
+            'name'       => '姓名',
+            'department' => '部门',
+            'mobile'     => '手机号',
+        ];
+
+        //获取手机号
+        $mobile_list       = array_column($list, 'mobile');
+        $mobile_check_list = TalentUserModel::where('mobile', 'in', $mobile_list)->column('mobile');
+
+        //错误判断
+        $validate = \think\facade\Validate::rule('mobile', 'mobile');
+        $time     = time();
+        foreach ($list as $k => $v) {
+            foreach ($empty_check as $key => $value) {
+                if (empty($v[$key])) {
+                    return ajax_return(1, '第' . ($k + 2) . '行的' . $value . '不能为空');
+                }
+            }
+            if (!$validate->check($v)) {
+                return ajax_return(1, '第' . ($k + 2) . '行的手机号格式不对');
+            }
+            if (!empty(in_array($v['mobile'], $mobile_check_list))) {
+                return ajax_return(1, '第' . ($k + 2) . '行的手机号已存在');
+            }
+            $list[$k]['salt']        = rand_str();
+            $list[$k]['password']    = md5(md5($list[$k]['salt']) . '123456');
+            $list[$k]['create_time'] = $list[$k]['update_time'] = $time;
+        }
+
+        TalentUserModel::insertAll($list);
+        ajax_return(0);
+    }
+
+    /**
+     * 挂钩工作登记表
+     */
+    public function work()
+    {
+        return view();
+    }
+
+    public function listWork()
+    {
+        $model = new TalentWorkModel();
+        $param = input('param.');
+
+        //姓名
+        if (!empty($param['name'])) {
+            $model = $model->where([
+                ['user_id', 'in', function ($query) use ($param) {
+                    $query->name('talent_user')->field('id')->where('name', 'like', "%{$param['name']}%")->buildSql();
+                }],
+            ]);
+        }
+
+        //部门
+        if (!empty($param['department'])) {
+            $model = $model->where([
+                ['user_id', 'in', function ($query) use ($param) {
+                    $query->name('talent_user')->field('id')->where('department', 'like', "%{$param['department']}%")->buildSql();
+                }],
+            ]);
+        }
+
+        //手机号
+        if (!empty($param['mobile'])) {
+            $model = $model->where([
+                ['user_id', 'in', function ($query) use ($param) {
+                    $query->name('talent_user')->field('id')->where('mobile', 'like', "%{$param['mobile']}%")->buildSql();
+                }],
+            ]);
+        }
+
+        //月份
+        if (!empty($param['month'])) {
+            $model = $model->where('month', $param['month']);
+        }
+
+        $list  = $model->with('user')->limit(input('limit'))->page(input('page'))->order('update_time desc')->select();
+        $count = $model->count();
+        if ($count == 0) {
+            ajax_return(1, '未查询到数据');
+        }
+        list_return($list, $count);
+    }
+
+    public function workDetail()
+    {
+        $id = input('id', 0);
+        if (empty($id)) {
+            return '数据错误,请关闭';
+        }
+
+        $info = TalentWorkModel::with('user')->where('id', $id)->find();
+        if (empty($info)) {
+            return '数据错误,请关闭';
+        }
+
+        return view('', [
+            'info' => $info,
+        ]);
+    }
+
+    /**
+     * 未登记用户
+     */
+    public function unRegister()
+    {
+        $month = date('Y-m');
+        return view('', [
+            'month' => $month,
+        ]);
+    }
+
+    public function unRegisterPost()
+    {
+        $month = input('month', '');
+        if (empty($month)) {
+            ajax_return();
+        }
+
+        $user_ids     = TalentUserModel::where('status', TalentUserModel::STATUS_NORMAL)->column('id');
+        $register_ids = TalentWorkModel::where('month', $month)->column('user_id');
+        $diff_id      = array_diff($user_ids, $register_ids);
+        $user         = TalentUserModel::where('id', 'in', $diff_id)->select();
+
+        $res = [];
+        foreach ($user as $item) {
+            $res[] = $item['name'] . "(" . $item['department'] . ")";
+        }
+
+        ajax_return(0, '', implode(',', $res));
+    }
+
+    /**
+     * 导出挂钩工作登记表
+     */
+    public function exportWork()
+    {
+        $model = new TalentWorkModel();
+        $param = input('param.');
+
+        if (!empty($param['id'])) {
+            $model = $model->where('id', 'in', $param['id']);
+        }
+
+        //姓名
+        if (!empty($param['name'])) {
+            $model = $model->where([
+                ['user_id', 'in', function ($query) use ($param) {
+                    $query->name('talent_user')->field('id')->where('name', 'like', "%{$param['name']}%")->buildSql();
+                }],
+            ]);
+        }
+
+        //部门
+        if (!empty($param['department'])) {
+            $model = $model->where([
+                ['user_id', 'in', function ($query) use ($param) {
+                    $query->name('talent_user')->field('id')->where('department', 'like', "%{$param['department']}%")->buildSql();
+                }],
+            ]);
+        }
+
+        //手机号
+        if (!empty($param['mobile'])) {
+            $model = $model->where([
+                ['user_id', 'in', function ($query) use ($param) {
+                    $query->name('talent_user')->field('id')->where('mobile', 'like', "%{$param['mobile']}%")->buildSql();
+                }],
+            ]);
+        }
+
+        //月份
+        if (!empty($param['month'])) {
+            $model = $model->where('month', $param['month']);
+        }
+
+        $list = $model->with('user')->order('update_time desc')->select();
+        foreach ($list as $v) {
+            $v['user_name']       = $v['user']['name'];
+            $v['user_department'] = $v['user']['department'];
+            $v['user_mobile']     = $v['user']['mobile'];
+            $v['contact_text']    = implode(',', $v['contact']);
+            $v['cate_text']       = implode(',', $v['cate']);
+        }
+
+        $xlsCell = [
+            ['user_name', '姓名'],
+            ['user_department', '部门'],
+            ['user_mobile', '手机号'],
+            ['month', '月份'],
+            ['should_num', '应挂钩人数'],
+            ['new_num', '本月新增人数'],
+            ['unfinished_num', '未完成挂钩人数'],
+            ['reason', '未挂钩联系原因'],
+            ['consult_num', '本月回答咨询次数'],
+            ['contact_text', '联系方式'],
+            ['cate_text', '咨询问题类别'],
+            ['description', '具体问题描述及解决措施描述'],
+            ['assist', '需协调事项说明'],
+            ['update_time', '填表时间'],
+        ];
+        export_exl("挂钩工作登记表", $xlsCell, $list);
+    }
+}

+ 51 - 0
app/admin/view/talent/un_register.html

@@ -0,0 +1,51 @@
+<style>
+</style>
+<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="month" readonly id="month" value="{$month}" placeholder="请选择月份" autocomplete="off" class="layui-input">
+                    </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 name-content">
+                请选择月份后点击搜索按钮
+        </div>
+    </div>
+</div>
+
+<script>
+    layui.use(['index', 'form', 'laydate'], function () {
+        const $ = layui.$;
+        const form = layui.form;
+        const laydate = layui.laydate;
+        form.render();
+
+        laydate.render({
+            elem: '#month'
+            ,type: 'month'
+        });
+
+        form.on('submit({$lay_btn})', function (data) {
+            $.post("{:url('talent/unRegisterPost')}",data.field,function(json){
+                if (json.data != '') {
+                    $('.name-content').text(json.data);
+                } else {
+                    $('.name-content').text('所有人员都已填写');
+                }
+            });
+        });
+
+
+    });
+</script>

+ 153 - 0
app/admin/view/talent/user.html

@@ -0,0 +1,153 @@
+<style>
+
+</style>
+<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">
+                        <input type="text" name="department" 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="mobile" 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>
+            <button class="layui-btn layuiadmin-btn" data-type="import">批量导入</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-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('talent/listUser')}",
+            cols: [
+                [
+                    {field: 'id', title: 'ID' ,width: 100},
+                    {field: 'name', title: '姓名' ,width: 200},
+                    {field: 'department', title: '部门', width: 200},
+                    {field: 'mobile', title: '手机号', width: 200},
+                    {field: 'status_text', title: '状态', width: 200, align: 'center'},
+                    {field: 'create_time', title: '添加时间', width: 200},
+                    {title: '操作', 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('talent/userForm')}",
+                    maxmin: true,
+                    area: ['550px', '550px']
+                });
+                layer.full(index);
+            },
+            import: function() {
+                layer.open({
+                    type: 2,
+                    title: '批量导入',
+                    content: "{:url('talent/importUser')}",
+                    maxmin: true,
+                    area: ['750px', '300px']
+                });
+            },
+        };
+
+        //监听工具条
+        table.on('tool({$lay_table})', function (obj) {
+            const data = obj.data;
+            if (obj.event === 'del') {
+                layer.confirm('确定删除此用户吗?', function (index) {
+                    admin.req({
+                        url: "{:url('talent/delUser')}",
+                        data: {
+                            id: 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('talent/userForm')}?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>

+ 78 - 0
app/admin/view/talent/user_form.html

@@ -0,0 +1,78 @@
+<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="name" value="{:array_get($info,'name')}" 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" name="department" value="{:array_get($info,'department')}" 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" name="mobile" value="{:array_get($info,'mobile')}" lay-verify="required|phone" placeholder="请输入手机号"
+                                       autocomplete="off" class="layui-input">
+                            </div>
+                        </div>
+                        <div class="layui-form-item">
+                            <label class="layui-form-label">密码</label>
+                            <div class="layui-input-block">
+                                <input type="text" name="password" placeholder="请输入登录密码,不修改留空" autocomplete="off" class="layui-input">
+                            </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" lay-filter="type" value="{$key}" title="{$v}" {eq name=":array_get($info,'status',1)" value="$key" }checked{/eq}>
+                                {/volist}
+                            </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>
+
+<script>
+    layui.use(['index', 'form', 'upload', 'laydate'], function () {
+        const admin = layui.admin;
+        const form = layui.form;
+        form.render();
+
+        form.on('submit({$lay_btn})', function (obj) {
+            const index = parent.layer.getFrameIndex(window.name);
+            admin.req({
+                url: "{:url('talent/editUser')}",
+                type: 'post',
+                data: obj.field,
+                done: function (res) {
+                    layer.msg("提交成功", {
+                        icon: 1
+                    });
+                    parent.layui.table.reload('lay-talent-user-table'); //重载表格
+                    parent.layer.close(index);
+                }
+            });
+        });
+    });
+</script>

+ 166 - 0
app/admin/view/talent/work.html

@@ -0,0 +1,166 @@
+<style>
+    .layui-table-cell {height:45px;line-height:unset;white-space:normal;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;}
+    .user-box {display:flex;}
+    .user-box .content .name {font-size:18px;color:#000;}
+    .user-box .content .desc {font-size:14px;margin-top:5px;}
+</style>
+<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">
+                        <input type="text" name="department" 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="mobile" 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="month" readonly id="month" placeholder="请选择月份" autocomplete="off" class="layui-input">
+                    </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="unregister">未登记用户</button>
+            <button class="layui-btn layuiadmin-btn" data-type="export">导出</button>
+        </div>
+
+        <div class="layui-card-body">
+            <table id="{$lay_table}" lay-filter="{$lay_table}"></table>
+            <script type="text/html" id="userTpl">
+                <div class="user-box">
+                    <div class="content">
+                        <div class="name">{{d.user.name}}</div>
+                        <div class="desc">部门:{{d.user.department}},手机号:{{d.user.mobile}}</div>
+                    </div>
+                </div>
+            </script>
+            <script type="text/html" id="setTpl">
+                <a class="layui-btn layui-btn-normal layui-btn-xs" lay-event="detail"><i class="layui-icon layui-icon-read"></i>详情</a>
+            </script>
+        </div>
+    </div>
+</div>
+
+<script>
+    layui.use(['index', 'form', 'table', 'laydate'], function () {
+        const $ = layui.$;
+        const form = layui.form;
+        const table = layui.table;
+        const laydate = layui.laydate;
+        let search_param = {};
+        form.render();
+
+        laydate.render({
+            elem: '#month'
+            ,type: 'month'
+        });
+
+        table.render({
+            elem: '#{$lay_table}',
+            url: "{:url('talent/listWork')}",
+            cols: [
+                [
+                    {field: 'user', title: '用户信息' ,width: 350, templet: '#userTpl'},
+                    {field: 'month', title: '月份', width: 100},
+                    {field: 'should_num', title: '应挂钩人数', width: 100},
+                    {field: 'new_num', title: '本月新增人数', width: 120},
+                    {field: 'unfinished_num', title: '未完成挂钩人数', width: 150},
+                    {field: 'consult_num', title: '本月回答咨询次数', width: 150},
+                    {field: 'contact', title: '联系方式', width: 150},
+                    {field: 'cate', title: '咨询问题类别', width: 200},
+                    {field: 'update_time', title: '填表时间', width: 160},
+                    {title: '操作', align: 'center', fixed: 'right', toolbar: '#setTpl'}
+                ]
+            ],
+            page: true,
+            limit: 50,
+            cellMinWidth: 150,
+            text: '对不起,加载出现异常!'
+        });
+
+        form.on('submit({$lay_btn})', function (data) {
+            search_param = data.field;
+            table.reload('{$lay_table}', {
+                where: data.field,
+                page: {
+                    curr: 1
+                }
+            });
+        });
+
+        //事件
+        const active = {
+            unregister: function () {
+                layer.open({
+                    type: 2,
+                    title: '未登记用户',
+                    content: "{:url('talent/unRegister')}",
+                    maxmin: true,
+                    area: ['800px', '550px']
+                });
+            },
+            export: function() {
+                const check_data = table.checkStatus('{$lay_table}').data;
+                const url = "{:url('talent/exportWork')}";
+                if (check_data.length === 0) {
+                    let param = '';
+                    for (let item in search_param) {
+                        param += '&' + item + '=' + search_param[item];
+                    }
+                    if (param === '') {
+                        window.open(url);
+                    } else {
+                        window.open(url + '?' + param.slice(1));
+                    }
+                } else {
+                    let id_arr = [];
+                    for (let i = 0; i < check_data.length; i++) {
+                        id_arr.push(check_data[i].id);
+                    }
+
+                    window.open(url + '?id=' + id_arr.join());
+                }
+            },
+        };
+
+        //监听工具条
+        table.on('tool({$lay_table})', function (obj) {
+            const data = obj.data;
+            if (obj.event === 'detail') {
+                const index = layer.open({
+                    type: 2,
+                    title: '详情',
+                    content: "{:url('talent/workDetail')}?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>

+ 61 - 0
app/admin/view/talent/work_detail.html

@@ -0,0 +1,61 @@
+<style>
+    .title {font-size:16px;color:black;}
+</style>
+<div class="layui-fluid">
+    <table class="layui-table">
+        <colgroup>
+            <col width="150" />
+            <col width="400" />
+            <col width="150" />
+            <col width="400" />
+        </colgroup>
+        <tbody>
+        <tr>
+            <td class="title">姓名</td>
+            <td>{$info.user.name}</td>
+            <td class="title">部门</td>
+            <td>{$info.user.department}</td>
+        </tr>
+        <tr>
+            <td class="title">电话</td>
+            <td>{$info.user.mobile}</td>
+            <td class="title">月份</td>
+            <td>{$info.month}</td>
+        </tr>
+        <tr>
+            <td class="title">应挂钩人数</td>
+            <td>{$info.should_num}</td>
+            <td class="title">本月新增人数</td>
+            <td>{$info.new_num}</td>
+        </tr>
+        <tr>
+            <td class="title">未完成挂钩人数</td>
+            <td>{$info.unfinished_num}</td>
+            <td class="title">未挂钩联系原因</td>
+            <td>{$info.reason}</td>
+        </tr>
+        <tr>
+            <td class="title">本月回答咨询次数</td>
+            <td>{$info.consult_num}</td>
+            <td></td>
+            <td></td>
+        </tr>
+        <tr>
+            <td class="title">联系方式</td>
+            <td>{:implode(",",$info['contact'])}</td>
+            <td class="title">咨询问题类别</td>
+            <td>{:implode(",",$info['cate'])}</td>
+        </tr>
+        <tr>
+            <td class="title">需协调事项说明</td>
+            <td>{$info.assist}</td>
+            <td class="title">填写时间</td>
+            <td>{$info.update_time}</td>
+        </tr>
+        </tbody>
+    </table>
+</div>
+
+<script>
+
+</script>

+ 20 - 0
app/common/model/TalentUserModel.php

@@ -0,0 +1,20 @@
+<?php
+
+namespace app\common\model;
+
+class TalentUserModel extends BaseModel
+{
+    // 设置表名
+    protected $name = 'talent_user';
+
+    // 常量
+    const STATUS = [1 => '正常', 2 => '禁用'];
+
+    const STATUS_NORMAL  = 1;
+    const STATUS_DISABLE = 2;
+
+    public function getStatusTextAttr($value, $data)
+    {
+        return self::STATUS[$data['status']];
+    }
+}

+ 23 - 0
app/common/model/TalentWorkModel.php

@@ -0,0 +1,23 @@
+<?php
+
+namespace app\common\model;
+
+class TalentWorkModel extends BaseModel
+{
+    // 设置表名
+    protected $name = 'talent_work';
+
+    // 设置字段自动转换类型
+    protected $type = [
+        'contact' => 'array',
+        'cate'    => 'array',
+    ];
+
+    const CONTACT = ['来电咨询', '微信咨询', '人才服务专员上门拜访', '人才现场咨询', '其他'];
+    const CATE    = ['人才津贴', '人才认定', '交通补贴', '购房补贴', '人才活动', '子女就学', '配偶就业', '其他'];
+
+    public function user()
+    {
+        return $this->hasOne(TalentUserModel::class, 'id', 'user_id');
+    }
+}

+ 22 - 0
app/common/validate/TalentUserValidate.php

@@ -0,0 +1,22 @@
+<?php
+
+namespace app\common\validate;
+
+use think\Validate;
+
+class TalentUserValidate extends Validate
+{
+    protected $rule = [
+        'name'       => 'require',
+        'mobile'     => 'require|mobile',
+        'department' => 'require',
+    ];
+
+    protected $message = [
+        'name'           => '姓名不能为空',
+        'mobile.require' => '手机号不能为空',
+        'mobile.mobile'  => '手机号格式不正确',
+        'department'     => '部门不能为空',
+    ];
+
+}

+ 1 - 0
app/talent/.htaccess

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

+ 26 - 0
app/talent/TalentBaseController.php

@@ -0,0 +1,26 @@
+<?php
+
+namespace app\talent;
+
+use app\BaseController;
+use app\common\model\TalentUserModel;
+
+/**
+ * 控制器基础类
+ */
+abstract class TalentBaseController extends BaseController
+{
+    protected $user = null;
+
+    // 初始化
+    protected function initialize()
+    {
+        $user_id    = get_user_id();
+        $this->user = TalentUserModel::find($user_id);
+        if ($this->user->status == TalentUserModel::STATUS_DISABLE) {
+            session('mobile.user.id', null);
+            jump('该账号已被禁用,请联系管理员', url('login/login'));
+        }
+    }
+
+}

+ 39 - 0
app/talent/common.php

@@ -0,0 +1,39 @@
+<?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_id()
+{
+    $sessionUserId = session('talent.user.id');
+    if (empty($sessionUserId)) {
+        session('back_url',request()->url());
+        $response = redirect('/talent/login/login');
+        throw new \think\exception\HttpResponseException($response);
+    }
+
+    return $sessionUserId;
+}
+

+ 18 - 0
app/talent/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',
+        '__MIMAGES__'       => '/static/mobile/images',
+        '__MCSS__'          => '/static/mobile/css',
+        '__MJS__'           => '/static/mobile/js',
+        '__COMPONENTS__'    => '/static/mobile/js/components',
+    ],
+];

+ 35 - 0
app/talent/controller/Index.php

@@ -0,0 +1,35 @@
+<?php
+
+namespace app\talent\controller;
+
+use app\common\model\TalentWorkModel;
+use app\talent\TalentBaseController;
+
+class Index extends TalentBaseController
+{
+    public function index()
+    {
+        $month       = date('Y-m');
+        $is_register = 'false';
+        $work_check  = TalentWorkModel::where('month', $month)->where('user_id', $this->user->id)->find();
+        if (!empty($work_check)) {
+            $is_register = 'true';
+        }
+
+        return view('', [
+            'is_register' => $is_register,
+            'month'       => $month,
+        ]);
+    }
+
+    public function listWork()
+    {
+        $list = TalentWorkModel::where('user_id', $this->user->id)
+            ->limit(input('limit', 10))
+            ->order('update_time', 'desc')
+            ->page(input('page', 1))
+            ->select();
+
+        ajax_success($list);
+    }
+}

+ 35 - 0
app/talent/controller/Login.php

@@ -0,0 +1,35 @@
+<?php
+
+namespace app\talent\controller;
+
+use app\common\model\TalentUserModel;
+
+class Login
+{
+    public function login()
+    {
+        return view();
+    }
+
+    public function doLogin()
+    {
+        $param = input('post.');
+        if (empty($param['mobile']) || empty($param['password'])) {
+            ajax_return(1, '请输入手机号或密码');
+        }
+
+        $user = TalentUserModel::where('mobile', $param['mobile'])->find();
+        if (empty($user)) {
+            ajax_return(1, '手机号不存在');
+        }
+        if ($user['status'] == TalentUserModel::STATUS_DISABLE) {
+            ajax_return(1, '该账号已被禁用,请联系管理员');
+        }
+        if ($user['password'] != md5(md5($user['salt']) . $param['password'])) {
+            ajax_return(1, '密码错误');
+        }
+
+        session('talent.user.id', $user['id']);
+        ajax_return();
+    }
+}

+ 33 - 0
app/talent/controller/My.php

@@ -0,0 +1,33 @@
+<?php
+
+namespace app\talent\controller;
+
+use app\talent\TalentBaseController;
+
+class My extends TalentBaseController
+{
+    public function password()
+    {
+        return view();
+    }
+
+    public function passwordPost()
+    {
+        $data = input('post.');
+
+        if ($this->user['password'] != md5(md5($this->user['salt']) . $data['old_password'])) {
+            ajax_return(1, '原密码错误');
+        }
+        if ($data['new_password'] != $data['confirm_password']) {
+            ajax_return(1, '新密码和确认密码不一致');
+        }
+
+        $this->user->salt     = rand_str();
+        $this->user->password = md5(md5($this->user->salt) . $data['new_password']);
+        $this->user->save();
+
+        session('talent.user.id', null);
+
+        ajax_return();
+    }
+}

+ 113 - 0
app/talent/controller/Work.php

@@ -0,0 +1,113 @@
+<?php
+
+namespace app\talent\controller;
+
+use app\common\model\TalentWorkModel;
+use app\talent\TalentBaseController;
+
+class Work extends TalentBaseController
+{
+    /**
+     * 新增
+     */
+    public function add()
+    {
+        return view('', [
+            'contact_list' => json_encode(TalentWorkModel::CONTACT),
+            'cate_list'    => json_encode(TalentWorkModel::CATE),
+        ]);
+    }
+
+    public function addPost()
+    {
+        $month      = date('Y-m');
+        $work_check = TalentWorkModel::where('month', $month)->where('user_id', $this->user->id)->find();
+        if (!empty($work_check)) {
+            ajax_return(1, '该月已填写,请勿重复填写!');
+        }
+
+        $post            = input('post.');
+        $post['user_id'] = $this->user->id;
+        $post['month']   = $month;
+
+        TalentWorkModel::create($post);
+
+        ajax_return();
+    }
+
+    /**
+     * 编辑
+     */
+    public function edit()
+    {
+        $id = input('id', 0);
+        if (empty($id)) {
+            jump('该信息不存在');
+        }
+
+        $info = TalentWorkModel::where('user_id', $this->user->id)->where('id', $id)->find();
+        if (empty($info)) {
+            jump('该信息不存在');
+        }
+
+        return view('', [
+            'info'         => $info,
+            'contact_list' => json_encode(TalentWorkModel::CONTACT),
+            'cate_list'    => json_encode(TalentWorkModel::CATE),
+        ]);
+    }
+
+    public function editPost()
+    {
+        $post = input('post.');
+        unset($post['create_time'], $post['update_time']);
+        TalentWorkModel::update($post);
+
+        ajax_return();
+    }
+
+    /**
+     * 复制
+     */
+    public function copy()
+    {
+        $id = input('id', 0);
+        if (empty($id)) {
+            jump('该信息不存在');
+        }
+
+        $info = TalentWorkModel::where('user_id', $this->user->id)->where('id', $id)->find();
+        if (empty($info)) {
+            jump('该信息不存在');
+        }
+
+        $month      = date('Y-m');
+        $work_check = TalentWorkModel::where('month', $month)->where('user_id', $this->user->id)->find();
+        if (!empty($work_check)) {
+            jump('该月已填写,请勿重复填写!');
+        }
+
+        return view('', [
+            'info'         => $info,
+            'contact_list' => json_encode(TalentWorkModel::CONTACT),
+            'cate_list'    => json_encode(TalentWorkModel::CATE),
+        ]);
+    }
+
+    public function copyPost()
+    {
+        $month      = date('Y-m');
+        $work_check = TalentWorkModel::where('month', $month)->where('user_id', $this->user->id)->find();
+        if (!empty($work_check)) {
+            ajax_return(1, '该月已填写,请勿重复填写!');
+        }
+
+        $post = input('post.');
+        unset($post['id'], $post['create_time'], $post['update_time']);
+        $post['month'] = $month;
+
+        TalentWorkModel::create($post);
+
+        ajax_return();
+    }
+}

+ 17 - 0
app/talent/event.php

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

+ 10 - 0
app/talent/middleware.php

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

+ 9 - 0
app/talent/provider.php

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

+ 118 - 0
app/talent/view/index/index.html

@@ -0,0 +1,118 @@
+{extend name="public/base"/}
+{block name="css"}
+<style>
+    .work-list {width:90%;margin:10px auto;padding:10px 20px;box-sizing:border-box;border: 1px solid #eee;border-radius:10px;box-shadow: 0 0 10px #ccc;}
+    .work-list .work-header {border-bottom:1px solid #eee;padding-bottom:5px;}
+    .work-list .work-content {border-bottom:1px solid #eee;padding-bottom:5px;}
+    .work-list .work-content .content-item {margin-top:4px;font-size:14px;color:#666;display:flex;}
+    .work-list .work-content .content-item .title {width:120px;}
+    .work-list .work-content .content-item .num {color: var(--red);font-size:16px;}
+    .work-tool {display:flex;align-items:center;justify-content:flex-end;padding-top:5px;}
+    .work-tool .tool-btn {width:60px;height:25px;line-height:25px;border:1px solid var(--blue);text-align:center;margin-left:5px;border-radius:100px;font-size:14px;box-sizing:border-box;color:var(--blue);}
+    .add {width:50px;height:50px;position:fixed;bottom:50px;right:20px;}
+</style>
+{/block}
+{block name="body"}
+<van-nav-bar
+        class="nav-theme"
+        :fixed="true"
+        :placeholder="true"
+        right-text="修改密码"
+        @click-right="toPassword"
+>
+    <template #title>
+        <span class="text-white">工作登记记录</span>
+    </template>
+</van-nav-bar>
+
+<van-pull-refresh v-model="refreshing" @refresh="onRefresh">
+    <van-list
+            v-model:loading="loading"
+            :finished="finished"
+            finished-text="没有更多了"
+            @load="onList"
+    >
+        <div class="work-list" v-for="item in list">
+            <div class="work-header">{{item.month}}</div>
+            <div class="work-content">
+                <div class="content-item">
+                    <div class="title">应挂钩人数</div>
+                    <div class="num">{{item.should_num}}</div>
+                </div>
+                <div class="content-item">
+                    <div class="title">本月新增人数</div>
+                    <div class="num">{{item.new_num}}</div>
+                </div>
+                <div class="content-item">
+                    <div class="title">未完成挂钩人数</div>
+                    <div class="num">{{item.unfinished_num}}</div>
+                </div>
+            </div>
+            <div class="work-tool">
+                <div class="tool-btn" v-if="item.month == month" @click="toEdit(item.id)">编辑</div>
+                <div class="tool-btn" v-if="!is_register" @click="toCopy(item.id)">复制</div>
+            </div>
+        </div>
+    </van-list>
+</van-pull-refresh>
+<img src="__MIMAGES__/talent_work_add.png" class="add" @click="toAdd" v-if="!is_register"/>
+{/block}
+{block name="script"}
+<script>
+    function v_setup() {
+        let base = {};
+
+        base.is_register = Vue.ref({$is_register});
+        base.month = Vue.ref("{$month}");
+
+        base.toPassword = () => {
+            location.href = "{:url('my/password')}";
+        };
+
+        base.toAdd = () => {
+            location.href = "{:url('work/add')}";
+        };
+
+        base.toEdit = id => {
+            location.href = "{:url('work/edit')}?id=" + id;
+        };
+
+        base.toCopy = id => {
+            location.href = "{:url('work/copy')}?id=" + id;
+        };
+
+        //列表
+        base.page = Vue.ref(1);
+        base.loading = Vue.ref(false);
+        base.finished = Vue.ref(false);
+        base.refreshing = Vue.ref(false);
+        base.list = Vue.reactive([]);
+        base.onList = () => {
+            let param = {};
+            param.page = base.page.value;
+            base.page.value++;
+
+            postJson("{:url('index/listWork')}", param).then( ({data}) => {
+                base.loading.value = false;
+                if (base.refreshing.value) base.refreshing.value = false;
+                if (data.length === 0) {
+                    base.finished.value = true;
+                } else {
+                    base.list.push(...data);
+                }
+            });
+
+        };
+        base.onRefresh = () => {
+            base.list = Vue.reactive([]);
+            base.page.value = 1;
+            base.loading.value = true;
+            base.finished.value = false;
+
+            base.onList();
+        };
+
+        return base;
+    }
+</script>
+{/block}

+ 101 - 0
app/talent/view/login/login.html

@@ -0,0 +1,101 @@
+{extend name="public/base"/}
+{block name="css"}
+<style>
+    .content {
+        height: 100vh;
+        background-color: aquamarine;
+        background: url("__MIMAGES__/bg_login.jpg") no-repeat;
+        background-size: cover;
+    }
+
+    .topBox {
+        font-size: 17px;
+        color: #fff;
+        padding: 10px 25px;
+    }
+
+    h3 {
+        margin-bottom: 5px;
+    }
+
+    .inputBox {
+        position: fixed;
+        bottom: 0;
+        left: 0;
+        width: 100%;
+        height: 85vh;
+        background-color: #fff;
+        border-top-left-radius: 20px;
+        border-top-right-radius: 20px;
+        padding: 30px;
+        box-sizing: border-box;
+    }
+
+    .ipt {
+        margin-bottom: 25px;
+    }
+
+    .ipt h4 {
+        margin-bottom: 10px;
+        font-size: 18px;
+        color: #333;
+    }
+
+    .ipt input {
+        border:none;
+        border-bottom:1px solid #dedede;
+        width:100%;
+        padding-bottom: 10px;
+        font-size: 14px;
+    }
+
+    .loginBtn {
+        line-height: 43px;
+        text-align: center;
+        background: linear-gradient(to right, rgb(86, 104, 214), rgb(86, 104, 214));
+        border-radius: 20px;
+        color: #fff;
+        margin-top: 25px;
+        width:100%;
+        border:none;
+        display:block;
+    }
+
+</style>
+{/block}
+{block name="body"}
+<div class="content">
+    <div class="topBox">
+        <h3>晋江人资</h3>
+        <h3>人才挂钩工作登记管理系统</h3>
+    </div>
+    <div class="inputBox">
+        <div class="ipt">
+            <h4>手机号</h4>
+            <input type="mobile" v-model="form.mobile" placeholder="请输入手机号码" />
+        </div>
+        <div class="ipt">
+            <h4>密码</h4>
+            <input type="password" v-model="form.password" placeholder="请输入密码" />
+        </div>
+        <button class="loginBtn" @click="onLogin">登录</button>
+    </div>
+</div>
+{/block}
+{block name="script"}
+<script>
+    function v_setup() {
+        let base = {};
+
+        base.form = Vue.reactive({});
+
+        base.onLogin = () => {
+            postJson('/login/doLogin', base.form).then(({data, code}) => {
+                location.href = "{:url('index/index')}";
+            })
+        };
+
+        return base;
+    }
+</script>
+{/block}

+ 81 - 0
app/talent/view/my/password.html

@@ -0,0 +1,81 @@
+{extend name="public/base"/}
+{block name="css"}
+
+{/block}
+{block name="body"}
+<van-nav-bar
+        class="nav-theme"
+        :fixed="true"
+        :placeholder="true"
+        left-text="返回"
+        left-arrow
+        @click-left="onBack"
+>
+    <template #title>
+        <span class="text-white">修改密码</span>
+    </template>
+</van-nav-bar>
+<van-form @submit="onSubmit">
+    <van-cell-group>
+        <van-field
+                v-model="form.old_password"
+                required
+                type="password"
+                label="原始密码"
+                placeholder="请输入原始密码"
+                :rules="[{ required: true, message: '请输入原始密码' }]"
+        ></van-field>
+        <van-field
+                v-model="form.new_password"
+                required
+                type="password"
+                label="新密码"
+                placeholder="请输入新密码"
+                :rules="[{ required: true, message: '请输入新密码' }]"
+        ></van-field>
+        <van-field
+                v-model="form.confirm_password"
+                required
+                type="password"
+                label="确认密码"
+                placeholder="请输入确认密码"
+                :rules="[{ required: true, message: '请输入确认密码' }]"
+        ></van-field>
+    </van-cell-group>
+    <div style="margin: 16px;">
+        <van-button round block type="primary" native-type="submit">
+            提交
+        </van-button>
+    </div>
+</van-form>
+{/block}
+{block name="script"}
+<script>
+    function v_setup() {
+        let base = {};
+
+        base.form = Vue.reactive({
+            old_password: '',
+            new_password: '',
+            confirm_password: '',
+        });
+        base.onBack = () => {
+            location.href = "{:url('index/index')}";
+        };
+
+        //表单提交
+        base.onSubmit = () => {
+            postJson('/my/passwordPost',base.form).then(() => {
+                vant.showDialog({
+                    title: '提示',
+                    message: '修改成功',
+                }).then(() => {
+                    location.href = "{:url('index/index')}";
+                });
+            });
+        };
+
+        return base;
+    }
+</script>
+{/block}

+ 29 - 0
app/talent/view/public/base.html

@@ -0,0 +1,29 @@
+<!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">
+    {block name="body"}{/block}
+</div>
+{block name="script"}{/block}
+<script>
+    const vue3 = {
+        setup() {
+            return v_setup();
+        }
+    };
+
+    const app = Vue.createApp(vue3)
+        .use(vant)
+        .use(vant.Lazyload);
+</script>
+{block name="vue"}{/block}
+<script>
+    app.mount('#app');
+</script>
+</body>
+</html>

+ 64 - 0
app/talent/view/public/jump.html

@@ -0,0 +1,64 @@
+{extend name="public/base"/}
+{block name="css"}
+<style>
+    .icon{
+        text-align:center;
+        margin-top:50px;
+    }
+
+    .msg{
+        text-align:center;
+        margin:0;
+        padding:0;
+        font-size:24px;
+    }
+
+    .tips{
+        text-align:center;
+        margin-top:50px;
+        font-size:14px;
+        color:#aaa;
+    }
+
+    .tips .num{
+        color:#FF589B;
+    }
+</style>
+{/block}
+{block name="body"}
+<van-nav-bar
+        class="nav-theme"
+        :fixed="true"
+        :placeholder="true"
+>
+    <template #title>
+        <span class="text-white">提示</span>
+    </template>
+</van-nav-bar>
+<div style="width:100%;height:46px;"></div>
+<div class="icon">
+    <van-icon name="warning-o" size="160" color="#0081ff"></van-icon>
+</div>
+<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/talent/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( ({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>

+ 15 - 0
app/talent/view/public/meta_header.html

@@ -0,0 +1,15 @@
+<meta charset="utf-8">
+<title>晋江人资</title>
+<meta name="renderer" content="webkit">
+<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover"/>
+<meta name="apple-mobile-web-app-capable" content="yes" />
+<meta name="apple-mobile-web-app-status-bar-style" content="black" />
+<link rel="stylesheet" href="__MCSS__/vant4.6.min.css">
+<link rel="stylesheet" href="__MCSS__/style.css">
+<script src="__COMMON_JS__/vue3.3.4.min.js"></script>
+<script src="__MJS__/vant4.6.min.js"></script>
+<script src="__COMMON_JS__/axios1.4.min.js"></script>
+<script>
+    const baseUrl = "{:url('/')}";
+</script>
+<script src="__MJS__/axios_talent.js"></script>

+ 202 - 0
app/talent/view/work/add.html

@@ -0,0 +1,202 @@
+{extend name="public/base"/}
+{block name="css"}
+<style>
+    .van-row {width:100%;}
+    .btn_search_item{background:#f2f6ff;display:inline-block;border-radius:5px;line-height:35px;text-align:center;position:relative;font-size:13px;width:100%;color:#666;margin-bottom:10px;}
+    .btn_search_item.active{color:var(--pink);background:#fff7fa;}
+    .btn_search_icon{position:absolute;bottom:-3px;right:-3px;font-size:30px;}
+</style>
+{/block}
+{block name="body"}
+<van-nav-bar
+        class="nav-theme"
+        :fixed="true"
+        :placeholder="true"
+        left-text="返回"
+        left-arrow
+        @click-left="onBack"
+>
+    <template #title>
+        <span class="text-white">添加记录</span>
+    </template>
+</van-nav-bar>
+<van-form @submit="onSubmit">
+    <van-cell-group>
+        <van-field
+                v-model="form.should_num"
+                required
+                type="number"
+                label="应挂钩人数"
+                placeholder="请输入应挂钩人数"
+                :rules="[{ required: true, message: '请输入应挂钩人数' }]"
+        ></van-field>
+        <van-field
+                v-model="form.new_num"
+                required
+                type="number"
+                label="本月新增人数"
+                placeholder="请输入本月新增人数"
+                :rules="[{ required: true, message: '请输入本月新增人数' }]"
+        ></van-field>
+        <van-field
+                v-model="form.unfinished_num"
+                required
+                type="number"
+                label="未完成挂钩人数"
+                placeholder="请输入未完成挂钩人数"
+                :rules="[{ required: true, message: '请输入未完成挂钩人数' }]"
+        ></van-field>
+        <van-field
+                v-model="form.reason"
+                rows="2"
+                required
+                autosize
+                label="未挂钩联系原因"
+                type="textarea"
+                placeholder="若全部联系请填无"
+                :rules="[{ required: true, message: '请输入未挂钩联系原因' }]"
+        ></van-field>
+        <van-field
+                v-model="form.consult_num"
+                required
+                type="number"
+                label="本月回答咨询次数"
+                placeholder="请输入本月回答咨询次数"
+                :rules="[{ required: true, message: '请输入本月回答咨询次数' }]"
+        ></van-field>
+
+        <!--联系方式-->
+        <van-field name="form.contact" label="联系方式">
+            <template #input>
+                <van-row :gutter="10">
+                    <van-col span="8" v-for="(item) in contact_list">
+                        <div :class="{btn_search_item:true,active: in_array(item,form.contact)}"
+                             @click="selectContact(item)">
+                            {{item}}
+                            <van-icon v-if="in_array(item,form.contact)"
+                                      class="iconfont icon-gouxuan-youxiajiaogouxuan btn_search_icon text-blue"></van-icon>
+                        </div>
+                    </van-col>
+                </van-row>
+            </template>
+        </van-field>
+
+        <!--联系方式-->
+        <van-field name="form.cate" label="咨询问题类别">
+            <template #input>
+                <van-row :gutter="10">
+                    <van-col span="8" v-for="(item) in cate_list">
+                        <div :class="{btn_search_item:true,active: in_array(item,form.cate)}"
+                             @click="selectCate(item)">
+                            {{item}}
+                            <van-icon v-if="in_array(item,form.cate)"
+                                      class="iconfont icon-gouxuan-youxiajiaogouxuan btn_search_icon text-blue"></van-icon>
+                        </div>
+                    </van-col>
+                </van-row>
+            </template>
+        </van-field>
+
+        <van-field
+                v-model="form.description"
+                rows="2"
+                required
+                autosize
+                label="具体问题描述及解决措施描述"
+                type="textarea"
+                placeholder="若全部联系请填无"
+                :rules="[{ required: true, message: '请输入具体问题描述及解决措施描述' }]"
+        ></van-field>
+        <van-field
+                v-model="form.assist"
+                rows="2"
+                required
+                autosize
+                label="需协调事项说明"
+                type="textarea"
+                placeholder="若全部联系请填无"
+                :rules="[{ required: true, message: '请输入需协调事项说明' }]"
+        ></van-field>
+    </van-cell-group>
+    <div style="margin: 16px;">
+        <van-button round block type="primary" native-type="submit">
+            提交
+        </van-button>
+    </div>
+</van-form>
+{/block}
+{block name="script"}
+<script>
+    function v_setup() {
+        let base = {};
+
+        base.form = Vue.reactive({
+            should_num: 0,
+            new_num: 0,
+            unfinished_num: 0,
+            reason: '',
+            consult_num: 0,
+            contact: [],
+            cate: [],
+            description: '',
+            assist: '',
+        });
+        base.onBack = () => {
+            location.href = "{:url('index/index')}";
+        };
+
+        //公共方法
+        base.in_array = (search,array) => {
+            for(var i in array){
+                if(array[i] == search){
+                    return true;
+                }
+            }
+            return false;
+        };
+        base.removeByVal = (arrylist , val) => {
+            for(var i = 0; i < arrylist .length; i++) {
+                if(arrylist[i] == val) {
+                    arrylist .splice(i, 1);
+                    break;
+                }
+            }
+        };
+
+        //联系方式
+        base.contact_list = Vue.reactive({$contact_list});
+        base.selectContact = value => {
+            if (base.in_array(value,base.form.contact)) {
+                base.removeByVal(base.form.contact,value);
+            } else {
+                base.form.contact.push(value);
+            }
+        };
+
+        //咨询问题类别
+        base.cate_list = Vue.reactive({$cate_list});
+        base.selectCate = value => {
+            if (base.in_array(value,base.form.cate)) {
+                base.removeByVal(base.form.cate,value);
+            } else {
+                base.form.cate.push(value);
+            }
+        };
+
+
+        //表单提交
+        base.onSubmit = () => {
+            postJson('/work/addPost',base.form).then(() => {
+                vant.showDialog({
+                    title: '提示',
+                    message: '添加成功',
+                }).then(() => {
+                    location.href = "{:url('index/index')}";
+                });
+            });
+        };
+
+        return base;
+    }
+</script>
+{/block}

+ 192 - 0
app/talent/view/work/copy.html

@@ -0,0 +1,192 @@
+{extend name="public/base"/}
+{block name="css"}
+<style>
+    .van-row {width:100%;}
+    .btn_search_item{background:#f2f6ff;display:inline-block;border-radius:5px;line-height:35px;text-align:center;position:relative;font-size:13px;width:100%;color:#666;margin-bottom:10px;}
+    .btn_search_item.active{color:var(--pink);background:#fff7fa;}
+    .btn_search_icon{position:absolute;bottom:-3px;right:-3px;font-size:30px;}
+</style>
+{/block}
+{block name="body"}
+<van-nav-bar
+        class="nav-theme"
+        :fixed="true"
+        :placeholder="true"
+        left-text="返回"
+        left-arrow
+        @click-left="onBack"
+>
+    <template #title>
+        <span class="text-white">添加记录</span>
+    </template>
+</van-nav-bar>
+<van-form @submit="onSubmit">
+    <van-cell-group>
+        <van-field
+                v-model="form.should_num"
+                required
+                type="number"
+                label="应挂钩人数"
+                placeholder="请输入应挂钩人数"
+                :rules="[{ required: true, message: '请输入应挂钩人数' }]"
+        ></van-field>
+        <van-field
+                v-model="form.new_num"
+                required
+                type="number"
+                label="本月新增人数"
+                placeholder="请输入本月新增人数"
+                :rules="[{ required: true, message: '请输入本月新增人数' }]"
+        ></van-field>
+        <van-field
+                v-model="form.unfinished_num"
+                required
+                type="number"
+                label="未完成挂钩人数"
+                placeholder="请输入未完成挂钩人数"
+                :rules="[{ required: true, message: '请输入未完成挂钩人数' }]"
+        ></van-field>
+        <van-field
+                v-model="form.reason"
+                rows="2"
+                required
+                autosize
+                label="未挂钩联系原因"
+                type="textarea"
+                placeholder="若全部联系请填无"
+                :rules="[{ required: true, message: '请输入未挂钩联系原因' }]"
+        ></van-field>
+        <van-field
+                v-model="form.consult_num"
+                required
+                type="number"
+                label="本月回答咨询次数"
+                placeholder="请输入本月回答咨询次数"
+                :rules="[{ required: true, message: '请输入本月回答咨询次数' }]"
+        ></van-field>
+
+        <!--联系方式-->
+        <van-field name="form.contact" label="联系方式">
+            <template #input>
+                <van-row :gutter="10">
+                    <van-col span="8" v-for="(item) in contact_list">
+                        <div :class="{btn_search_item:true,active: in_array(item,form.contact)}"
+                             @click="selectContact(item)">
+                            {{item}}
+                            <van-icon v-if="in_array(item,form.contact)"
+                                      class="iconfont icon-gouxuan-youxiajiaogouxuan btn_search_icon text-blue"></van-icon>
+                        </div>
+                    </van-col>
+                </van-row>
+            </template>
+        </van-field>
+
+        <!--联系方式-->
+        <van-field name="form.cate" label="咨询问题类别">
+            <template #input>
+                <van-row :gutter="10">
+                    <van-col span="8" v-for="(item) in cate_list">
+                        <div :class="{btn_search_item:true,active: in_array(item,form.cate)}"
+                             @click="selectCate(item)">
+                            {{item}}
+                            <van-icon v-if="in_array(item,form.cate)"
+                                      class="iconfont icon-gouxuan-youxiajiaogouxuan btn_search_icon text-blue"></van-icon>
+                        </div>
+                    </van-col>
+                </van-row>
+            </template>
+        </van-field>
+
+        <van-field
+                v-model="form.description"
+                rows="2"
+                required
+                autosize
+                label="具体问题描述及解决措施描述"
+                type="textarea"
+                placeholder="若全部联系请填无"
+                :rules="[{ required: true, message: '请输入具体问题描述及解决措施描述' }]"
+        ></van-field>
+        <van-field
+                v-model="form.assist"
+                rows="2"
+                required
+                autosize
+                label="需协调事项说明"
+                type="textarea"
+                placeholder="若全部联系请填无"
+                :rules="[{ required: true, message: '请输入需协调事项说明' }]"
+        ></van-field>
+    </van-cell-group>
+    <div style="margin: 16px;">
+        <van-button round block type="primary" native-type="submit">
+            提交
+        </van-button>
+    </div>
+</van-form>
+{/block}
+{block name="script"}
+<script>
+    function v_setup() {
+        let base = {};
+
+        base.form = Vue.reactive({$info});
+        base.onBack = () => {
+            location.href = "{:url('index/index')}";
+        };
+
+        //公共方法
+        base.in_array = (search,array) => {
+            for(var i in array){
+                if(array[i] == search){
+                    return true;
+                }
+            }
+            return false;
+        };
+        base.removeByVal = (arrylist , val) => {
+            for(var i = 0; i < arrylist .length; i++) {
+                if(arrylist[i] == val) {
+                    arrylist .splice(i, 1);
+                    break;
+                }
+            }
+        };
+
+        //联系方式
+        base.contact_list = Vue.reactive({$contact_list});
+        base.selectContact = value => {
+            if (base.in_array(value,base.form.contact)) {
+                base.removeByVal(base.form.contact,value);
+            } else {
+                base.form.contact.push(value);
+            }
+        };
+
+        //咨询问题类别
+        base.cate_list = Vue.reactive({$cate_list});
+        base.selectCate = value => {
+            if (base.in_array(value,base.form.cate)) {
+                base.removeByVal(base.form.cate,value);
+            } else {
+                base.form.cate.push(value);
+            }
+        };
+
+
+        //表单提交
+        base.onSubmit = () => {
+            postJson('/work/copyPost',base.form).then(() => {
+                vant.showDialog({
+                    title: '提示',
+                    message: '添加成功',
+                }).then(() => {
+                    location.href = "{:url('index/index')}";
+                });
+            });
+        };
+
+        return base;
+    }
+</script>
+{/block}

+ 192 - 0
app/talent/view/work/edit.html

@@ -0,0 +1,192 @@
+{extend name="public/base"/}
+{block name="css"}
+<style>
+    .van-row {width:100%;}
+    .btn_search_item{background:#f2f6ff;display:inline-block;border-radius:5px;line-height:35px;text-align:center;position:relative;font-size:13px;width:100%;color:#666;margin-bottom:10px;}
+    .btn_search_item.active{color:var(--pink);background:#fff7fa;}
+    .btn_search_icon{position:absolute;bottom:-3px;right:-3px;font-size:30px;}
+</style>
+{/block}
+{block name="body"}
+<van-nav-bar
+        class="nav-theme"
+        :fixed="true"
+        :placeholder="true"
+        left-text="返回"
+        left-arrow
+        @click-left="onBack"
+>
+    <template #title>
+        <span class="text-white">修改记录</span>
+    </template>
+</van-nav-bar>
+<van-form @submit="onSubmit">
+    <van-cell-group>
+        <van-field
+                v-model="form.should_num"
+                required
+                type="number"
+                label="应挂钩人数"
+                placeholder="请输入应挂钩人数"
+                :rules="[{ required: true, message: '请输入应挂钩人数' }]"
+        ></van-field>
+        <van-field
+                v-model="form.new_num"
+                required
+                type="number"
+                label="本月新增人数"
+                placeholder="请输入本月新增人数"
+                :rules="[{ required: true, message: '请输入本月新增人数' }]"
+        ></van-field>
+        <van-field
+                v-model="form.unfinished_num"
+                required
+                type="number"
+                label="未完成挂钩人数"
+                placeholder="请输入未完成挂钩人数"
+                :rules="[{ required: true, message: '请输入未完成挂钩人数' }]"
+        ></van-field>
+        <van-field
+                v-model="form.reason"
+                rows="2"
+                required
+                autosize
+                label="未挂钩联系原因"
+                type="textarea"
+                placeholder="若全部联系请填无"
+                :rules="[{ required: true, message: '请输入未挂钩联系原因' }]"
+        ></van-field>
+        <van-field
+                v-model="form.consult_num"
+                required
+                type="number"
+                label="本月回答咨询次数"
+                placeholder="请输入本月回答咨询次数"
+                :rules="[{ required: true, message: '请输入本月回答咨询次数' }]"
+        ></van-field>
+
+        <!--联系方式-->
+        <van-field name="form.contact" label="联系方式">
+            <template #input>
+                <van-row :gutter="10">
+                    <van-col span="8" v-for="(item) in contact_list">
+                        <div :class="{btn_search_item:true,active: in_array(item,form.contact)}"
+                             @click="selectContact(item)">
+                            {{item}}
+                            <van-icon v-if="in_array(item,form.contact)"
+                                      class="iconfont icon-gouxuan-youxiajiaogouxuan btn_search_icon text-blue"></van-icon>
+                        </div>
+                    </van-col>
+                </van-row>
+            </template>
+        </van-field>
+
+        <!--联系方式-->
+        <van-field name="form.cate" label="咨询问题类别">
+            <template #input>
+                <van-row :gutter="10">
+                    <van-col span="8" v-for="(item) in cate_list">
+                        <div :class="{btn_search_item:true,active: in_array(item,form.cate)}"
+                             @click="selectCate(item)">
+                            {{item}}
+                            <van-icon v-if="in_array(item,form.cate)"
+                                      class="iconfont icon-gouxuan-youxiajiaogouxuan btn_search_icon text-blue"></van-icon>
+                        </div>
+                    </van-col>
+                </van-row>
+            </template>
+        </van-field>
+
+        <van-field
+                v-model="form.description"
+                rows="2"
+                required
+                autosize
+                label="具体问题描述及解决措施描述"
+                type="textarea"
+                placeholder="若全部联系请填无"
+                :rules="[{ required: true, message: '请输入具体问题描述及解决措施描述' }]"
+        ></van-field>
+        <van-field
+                v-model="form.assist"
+                rows="2"
+                required
+                autosize
+                label="需协调事项说明"
+                type="textarea"
+                placeholder="若全部联系请填无"
+                :rules="[{ required: true, message: '请输入需协调事项说明' }]"
+        ></van-field>
+    </van-cell-group>
+    <div style="margin: 16px;">
+        <van-button round block type="primary" native-type="submit">
+            提交
+        </van-button>
+    </div>
+</van-form>
+{/block}
+{block name="script"}
+<script>
+    function v_setup() {
+        let base = {};
+
+        base.form = Vue.reactive({$info});
+        base.onBack = () => {
+            location.href = "{:url('index/index')}";
+        };
+
+        //公共方法
+        base.in_array = (search,array) => {
+            for(var i in array){
+                if(array[i] == search){
+                    return true;
+                }
+            }
+            return false;
+        };
+        base.removeByVal = (arrylist , val) => {
+            for(var i = 0; i < arrylist .length; i++) {
+                if(arrylist[i] == val) {
+                    arrylist .splice(i, 1);
+                    break;
+                }
+            }
+        };
+
+        //联系方式
+        base.contact_list = Vue.reactive({$contact_list});
+        base.selectContact = value => {
+            if (base.in_array(value,base.form.contact)) {
+                base.removeByVal(base.form.contact,value);
+            } else {
+                base.form.contact.push(value);
+            }
+        };
+
+        //咨询问题类别
+        base.cate_list = Vue.reactive({$cate_list});
+        base.selectCate = value => {
+            if (base.in_array(value,base.form.cate)) {
+                base.removeByVal(base.form.cate,value);
+            } else {
+                base.form.cate.push(value);
+            }
+        };
+
+
+        //表单提交
+        base.onSubmit = () => {
+            postJson('/work/editPost',base.form).then(() => {
+                vant.showDialog({
+                    title: '提示',
+                    message: '修改成功',
+                }).then(() => {
+                    location.href = "{:url('index/index')}";
+                });
+            });
+        };
+
+        return base;
+    }
+</script>
+{/block}

二進制
public/static/common/exl/talent_user.xls


二進制
public/static/mobile/images/talent_work_add.png


+ 28 - 0
public/static/mobile/js/axios_talent.js

@@ -0,0 +1,28 @@
+const axios_instance = axios.create({
+    baseURL: baseUrl + "talent",
+    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 {
+                    vant.showToast(data.msg);
+                    return false;
+                }
+            } else if (data.code === 401) {
+                location.href = '/talent/login/login';
+            }
+            return resolve(data);
+        });
+    });
+}