作为程序员,“加班” 是常见职场场景 —— 不少朋友在换工作时,因缺乏目标公司的加班情况参考,容易陷入 “踩坑” 困境。此前有类似加班数据参考项目,但受外部环境影响,访问便利性下降。

基于此,我决定开发一款轻量化加班统计在线工具,核心是 “用技术提供客观职场数据参考”,且全程注重合规设计(无排名、不对比,仅做数据收集展示)。本文分享工具的技术实现细节,包括前端交互逻辑、AI 辅助开发技巧、合规性技术设计。

一、项目背景:为什么做这款工具?

  1. 职场需求驱动:身边很多同事换工作时,希望提前了解目标公司的加班频率、工作压力,但缺乏可信的参考渠道,因此需要一款 “数据真实、功能轻量化” 的工具;
  2. 技术落地场景:此前创业时注册过公司(目前闲置),可借助企业资质推进合规备案,避免个人开发的合规风险;同时,用 Vue 做前端、AI 辅助生成基础代码,能快速实现核心功能,降低开发成本。

二、核心功能设计:合规优先,极简实用

工具 V1 版本聚焦 “数据真实 + 合规安全”,仅保留 2 个核心功能,所有设计均围绕 “符合相关法规” 展开:

1. 手机号短信认证:确保数据源头真实

  • 技术逻辑:用户进入工具后,需完成手机号短信验证(前端做格式校验,后端做验证码有效性验证),避免恶意刷数据;
  • 合规细节:手机号存储采用 “MD5 脱敏 + 数据库加密”,仅在验证时临时存储明文,验证后立即销毁,符合《个人信息保护法》对 “敏感个人信息” 的处理要求。

2. 加班数据结构化填写:提供客观参考维度

用户认证后,可填写 4 类核心数据(均为下拉选择或数值输入,减少主观描述偏差):

数据维度

选项 / 输入规则

设计目的

公司名称

手动输入(需填写全称,如 “XX 科技有限公司”)

确保查询时精准匹配

加班频率

几乎不加班 / 每周 1-2 天 / 每周 3-4 天 / 每周 5-6 天 / 每天加班

标准化频率描述

每日加班时长

1 小时内 / 1-2 小时 / 2-3 小时 / 3 小时以上

避免 “模糊表述”(如 “经常加班”)

工作压力评分

1-10 分(1 = 无压力,10 = 极度焦虑)

量化主观感受,提高参考性

3. 合规性技术兜底:不做 “敏感功能”

  • 无任何 “公司排名”“加班时长 PK” 模块,仅支持 “按公司名称查询历史填写数据”;
  • 数据展示时隐藏 “填写人信息”,仅显示 “脱敏后的统计结果”(如 “XX 公司:近 30 天有 12 人填写,60% 选择‘每周 3-4 天加班’”),避免隐私泄露。

三、技术实现:Vue + AI 辅助开发(附核心代码)

作为个人开发,用 AI 生成基础代码框架(如表单校验、短信倒计时逻辑),再手动优化合规与安全细节,大幅提升开发效率。以下是核心模块的技术实现代码(已做安全加固):

1. 技术选型理由

  • 前端框架:选 Vue 2.x(CDN 引入,无需构建环境,轻量化),适配工具 “极简功能” 定位;
  • 交互工具:Axios 做接口请求(封装拦截器,处理请求加密);
  • 安全措施:手机号脱敏用 md5.js(前端临时加密,后端存储加密后的数据),避免明文传输。

2. 核心模块代码实现

(1)短信认证组件(含手机号脱敏 + 格式校验)

<!-- 短信认证组件:Vue 2.x + CDN 实现 -->

<template>

  <div class="auth-container">

    <h3>职场数据参考工具 - 身份验证</h3>

    <form @submit.prevent="handleAuth">

      <!-- 手机号输入 -->

      <div class="form-item">

        <label>手机号:</label>

        <input

          type="tel"

          v-model="phoneNum"

          placeholder="请输入11位手机号"

          @input="formatPhone"  <!-- 实时格式化(脱敏显示) -->

          required

        >

        <p class="tip" v-if="phoneNum.length>0">{{ formattedPhone }}</p>

      </div>

      <!-- 验证码输入 -->

      <div class="form-item">

        <label>验证码:</label>

        <div class="code-group">

          <input

            type="text"

            v-model="verifyCode"

            placeholder="请输入6位验证码"

            maxlength="6"

            required

          >

          <button

            type="button"

            class="get-code-btn"

            @click="getVerifyCode"

            :disabled="isCodeSending"

          >

            {{ isCodeSending ? `倒计时${countDown}s` : '获取验证码' }}

          </button>

        </div>

      </div>

      <button type="submit" class="submit-btn">验证通过,进入填写</button>

    </form>

  </div>

</template>

<script>

// 引入 md5 用于手机号脱敏(CDN 引入,无需安装)

import md5 from 'https://cdn.jsdelivr.net/npm/md5@2.3.0/dist/md5.min.js';

export default {

  data() {

    return {

      phoneNum: '',       // 原始手机号(仅前端临时存储,不落地)

      formattedPhone: '', // 脱敏后手机号(如:138****5678)

      verifyCode: '',

      isCodeSending: false,

      countDown: 60

    }

  },

  methods: {

    // 手机号脱敏格式化

    formatPhone() {

      const phone = this.phoneNum.replace(/\D/g, ''); // 过滤非数字

      if (phone.length <= 3) {

        this.formattedPhone = phone;

      } else if (phone.length <= 7) {

        this.formattedPhone = `${phone.slice(0,3)}****${phone.slice(3)}`;

      } else {

        this.formattedPhone = `${phone.slice(0,3)}****${phone.slice(7)}`;

      }

    },

    // 获取验证码(模拟接口请求,实际需对接后端)

    getVerifyCode() {

      // 1. 手机号格式校验

      const phoneReg = /^1[3-9]\d{9}$/;

      if (!phoneReg.test(this.phoneNum.replace(/\D/g, ''))) {

        alert('请输入正确的11位手机号');

        return;

      }

      // 2. 模拟发送验证码(实际项目中,此处需传“脱敏后的手机号”给后端)

      this.isCodeSending = true;

      const timer = setInterval(() => {

        this.countDown--;

        if (this.countDown <= 0) {

          clearInterval(timer);

          this.isCodeSending = false;

          this.countDown = 60;

        }

      }, 1000);

      // 3. 后端请求示例(传脱敏后的数据,避免明文)

      // axios.post('/api/send-code', {

      //   phone: md5(this.phoneNum) // 前端临时 md5 脱敏,后端需对应解密逻辑

      // }).then(res => {

      //   if (res.data.code !== 200) alert('验证码发送失败,请重试');

      // });

    },

    // 提交验证(模拟跳转)

    handleAuth() {

      if (this.verifyCode.length !== 6) {

        alert('请输入6位验证码');

        return;

      }

      // 验证通过:存储“已认证”状态(仅前端临时存储,不落地用户信息)

      localStorage.setItem('isAuth', 'true');

      this.$router.push('/fill-data'); // 跳转至数据填写页

    }

  }

}

</script>

<style scoped>

.auth-container { max-width: 500px; margin: 2rem auto; padding: 2rem; border: 1px solid #eee; border-radius: 8px; }

.form-item { margin-bottom: 1.5rem; }

.code-group { display: flex; gap: 1rem; }

.code-group input { flex: 1; }

.get-code-btn { padding: 0.5rem 1rem; background: #409eff; color: #fff; border: none; border-radius: 4px; cursor: pointer; }

.submit-btn { width: 100%; padding: 0.8rem; background: #67c23a; color: #fff; border: none; border-radius: 4px; cursor: pointer; }

.tip { color: #999; font-size: 0.875rem; margin-top: 0.3rem; }

</style>

(2)加班数据填写组件(含合规校验)

<!-- 数据填写组件:结构化输入+合规校验 -->

<template>

  <div class="fill-container">

    <h3>职场加班情况填写(仅做客观参考)</h3>

    <form @submit.prevent="submitData">

      <!-- 公司名称(必填,需全称) -->

      <div class="form-item">

        <label>公司全称 <span class="required">*</span></label>

        <input

          type="text"

          v-model="formData.companyName"

          placeholder="如:XX科技有限公司(需准确全称)"

          required

        >

        <p class="tip">填写全称可提高数据查询准确性</p>

      </div>

      <!-- 加班频率(下拉选择,标准化) -->

      <div class="form-item">

        <label>平均加班频率 <span class="required">*</span></label>

        <select v-model="formData.frequency" required>

          <option value="">请选择</option>

          <option value="几乎不加班">几乎不加班(每月≤2次)</option>

          <option value="每周1-2天">每周1-2天</option>

          <option value="每周3-4天">每周3-4天</option>

          <option value="每周5-6天">每周5-6天</option>

          <option value="每天加班">每天均需加班</option>

        </select>

      </div>

      <!-- 加班时长(下拉选择) -->

      <div class="form-item">

        <label>每日加班时长 <span class="required">*</span></label>

        <select v-model="formData.duration" required>

          <option value="">请选择</option>

          <option value="1小时内">1小时内</option>

          <option value="1-2小时">1-2小时</option>

          <option value="2-3小时">2-3小时</option>

          <option value="3小时以上">3小时以上</option>

        </select>

      </div>

      <!-- 压力评分(数值输入,量化) -->

      <div class="form-item">

        <label>工作压力评分(1-10分) <span class="required">*</span></label>

        <input

          type="number"

          v-model="formData.stressScore"

          min="1"

          max="10"

          placeholder="1=无压力,10=极度焦虑"

          required

        >

      </div>

      <!-- 补充说明(可选) -->

      <div class="form-item">

        <label>补充说明(可选)</label>

        <textarea

          v-model="formData.remark"

          rows="3"

          placeholder="如:是否有加班费、是否双休等"

        ></textarea>

      </div>

      <button type="submit" class="submit-btn">提交数据(仅用于客观参考)</button>

    </form>

  </div>

</template>

<script>

import axios from 'https://cdn.jsdelivr.net/npm/axios@0.27.2/dist/axios.min.js';

export default {

  data() {

    return {

      formData: {

        companyName: '',

        frequency: '',

        duration: '',

        stressScore: '',

        remark: ''

      }

    }

  },

  mounted() {

    // 校验是否已认证(未认证则跳转)

    if (localStorage.getItem('isAuth') !== 'true') {

      this.$router.push('/auth');

    }

  },

  methods: {

    submitData() {

      // 1. 基础数据校验

      if (!this.formData.companyName.trim()) {

        alert('请填写公司全称');

        return;

      }

      // 2. 提交数据(实际项目需加密请求)

      axios.post('/api/submit-overtime', {

        ...this.formData,

        submitTime: new Date().getTime() // 增加提交时间戳,避免重复提交

      }).then(res => {

        if (res.data.code === 200) {

          alert('数据提交成功!感谢你的客观分享');

          // 重置表单

          this.formData = { companyName: '', frequency: '', duration: '', stressScore: '', remark: '' };

        } else {

          alert('提交失败,请稍后重试');

        }

      }).catch(err => {

        console.error('提交异常:', err);

        alert('网络异常,请检查网络后重试');

      });

    }

  }

}

</script>

<style scoped>

.fill-container { max-width: 500px; margin: 2rem auto; padding: 2rem; border: 1px solid #eee; border-radius: 8px; }

.form-item { margin-bottom: 1.5rem; }

.required { color: #f56c6c; }

.submit-btn { width: 100%; padding: 0.8rem; background: #67c23a; color: #fff; border: none; border-radius: 4px; cursor: pointer; }

.tip { color: #999; font-size: 0.875rem; margin-top: 0.3rem; }

</style>

四、项目合规性与技术优化方向

1. 已落地的合规措施

  • 数据收集:仅收集 “必要且最小范围” 的信息(手机号用于验证,加班数据用于参考),无冗余收集;
  • 数据展示:无任何公司排名、加班时长对比功能,仅支持 “按公司名称查询历史填写结果”,且隐藏提交人信息;
  • 资质合规:借助企业资质推进备案,避免个人开发的合规风险,后续上线后会公示备案信息。

2. 后续技术优化计划

  1. 后端安全加固:增加 IP 限制(单 IP 每日最多提交 3 条数据),防止恶意刷量;
  2. 前端体验优化:用 Vuex 管理全局状态,替代 localStorage 存储认证状态,提升安全性;
  3. 数据可视化:后续增加 “行业加班趋势图表”(如 “互联网行业平均加班频率”),用 ECharts 实现,提供更直观的参考。

五、项目现状与技术交流

目前工具已完成前端核心功能开发,后端正在推进合规备案与接口开发,后续上线后会在我的 CSDN 主页更新技术文档与演示地址。

如果有 Vue 表单优化、数据加密相关的技术建议,欢迎在评论区交流 —— 个人开发精力有限,希望通过技术协作,让工具更实用、更安全。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐