浏览代码

省外经纪人简历库

linwu 1 年之前
父节点
当前提交
3393bcce02

+ 0 - 9
app/admin/common.php

@@ -99,12 +99,3 @@ if (!function_exists('excel_export')) {
         exit();
     }
 }
-
-function array_get($array,$key,$default = '')
-{
-    if (empty($array[$key])) {
-        return $default;
-    }
-
-    return $array[$key];
-}

+ 122 - 0
app/admin/controller/Outresume.php

@@ -0,0 +1,122 @@
+<?php
+
+namespace app\admin\controller;
+
+use app\admin\BaseController;
+use app\common\model\OutFollow;
+use app\common\model\OutResume as OutResumeModel;
+
+class Outresume extends BaseController
+{
+
+    // 列表
+    public function index()
+    {
+        $status_list = OutResumeModel::$status;
+
+        return view('outresume/resumelist', [
+            'status_list' => $status_list,
+        ]);
+    }
+
+    public function listresume()
+    {
+        $limit   = input('limit');
+        $page    = input('page');
+        $map     = [];
+        $keyword = input('keyword/s');
+        if (!empty($keyword)) {
+            $map[] = ['name|mobile', 'like', "%{$keyword}%"];
+        }
+        $status = input('status/d');
+        if (!empty($status)) {
+            $map[] = ['status', '=', $status];
+        }
+        $list  = OutResumeModel::with('broker')
+            ->where($map)
+            ->order(['updatetime' => 'desc'])
+            ->limit($limit)
+            ->page($page)
+            ->append(['status_text', 'gender_text'])
+            ->select();
+        $count = OutResumeModel::where($map)->count();
+        if ($count == 0) {
+            exit(json_encode([
+                'code' => 1,
+                'msg'  => "未查询到数据",
+            ]));
+        }
+        exit(json_encode([
+            'code'  => 0,
+            'msg'   => "",
+            'count' => $count,
+            'data'  => $list,
+        ]));
+    }
+
+    public function resume()
+    {
+        $id     = input('get.id');
+        $resume = OutResumeModel::with(['broker'])->where('id', $id)->find();
+        if (empty($resume)) {
+            exit('该数据不存在');
+        }
+        $status_list = OutResumeModel::$status;
+
+        return view('outresume/resumeform', [
+            'status_list' => $status_list,
+            'resume'      => $resume,
+        ]);
+    }
+
+    public function editresume()
+    {
+        $data = input('post.');
+
+        OutResumeModel::update($data);
+
+        exit(json_encode([
+            'code' => 0,
+        ]));
+    }
+
+    // 用户跟进记录
+    public function follow()
+    {
+        $id          = input('id/d');
+        $resume      = OutResumeModel::findOrEmpty($id);
+        $follow_list = OutFollow::where('resume_id', $id)->order('id', 'desc')->limit(100)->select();
+        $status_list = OutResumeModel::$status;
+        return view('outresume/follow', [
+            'resume'      => $resume,
+            'follow_list' => $follow_list,
+            'status_list' => $status_list,
+        ]);
+    }
+
+    public function editFollow()
+    {
+        $resume_id = input('resume_id/d', 0);
+        $resume    = OutResumeModel::findOrEmpty($resume_id);
+        if ($resume->isEmpty()) {
+            exit(json_encode([
+                'code' => 1,
+                'msg'  => "信息不存在。",
+            ]));
+        }
+        $status = input('status/d', 1);
+        OutFollow::create([
+            'resume_id'  => $resume_id,
+            'remark'     => input('remark/s', ""),
+            'status'     => $status,
+            'createtime' => time(),
+        ]);
+        $resume->save([
+            'status'     => $status,
+            'updatetime' => time(),
+        ]);
+        exit(json_encode([
+            'code' => 0,
+        ]));
+    }
+}

+ 104 - 0
app/admin/view/outresume/follow.html

@@ -0,0 +1,104 @@
+<div class="layui-fluid">
+	<div class="layui-card">
+		<div class="layui-card-header">跟进信息</div>
+		<div class="layui-card-body">
+
+			<div class="layui-form layui-form-pane" lay-filter="LAY-resume-follow-edit">
+				<input type="hidden" name="resume_id" value="{$resume.id}">
+				<div class="layui-form-item">
+					<label class="layui-form-label">用户信息</label>
+					<div class="layui-input-block">
+						<input type="text" value="{$resume.name} - {$resume.mobile}" readonly class="layui-input layui-disabled">
+					</div>
+				</div>
+				<div class="layui-form-item">
+					<label class="layui-form-label"><span style="color:#f90c05;">*</span>跟进备注</label>
+					<div class="layui-input-block">
+						<textarea name="remark" lay-verify="required" placeholder="请输入..." class="layui-textarea"></textarea>
+					</div>
+				</div>
+				<div class="layui-form-item" pane>
+					<label class="layui-form-label">跟进状态</label>
+					<div class="layui-input-block">
+						<input type="radio" name="status" value="1" title="未跟进" {eq name="resume.status|default=1" value="1" }checked{/eq} />
+						<input type="radio" name="status" value="2" title="未面试" {eq name="resume.status" value="2" }checked{/eq} />
+						<input type="radio" name="status" value="3" title="面试通过" {eq name="resume.status" value="3" }checked{/eq} />
+						<input type="radio" name="status" value="4" title="面试未通过" {eq name="resume.status" value="4" }checked{/eq} />
+						<input type="radio" name="status" value="5" title="用户放弃" {eq name="resume.status" value="5" }checked{/eq} />
+						<input type="radio" name="status" value="6" title="已入职" {eq name="resume.status" value="6" }checked{/eq} />
+						<input type="radio" name="status" value="7" title="已离职" {eq name="resume.status" value="7" }checked{/eq} />
+					</div>
+				</div>
+				<div class="layui-form-item">
+					<div class="layui-input-block">
+						<input type="button" lay-submit lay-filter="LAY-outresume-follow-edit-submit" value="确认" class="layui-btn">
+					</div>
+				</div>
+			</div>
+
+			<fieldset class="layui-elem-field layui-field-title" style="margin-top: 30px;">
+				<legend>以往跟进记录</legend>
+			</fieldset>
+			<ul class="layui-timeline">
+				{volist name="follow_list" id="vo"}
+				<li class="layui-timeline-item">
+					<i class="layui-icon layui-timeline-axis"></i>
+					<div class="layui-timeline-content layui-text">
+						<h3 class="layui-timeline-title">
+							{$vo.createtime} 【{$vo.status_text}】
+						</h3>
+						<p>{$vo.remark}</p>
+					</div>
+				</li>
+				{/volist}
+				<li class="layui-timeline-item">
+					<i class="layui-icon layui-timeline-axis"></i>
+					<div class="layui-timeline-content layui-text">
+						<div class="layui-timeline-title">已加载完 ( 最多显示最近100条跟进记录 )</div>
+					</div>
+				</li>
+			</ul>
+
+
+		</div>
+	</div>
+</div>
+
+<script>
+	layui.config({
+		base: '/static/echoui/' //静态资源所在路径
+	}).extend({
+		index: 'lib/index' //主入口模块
+	}).use(['index', 'form', 'set', 'table'], function() {
+		var $ = layui.$,
+			setter = layui.setter,
+			admin = layui.admin,
+			form = layui.form,
+			table = layui.table;
+		form.render();
+
+		form.on('submit(LAY-outresume-follow-edit-submit)', function(obj) {
+			var index = parent.layer.getFrameIndex(window.name);
+			admin.req({
+				url: setter.baseAdminUrl + 'outresume/editfollow',
+				data: obj.field,
+				done: function(res) {
+					layer.msg("提交成功", {
+						icon: 1
+					});
+					var findex = parent.layer.open({
+						type: 2,
+						title: '用户【{$resume.name} - {$resume.mobile}】跟进记录',
+						content: 'follow.html?id={$resume.id}',
+						maxmin: true,
+						area: ['750px', '550px']
+					});
+					parent.layui.table.reload('LAY-outresume-resumelist-table');
+					parent.layer.close(index);
+					parent.layer.full(findex);
+				}
+			});
+		});
+
+	});
+</script>

+ 134 - 0
app/admin/view/outresume/resumeform.html

@@ -0,0 +1,134 @@
+<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-outresume-resumeform-edit">
+						<input type="hidden" name="id" value="{$resume.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="{$resume.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" value="{$resume.broker.title} - {$resume.broker.mobile}" readonly 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="{$resume.mobile}" 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="idcard" value="{$resume.idcard}" lay-verify="required" placeholder="请输入身份证号" autocomplete="off"
+									   class="layui-input">
+							</div>
+						</div>
+						<div class="layui-form-item" pane>
+							<label class="layui-form-label">性别</label>
+							<div class="layui-input-block">
+								<input type="radio" name="gender" value="1" title="男" {eq name="resume.gender|default=1" value="1" }checked{/eq} />
+								<input type="radio" name="gender" value="2" title="女" {eq name="resume.gender" value="2" }checked{/eq} />
+							</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="age" value="{$resume.age}" lay-verify="required" 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="jobintention" value="{$resume.jobintention}" 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="address" value="{$resume.address}" 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="education" value="{$resume.education}" placeholder="请输入学历" autocomplete="off"
+									   class="layui-input">
+							</div>
+						</div>
+						<div class="layui-form-item">
+							<label class="layui-form-label">备注</label>
+							<div class="layui-input-block">
+								<textarea class="layui-textarea" name="comment" id="details" placeholder="请输入备注" style="display: none"
+								 lay-verify="editcontent">
+											{$resume.comment}
+										</textarea>
+							</div>
+						</div>
+						<div class="layui-form-item">
+							<div class="layui-input-block">
+								<input type="button" lay-submit lay-filter="LAY-outresume-resumeform-edit-submit" value="确认提交" class="layui-btn">
+							</div>
+						</div>
+					</div>
+
+				</div>
+			</div>
+		</div>
+	</div>
+</div>
+
+<script>
+	layui.config({
+		base: '/static/echoui/' //静态资源所在路径
+	}).extend({
+		index: 'lib/index' //主入口模块
+	}).use(['index', 'form', 'set', 'layedit', 'laydate', 'upload'], function() {
+		var $ = layui.$,
+			setter = layui.setter,
+			admin = layui.admin,
+			layedit = layui.layedit,
+			laydate = layui.laydate,
+			form = layui.form,
+			upload = layui.upload;
+		form.render();
+
+		var details = layedit.build('details', {
+			uploadImage: { url: setter.baseAdminUrl + 'attachment/tplfieldimage', type: 'post' }
+		});
+		form.verify({
+			editcontent: function(value) {
+				layedit.sync(details);
+			}
+		});
+
+		form.on('submit(LAY-outresume-resumeform-edit-submit)', function(obj) {
+			var index = parent.layer.getFrameIndex(window.name);
+			admin.req({
+				url: setter.baseAdminUrl + 'outresume/editresume',
+				type: 'post',
+				data: obj.field,
+				done: function(res) {
+					layer.msg("提交成功", {
+						icon: 1
+					});
+					parent.layui.table.reload('LAY-outresume-resumelist-table'); //重载表格
+					parent.layer.close(index);
+				}
+			});
+		});
+
+	});
+</script>

+ 113 - 0
app/admin/view/outresume/resumelist.html

@@ -0,0 +1,113 @@
+<div class="layui-fluid">
+	<div class="layui-card">
+		<div class="layui-form layui-form-pane  layui-card-header layuiadmin-card-header-auto" lay-filter="LAY-outresume-resumelist-search">
+			<div class="layui-form-item">
+				<div class="layui-inline">
+					<label class="layui-form-label">姓名/手机号</label>
+					<div class="layui-input-block">
+						<input type="text" name="keyword" 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="status"}
+								<option value="{$key}">{$status}</option>
+							{/volist}
+						</select>
+					</div>
+				</div>
+				<div class="layui-inline">
+					<button class="layui-btn" lay-submit lay-filter="LAY-outresume-resumelist-search-btn">
+						<i class="layui-icon layui-icon-search layuiadmin-button-btn"></i>
+					</button>
+				</div>
+			</div>
+		</div>
+
+		<div class="layui-card-body">
+			<table id="LAY-outresume-resumelist-table" lay-filter="LAY-outresume-resumelist-table"></table>
+			<script type="text/html" id="broker">
+				{{d.broker.title}}({{d.broker.mobile}})
+			</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-normal layui-btn-xs" lay-event="follow">
+					<i class="layui-icon layui-icon-edit"></i>跟进
+				</a>
+			</script>
+		</div>
+	</div>
+</div>
+
+<script>
+	layui.config({
+		base: '/static/echoui/' //静态资源所在路径
+	}).extend({
+		index: 'lib/index' //主入口模块
+	}).use(['index', 'form', 'set', 'table'], function() {
+		var $ = layui.$,
+			setter = layui.setter,
+			admin = layui.admin,
+			form = layui.form,
+			table = layui.table;
+		form.render();
+
+		table.render({
+			elem: '#LAY-outresume-resumelist-table',
+			url: setter.baseAdminUrl + 'outresume/listresume',
+			cols: [
+				[
+					{ title: '经纪人', align: 'center', toolbar: '#broker' },
+					{ field: 'name', title: '姓名', width: 100},
+					{ field: 'mobile', title: '联系方式', width: 160 },
+					{ field: 'idcard', title: '身份证号' },
+					{ field: 'gender_text', title: '性别', width: 60 },
+					{ field: 'status_text', title: '状态', width: 80, align: 'center' },
+					{ title: '操作', width: 150, align: 'center', fixed: 'right', toolbar: '#setTpl' }
+				]
+			],
+			page: true,
+			limit: 50,
+			cellMinWidth: 150,
+			text: '对不起,加载出现异常!'
+		});
+
+		form.on('submit(LAY-outresume-resumelist-search-btn)', function(data) {
+			table.reload('LAY-outresume-resumelist-table', {
+				where: data.field,
+				page: {
+					curr: 1
+				}
+			});
+		});
+
+		//监听工具条
+		table.on('tool(LAY-outresume-resumelist-table)', function(obj) {
+			var data = obj.data;
+			if (obj.event === 'edit') {
+				var index = layer.open({
+					type: 2,
+					title: '编辑简历',
+					content: 'resume.html?id=' + data.id,
+					maxmin: true,
+					area: ['550px', '550px']
+				});
+				layer.full(index);
+			} else if (obj.event === 'follow') {
+				var index = layer.open({
+					type: 2,
+					title: '用户【' + data.name + ' - ' + data.mobile + '】跟进记录',
+					content: 'follow.html?id=' + data.id,
+					maxmin: true,
+					area: ['750px', '550px']
+				});
+				layer.full(index);
+			}
+		});
+	});
+</script>

+ 4 - 0
app/common.php

@@ -16,6 +16,10 @@ use think\facade\Db;
 // 高德地图Key
 define('AMAP_KEY', '937f431e40453c79c1c18af4a69c6b79');
 
+function array_get($array, $key, $default = null)
+{
+    return isset($array[$key]) ? $array[$key] : $default;
+}
 
 function page_result1($code = 0, $msg = '', $data = [])
 {

+ 37 - 0
app/common/model/OutFollow.php

@@ -0,0 +1,37 @@
+<?php
+
+namespace app\common\model;
+
+use think\Model;
+
+class OutFollow extends Model
+{
+    // 设置字段信息
+    protected $schema = [
+        'id'         => 'int',
+        'resume_id'  => 'int',
+        'status'     => 'int',
+        'remark'     => 'string',
+        'createtime' => 'int',
+    ];
+
+    // 设置字段自动转换类型
+    protected $type = [
+        'createtime' => 'timestamp:Y-m-d H:i:s',
+    ];
+
+    public static $status = [1 => '未跟进', 2 => '未面试', 3 => '面试通过', 4 => '面试未通过', 5 => '用户放弃', 6 => '已入职', 7 => '已离职'];
+
+    public function getStatusTextAttr($value, $data)
+    {
+        return self::$status[$data['status']];
+    }
+
+    // 关联User
+    public function resume()
+    {
+        return $this->hasOne(OutResume::class, "id", "resume_id");
+    }
+
+
+}

+ 53 - 0
app/common/model/OutResume.php

@@ -0,0 +1,53 @@
+<?php
+
+namespace app\common\model;
+
+use think\Model;
+
+class OutResume extends Model
+{
+
+    // 设置字段信息
+    protected $schema = [
+        'id'           => 'int',
+        'brokerid'     => 'int',
+        'name'         => 'string',
+        'avatar'       => 'string',
+        'mobile'       => 'string',
+        'idcard'       => 'string',
+        'gender'       => 'tinyint',
+        'age'          => 'int',
+        'jobintention' => 'string',
+        'address'      => 'string',
+        'education'    => 'string',
+        'createtime'   => 'int',
+        'updatetime'   => 'int',
+        'comment'      => 'string',
+        'status'       => 'int',
+    ];
+
+    // 设置字段自动转换类型
+    protected $type = [
+        'createtime' => 'timestamp:Y-m-d H:i:s',
+        'updatetime' => 'timestamp:Y-m-d H:i:s',
+    ];
+
+    static $status = [1 => '未跟进', 2 => '未面试', 3 => '面试通过', 4 => '面试未通过', 5 => '用户放弃', 6 => '已入职', 7 => '已离职'];
+    static $gender = [1 => '男', 2 => '女'];
+
+    public function getStatusTextAttr($value, $data)
+    {
+        return self::$status[$data['status']];
+    }
+
+    public function getGenderTextAttr($value, $data)
+    {
+        return self::$gender[$data['gender']];
+    }
+
+    // 关联Worker
+    public function broker()
+    {
+        return $this->hasOne(Broker::class, "id", "brokerid");
+    }
+}

+ 8 - 1
app/mobile/common.php

@@ -25,13 +25,20 @@ function jump($msg = '', $url = null, $wait = 3)
     throw new \think\exception\HttpResponseException($html);
 }
 
-function ajax_success($data)
+function ajax_success($data = '')
 {
     $res      = ['code' => 0, 'msg' => '成功', 'data' => $data];
     $response = \think\Response::create($res, 'json');
     throw new \think\exception\HttpResponseException($response);
 }
 
+function ajax_return($code = 0, $msg = '', $data = [])
+{
+    $res      = ['code' => $code, 'msg' => $msg, 'data' => $data];
+    $response = \think\Response::create($res, 'json');
+    throw new \think\exception\HttpResponseException($response);
+}
+
 function get_user_id()
 {
     $sessionUserId = session('mobile.user.id');

+ 146 - 0
app/mobile/controller/Resume.php

@@ -0,0 +1,146 @@
+<?php
+
+namespace app\mobile\controller;
+
+use app\common\model\OutFollow;
+use app\common\model\OutResume as OutResumeModel;
+use app\common\model\OutResume;
+use app\mobile\MobileBaseController;
+use app\mobile\validate\FollowValidate;
+use app\mobile\validate\ResumeValidate;
+use think\App;
+use think\exception\ValidateException;
+use think\facade\View;
+
+class Resume extends MobileBaseController
+{
+    private $_broker = null;
+
+    public function __construct(App $app)
+    {
+        parent::__construct($app);
+        $this->_broker = get_broker();
+        View::assign('broker', $this->_broker);
+    }
+
+    /**
+     * 简历列表
+     */
+    public function index()
+    {
+        return view('resume/index');
+    }
+
+    /**
+     * 收益明细列表
+     */
+    public function listResume()
+    {
+        $map = $this->dealLikeInput(['searchKey' => 'name|mobile']);
+
+        $map[] = ['brokerid', '=', $this->_broker['id']];
+        $list  = OutResumeModel::where($map)
+            ->order(['updatetime' => 'desc'])
+            ->limit(input('limit', 5))
+            ->page(input('page', 1))
+            ->append(['status_text'])
+            ->select();
+
+        foreach ($list as $v) {
+            $log = OutFollow::where('resume_id', $v['id'])->order('id desc')->find();
+            if (!empty($log)) {
+                $v['last_msg']      = $log['remark'];
+                $v['last_msg_time'] = $log['createtime'];
+            } else {
+                $v['last_msg']      = '未跟进';
+                $v['last_msg_time'] = '';
+            }
+        }
+
+        ajax_success($list);
+    }
+
+    /**
+     * 表单
+     */
+    public function form()
+    {
+        $id   = input('get.id');
+        $data = ['form' => '{gender:1}'];
+
+        if (!empty($id)) {
+            $form = OutResume::where('id', $id)->find();
+            if (!empty($form)) {
+                $data['form'] = $form;
+            }
+        }
+
+        return view('resume/form', $data);
+    }
+
+    /**
+     * 表单提交
+     */
+    public function formPost()
+    {
+        $data = input('post.');
+        try {
+            validate(ResumeValidate::class)->check($data);
+        } catch (ValidateException $e) {
+            ajax_return(1, $e->getError());
+        }
+
+        $data['brokerid']   = $this->_broker['id'];
+        $data['updatetime'] = time();
+
+        if (empty($data['id'])) {
+            $data['createtime'] = time();
+            OutResume::create($data);
+        } else {
+            OutResume::update($data);
+        }
+
+        ajax_return();
+    }
+
+    /**
+     * 跟进
+     */
+    public function follow()
+    {
+        $id     = input('get.id');
+        $resume = OutResume::where('id', $id)->append(['gender_text'])->find();
+        if (empty($resume)) {
+            jump('该简历不存在');
+        }
+
+        $follow = OutFollow::where('resume_id', $id)->append(['status_text'])->order('id', 'desc')->select();
+
+        return view('resume/follow', [
+            'id'          => $id,
+            'resume'      => json_encode($resume),
+            'follow'      => $follow,
+            'status_list' => json_encode(OutFollow::$status),
+        ]);
+    }
+
+    /**
+     * 跟进提交
+     */
+    public function followPost()
+    {
+        $data = input('post.');
+        try {
+            validate(FollowValidate::class)->check($data);
+        } catch (ValidateException $e) {
+            ajax_return(1, $e->getError());
+        }
+
+        OutResume::update(['updatetime' => time()], ['id' => $data['resume_id']]);
+
+        $data['createtime'] = time();
+        OutFollow::create($data);
+
+        ajax_return();
+    }
+}

+ 20 - 0
app/mobile/validate/FollowValidate.php

@@ -0,0 +1,20 @@
+<?php
+
+namespace app\mobile\validate;
+
+use think\Validate;
+
+class FollowValidate extends Validate
+{
+    protected $rule = [
+        'resume_id' => 'require',
+        'status'    => 'require',
+        'remark'    => 'require',
+    ];
+
+    protected $message = [
+        'resume_id' => '简历信息不能为空',
+        'status'    => '请选择状态',
+        'remark'    => '备注不能为空',
+    ];
+}

+ 24 - 0
app/mobile/validate/ResumeValidate.php

@@ -0,0 +1,24 @@
+<?php
+
+namespace app\mobile\validate;
+
+use think\Validate;
+
+class ResumeValidate extends Validate
+{
+    protected $rule = [
+        'name'   => 'require',
+        'mobile' => 'require|mobile',
+        'idcard' => 'require|idCard',
+        'gender' => 'require',
+        'age'    => 'require|number',
+    ];
+
+    protected $message = [
+        'name'   => '请选择姓名',
+        'mobile' => '手机号格式不对',
+        'idcard' => '身份证号格式不对',
+        'gender' => '请选择性别',
+        'age'    => '年龄格式不对',
+    ];
+}

+ 1 - 1
app/mobile/view/login/mobile.html

@@ -108,7 +108,7 @@
         //手机号验证
         base.validator = (val) => {
             return /^1(?:3\d|4[4-9]|5[0-35-9]|6[67]|7[013-8]|8\d|9\d)\d{8}$/.test(val);
-        }
+        };
 
         return base;
     }

+ 1 - 1
app/mobile/view/my/index.html

@@ -52,7 +52,7 @@
             ></van-image>
         </template>
     </van-grid-item>
-    <van-grid-item text="简历库">
+    <van-grid-item text="简历库" url="/mobile/resume/index">
         <template #icon>
             <van-image
                     width="50%"

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

@@ -12,4 +12,5 @@
 <script>
     const baseUrl = "{:url('/')}";
 </script>
-<script src="__MJS__/axios_instance.js"></script>
+<script src="__MJS__/axios_instance.js"></script>
+<script src="__MJS__/common.js"></script>

+ 132 - 0
app/mobile/view/resume/follow.html

@@ -0,0 +1,132 @@
+{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-field
+        v-model="resume.name"
+        label="姓名"
+        readonly
+></van-field>
+<van-field
+        v-model="resume.gender_text"
+        label="性别"
+        readonly
+></van-field>
+<van-field
+        v-model="resume.mobile"
+        label="手机号"
+        readonly
+></van-field>
+<van-field
+        v-model="resume.idcard"
+        label="身份证号"
+        readonly
+></van-field>
+<van-field
+        v-model="resume.age"
+        label="年龄"
+        readonly
+></van-field>
+
+<van-tabs v-model:active="active" style="margin-top:10px;">
+    <van-tab title="上报跟进记录">
+        <van-form @submit="onSubmit">
+            <van-cell-group>
+                <van-field
+                        v-model="status_text"
+                        is-link
+                        required
+                        readonly
+                        label="状态"
+                        placeholder="请选择状态"
+                        @click="showStatus = true"
+                        :rules="[{ required: true, message: '请选择状态' }]"
+                ></van-field>
+                <van-popup v-model:show="showStatus" round position="bottom">
+                    <van-picker
+                            :columns="statusList"
+                            @cancel="showStatus = false"
+                            @confirm="onStatusListConfirm"
+                    ></van-picker>
+                </van-popup>
+                <van-field
+                        v-model="form.remark"
+                        rows="2"
+                        required
+                        autosize
+                        label="备注"
+                        type="textarea"
+                        maxlength="1000"
+                        placeholder="请输入备注"
+                        show-word-limit
+                        :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>
+    </van-tab>
+    <van-tab title="往期跟进记录">
+        <van-empty v-if="follow.length == 0" description="暂无跟进记录"></van-empty>
+        <van-steps direction="vertical" :active="0">
+            <van-step v-for="f in follow">
+                <h3>【{{f.status_text}}】{{f.remark}}</h3>
+                <p>{{f.createtime}}</p>
+            </van-step>
+        </van-steps>
+    </van-tab>
+</van-tabs>
+{/block}
+{block name="script"}
+<script>
+    function v_setup() {
+        let base = {};
+
+        //基础数据
+        base.follow = Vue.reactive({$follow});
+        base.resume = Vue.reactive({$resume});
+
+        //标签页
+        base.active = Vue.ref(0);
+
+        //表单
+        base.form = Vue.reactive({resume_id:{$id},status:1,remark:''});
+        base.onSubmit = () => {
+            postJson("{:url('resume/followPost')}", base.form).then(({data}) => {
+                location.reload();
+            });
+        };
+
+        //状态选择
+        base.statusList = Vue.reactive(arrayToPicker({$status_list}));
+        base.status_text = Vue.ref(base.statusList[0].text);
+        base.showStatus = Vue.ref(false);
+        base.onStatusListConfirm = ({selectedOptions}) => {
+            base.showStatus.value = false;
+            base.form.status = selectedOptions[0].value;
+            base.status_text.value = selectedOptions[0].text;
+        };
+
+        base.onBack = () => {
+            history.back();
+        };
+
+        return base;
+    }
+</script>
+{/block}

+ 124 - 0
app/mobile/view/resume/form.html

@@ -0,0 +1,124 @@
+{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">
+    <div class="lw-title">基础信息</div>
+    <van-cell-group inset>
+        <van-field
+                v-model="form.name"
+                required
+                label="姓名"
+                placeholder="请填写姓名"
+                :rules="[{ required: true, message: '请填写姓名' }]"
+        ></van-field>
+        <van-field required name="gender" label="性别">
+            <template #input>
+                <van-radio-group v-model="form.gender" direction="horizontal">
+                    <van-radio :name="1">男</van-radio>
+                    <van-radio :name="2">女</van-radio>
+                </van-radio-group>
+            </template>
+        </van-field>
+        <van-field
+                v-model="form.mobile"
+                required
+                label="手机号"
+                placeholder="请填写手机号"
+                :rules="[
+                        { required: true, message: '请填写手机号' },
+                        { validator, message: '请输入正确的手机号'}
+                    ]"
+        ></van-field>
+        <van-field
+                v-model="form.idcard"
+                required
+                label="身份证"
+                placeholder="请填写身份证号"
+                :rules="[{ required: true, message: '请填写身份证号' }]"
+        ></van-field>
+        <van-field
+                v-model="form.age"
+                type="digit"
+                required
+                label="年龄"
+                placeholder="请填写年龄"
+                :rules="[{ required: true, message: '请填写年龄' }]"
+        ></van-field>
+    </van-cell-group>
+
+    <div class="lw-title">求职信息</div>
+    <van-cell-group inset>
+        <van-field
+                v-model="form.jobintention"
+                label="求职意向"
+                placeholder="请填写求职意向"
+        ></van-field>
+        <van-field
+                v-model="form.address"
+                label="地址"
+                placeholder="请填写地址"
+        ></van-field>
+        <van-field
+                v-model="form.education"
+                label="学历"
+                placeholder="请填写学历"
+        ></van-field>
+        <van-field
+                v-model="form.comment"
+                rows="2"
+                autosize
+                label="备注"
+                type="textarea"
+                maxlength="1000"
+                placeholder="请输入备注"
+                show-word-limit
+        ></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({$form});
+        base.onSubmit = () => {
+            postJson("{:url('resume/formPost')}", base.form).then(({data}) => {
+                location.href = "{:url('resume/index')}";
+            });
+        };
+
+        base.onBack = () => {
+            history.back();
+        };
+
+        //手机号验证
+        base.validator = (val) => {
+            return /^1(?:3\d|4[4-9]|5[0-35-9]|6[67]|7[013-8]|8\d|9\d)\d{8}$/.test(val);
+        };
+
+
+        return base;
+    }
+</script>
+{/block}

+ 76 - 0
app/mobile/view/resume/index.html

@@ -0,0 +1,76 @@
+{extend name="public/base"/}
+{block name="css"}
+<style>
+    .footer-block{width:100%;height:60px;}
+    .footer{position:fixed;width:100%;left:0;bottom:0;box-sizing:border-box;padding:5px 20px;background:white;}
+</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-search
+        v-model="form.searchKey"
+        placeholder="请输入姓名或手机号"
+        @search="onSearch"
+        @cancel="onCancel"
+        show-action
+>
+    <template #action>
+        <div @click="onCancel">重置</div>
+    </template>
+</van-search>
+
+{include file="public/list_load" list="<resume-list @follow='onFollow' @edit='onEdit' :list='list'></resume-list>" /}
+
+<div class="footer-block"></div>
+<div class="footer">
+    <van-button type="primary" @click="onAdd" block>添加</van-button>
+</div>
+{/block}
+{block name="script"}
+<script>
+    function v_setup() {
+        let base = list_load('resume/listResume',{searchKey:''});
+
+        //搜索
+        base.onSearch = () => {
+            base.onRefresh();
+        };
+        base.onCancel = () => {
+            base.form.searchKey = "";
+            base.onRefresh();
+        };
+
+        //列表
+        base.onFollow = (id) => {
+            location.href = "{:url('/mobile/resume/follow')}?id=" + id;
+        };
+        base.onEdit = (id) => {
+            location.href = "{:url('/mobile/resume/form')}?id=" + id;
+        };
+        base.onAdd = () => {
+            location.href = "{:url('/mobile/resume/form')}";
+        };
+
+        //头部
+        base.onBack = () => {
+            location.href = "{:url('/mobile/my/index')}";
+        };
+
+        return base;
+    }
+</script>
+{/block}
+{block name="vue"}
+<script src="__COMPONENTS__/resumeList.js"></script>
+{/block}

+ 17 - 2
public/static/mobile/css/style.css

@@ -3,7 +3,9 @@ body {
     --pink:#FF589B;
     --blue: #0081ff;
     --green: #39b54a;
-    --gray: #666;
+    --gray: #aaa;
+    --grey: #666;
+    --orange: #f37b1d;
     background:#f8f9fb;
     font-family: "Microsoft Yahei",arial,"Hiragino Sans GB","Hiragino Sans GB W3",宋体,simsun;
     color:#333;
@@ -16,6 +18,8 @@ html {
     box-sizing: inherit;
 }
 
+.lw-title {margin: 0;padding:16px;color:#969799;font-weight: 400;font-size: 14px;line-height: 16px;}
+
 .nav-theme,.nav-theme .van-nav-bar{background:var(--blue);}
 .nav-theme .van-nav-bar__title,.nav-theme .van-nav-bar__text,.nav-theme .van-icon{color:white;}
 .van-swipe {background:white;}
@@ -34,4 +38,15 @@ html {
 .income-list .income-item .left .comment {color: #aaaaaa;font-size: 12px;}
 .income-list .income-item .right {color: #e54d42;}
 .income-list .income-item .right .text {color: #0081ff;}
-.income-list .income-item .right .money {color: #e54d42;}
+.income-list .income-item .right .money {color: #e54d42;}
+
+.resume-list .resume-item{background:white;margin-top:10px;padding:10px 20px;}
+.resume-list .resume-item .title{display:flex;padding-bottom:10px;border-bottom:1px solid #eee;}
+.resume-list .resume-item .title .name {width:calc(100% - 70px);}
+.resume-list .resume-item .title .name .mobile {font-size:14px;}
+.resume-list .resume-item .title .status {width:70px;text-align:center;font-size:14px;color:var(--blue);}
+.resume-list .resume-item .content {padding:10px 0;}
+.resume-list .resume-item .content .remark {padding-bottom:5px;color:var(--orange);}
+.resume-list .resume-item .content .time {color:var(--gray);font-size:14px;}
+.resume-list .resume-item .tool {display:flex;align-items:center;justify-content:flex-end;}
+.resume-list .resume-item .tool .van-tag{margin-left:10px;}

+ 13 - 0
public/static/mobile/js/common.js

@@ -0,0 +1,13 @@
+/**
+ * 键值对数组转Picker数组
+ * @param arr array 键值对数组
+ */
+function arrayToPicker(arr)
+{
+    let res = [];
+    for (let key in arr) {
+        res.push({text:arr[key],value:key});
+    }
+
+    return res;
+}

+ 37 - 0
public/static/mobile/js/components/resumeList.js

@@ -0,0 +1,37 @@
+app.component('resume-list', {
+    template: `
+    <div class="resume-list">
+        <div class="resume-item" v-for="item in list">
+            <div class="title">
+                <div class="name">{{item.name}} <span class="mobile">({{item.mobile}})</span></div>
+                <div class="status">{{item.status_text}}</div>
+            </div>
+            <div class="content">
+                <div class="remark">{{item.last_msg}}</div>
+                <div class="time">{{item.last_msg_time}}</div>
+            </div>
+            <div class="tool">
+                <van-tag type="primary" plain size="large" @click="onFollow(item.id)">跟进</van-tag>
+                <van-tag type="success" plain size="large" @click="onEdit(item.id)">编辑</van-tag>
+            </div>
+        </div>
+    </div>
+    `,
+    data() {
+        return {}
+    },
+    props: {
+        list: {
+            type: Array,
+            default: [],
+        },
+    },
+    methods: {
+        onFollow(id) {
+            this.$emit('follow',id);
+        },
+        onEdit(id) {
+            this.$emit('edit',id);
+        },
+    },
+});