Преглед на файлове

Merge branch 'dev-yc' into dev

flater преди 4 години
родител
ревизия
e56d4d30ae

BIN
assets/img/skill_cert/icon_arrow_right.png


BIN
assets/img/skill_cert/icon_checked.png


BIN
assets/img/skill_cert/icon_coffee.png


BIN
assets/img/skill_cert/icon_remove.png


BIN
assets/img/skill_cert/icon_service_wechat.png


BIN
assets/img/skill_cert/icon_tips.png


+ 130 - 0
components/flow/flow.vue

@@ -0,0 +1,130 @@
+<template>
+  <div class="flow-container">
+    <div class="flow">
+      <div class="item" v-for="(item,index) in dataList" :key="index">
+        <div class="top">
+          <div :class="[item.active?'line':'dash-line',index===0?'item--hidden':'']"></div>
+          <div :class="[item.active?'solid':'outline']">
+            <span :class="{'sort--active':item.active,'sort':true}">{{index+1}}</span>
+          </div>
+          <div :class="[item.active?'line':'dash-line',index===dataList.length-1?'item--hidden':'']"></div>
+        </div>
+        <span :class="{'bottom--active':item.active,'bottom':true}">
+          {{item.label}}
+        </span>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+  export default {
+    name: "flow",
+    props: {
+      dataList: {
+        type: Array,
+        default: []
+      }
+    },
+    data() {
+      return {}
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+  $line-width1: 53px;
+  $line-width2: 20px;
+
+  .flow-container {
+    display: flex;
+    justify-content: center;
+    width: 100%;
+    height: 147px;
+    box-shadow: 0px -0.5px 0px 0px rgba(0, 0, 0, 0.04) inset;
+
+    .flow {
+      display: flex;
+      align-items: center;
+
+      .item {
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+
+        &--hidden {
+          visibility: hidden;
+        }
+
+        .top {
+          display: flex;
+          align-items: center;
+
+          .line {
+            width: $line-width1;
+            @media(max-width: 768px) {
+              width: $line-width2;
+            }
+            height: 1px;
+            border: 1px solid #308eff;
+          }
+
+          .dash-line {
+            width: $line-width1;
+            @media(max-width: 768px) {
+              width: $line-width2;
+            }
+            height: 1px;
+            border: 0.5px dashed #dedede;
+          }
+
+          .solid {
+            display: flex;
+            justify-content: center;
+            align-items: center;
+            width: 28px;
+            height: 28px;
+            background: #308eff;
+            border-radius: 50%;
+          }
+
+          .sort {
+            font-size: 15px;
+            font-family: PingFangSC, PingFangSC-Semibold, sans-serif;
+            font-weight: 600;
+            color: #666666;
+            line-height: 21px;
+
+            &--active {
+              color: #ffffff;
+            }
+          }
+
+          .outline {
+            display: flex;
+            justify-content: center;
+            align-items: center;
+            width: 28px;
+            height: 28px;
+            background: #ffffff;
+            border: 1px solid #dedede;
+            border-radius: 50%;
+          }
+        }
+
+        .bottom {
+          margin-top: 5px;
+          font-size: 13px;
+          font-family: PingFangSC, PingFangSC-Semibold, sans-serif;
+          font-weight: 600;
+          color: #666666;
+          line-height: 21px;
+
+          &--active {
+            color: #222222;
+          }
+        }
+      }
+    }
+  }
+</style>

+ 73 - 0
components/skill_cert_header/skill_cert_header.vue

@@ -0,0 +1,73 @@
+<template>
+  <div class="skill-cert-header">
+    <div class="page-title">
+      <span class="text">{{title}}</span>
+      <span class="cert-status">未认证</span>
+    </div>
+    <div class="divider"></div>
+  </div>
+</template>
+
+<script>
+  export default {
+    name: "skill_cert_header",
+    props: {
+      title: {
+        type: String,
+        default: ''
+      }
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+  .skill-cert-header {
+    display: flex;
+    flex-direction: column;
+    width: 100%;
+    box-sizing: border-box;
+
+    .page-title {
+      width: 100%;
+      position: relative;
+      height: 75px;
+
+      .text {
+        position: absolute;
+        left: 50%;
+        top: 50%;
+        transform: translate(-50%, -50%);
+        font-size: 24px;
+        font-family: PingFangSC, PingFangSC-Semibold, sans-serif;
+        font-weight: 600;
+        color: #222222;
+        text-align: center;
+      }
+
+      .cert-status {
+        position: absolute;
+        width: 61px;
+        height: 30px;
+        opacity: 0.8;
+        background: #efefef;
+        border-radius: 4px;
+
+        font-size: 13px;
+        font-family: PingFangSC, PingFangSC-Regular, sans-serif;
+        font-weight: 400;
+        text-align: center;
+        color: #666666;
+        line-height: 30px;
+        right: 0;
+        top: 22px;
+      }
+    }
+
+    .divider {
+      width: 110%;
+      height: 2px;
+      border: 1px solid rgba(0, 0, 0, 0.06);
+      transform: translate(-4.5%, 0);
+    }
+  }
+</style>

+ 331 - 0
pages/frontend/skill_cert/cert_form.vue

@@ -0,0 +1,331 @@
+<template>
+  <div class="container">
+    <skillCertHeader title="全职自由工作者认证"></skillCertHeader>
+    <div class="description rule">
+      <div class="col-title">
+        <span class="label">认证说明</span>
+      </div>
+      <p class="content">
+        1. 程序员客栈签约通过 <br/>2. 每周有40小时+进行平台项目工作,非自由职业者无需申请; <br/>3. 需提交社保缴纳记录(或者其他能证明自由职业身份)的截图;<br/>4.
+        如果近3个月如果有社保缴费记录,请备注缴费来源,否则不过 <br/>5. 确认诚信保证书+相关条约; <br/>6. 无纠纷记录,差评,能及时响应客户; <br/>7. 良好的责任心,工作态度和习惯,沟通能力 <br/>8.
+        客栈头像为本人露脸照片
+        <br/>9. 认证失败费用无法退回,请确保已理解所有内容 <br/>有效期【180天】<br/>审核时间:2个工作日内
+      </p>
+    </div>
+    <div class="description condition">
+      <div class="col-title">
+        <span class="label">认证权益</span>
+      </div>
+      <p class="content">
+        1. 会优先对接给需要全职开发者的客户 <br/>2. 拥有单独展示列表,获得更多曝光机会 <br/>3. 认证后派单权重提高 <br/>4. 拥有专属标志,增加客户信任感 <br/>5. 全职认证+会员可以同时接2单
+      </p>
+    </div>
+    <div class="col-title cert-flow">
+      <span class="label">认证流程</span>
+    </div>
+    <div class="content cert-flow-content">
+      1.认证失败费用无法退回,请确保已理解以上内容。 <br/>2.确保个人通过签约的资料真实有效。 <br/>3.对远程工作有极大的热情,乐于参与其中并为雇主创造价值。
+    </div>
+    <div class="improve_profile">
+      <span class="label">1、完善个人主页</span>
+      <img class="icon" src="@/assets/img/skill_cert/icon_arrow_right.png" alt=""/>
+    </div>
+    <div class="security">
+      <span class="security-title">2、上传三个月内的社保证明截图</span>
+      <span class="label">如何查询</span>
+      <img class="icon" src="@/assets/img/skill_cert/icon_tips.png" alt=""/>
+    </div>
+    <div class="width-infinity">
+      <el-upload
+        class="upload-demo"
+        drag
+        action="https://jsonplaceholder.typicode.com/posts/"
+        :on-success="handleSuccess"
+        :before-upload="beforeAvatarUpload"
+        multiple>
+        <i class="el-icon-upload"></i>
+        <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
+        <div class="el-upload__tip" slot="tip">支持2M以内的PNG、JPG、JPEG格式的图片</div>
+      </el-upload>
+    </div>
+    <span class="promise">3、承诺保证</span>
+    <div class="description promise-content">
+      <p class="title">承诺保证书</p>
+      <p class="label">本人承诺在【程序员客栈自由工作】认证过程中提交的信息真实有效,如因此给其他组织或个人(含本人)造成损失,一切本人承担</p>
+    </div>
+    <div class="agree-action">
+      <img class="icon" src="@/assets/img/skill_cert/icon_checked.png" alt=""/>
+      <span class="label">我已阅读并同意承诺保证书</span>
+    </div>
+    <el-input class="input" type="textarea" placeholder="请输入..." :autosize="{ minRows: 8}"
+              v-model="form.desc"></el-input>
+    <div class="buttons">
+      <el-button class="submit" type="primary" @click="submit">申请认证</el-button>
+    </div>
+  </div>
+</template>
+
+<script>
+  import skillCertHeader from "@/components/skill_cert_header/skill_cert_header";
+
+  export default {
+    name: "cert_form",
+    components: {
+      skillCertHeader
+    },
+    data() {
+      return {
+        form: {
+          desc: ''
+        }
+      }
+    },
+    methods: {
+      handleSuccess(res, file) {
+        this.imageUrl = URL.createObjectURL(file.raw);
+      },
+      beforeAvatarUpload(file) {
+        const isJPG = file.type === 'image/jpeg';
+        const isPNG = file.type === 'image/png';
+        const isLt2M = file.size / 1024 / 1024 < 2;
+
+        if (!(isJPG || isPNG)) {
+          this.$message.error('上传图片只能是 PNG/JPG/JPEG 格式!');
+        }
+        if (!isLt2M) {
+          this.$message.error('上传图片大小不能超过 2MB!');
+        }
+        return isJPG && isLt2M;
+      },
+      submit() {
+      }
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+  .container {
+    display: flex;
+    flex-direction: column;
+    width: 100%;
+    max-width: 1000px;
+    margin: 10px auto;
+    background: #ffffff;
+    border-radius: 10px;
+    padding: 0 85px;
+    @media(max-width: 768px) {
+      padding: 0 15px;
+    }
+    box-sizing: border-box;
+
+    .width-infinity {
+      width: 100%;
+    }
+
+    .content {
+      margin: 14px 14px 0 14px;
+      font-size: 15px;
+      font-family: PingFangSC, PingFangSC-Regular, sans-serif;
+      font-weight: 400;
+      text-align: left;
+      color: #222222;
+      line-height: 24px;
+    }
+
+    .description {
+      width: 100%;
+      background: #f4f5f9;
+      border-radius: 8px;
+      padding-bottom: 32px;
+    }
+
+    .rule {
+      margin-top: 20px;
+      margin-bottom: 10px;
+    }
+
+    .condition {
+      .item {
+        display: flex;
+        margin-top: 17px;
+
+        .text {
+          margin-left: 14px;
+          font-size: 15px;
+          font-family: PingFangSC, PingFangSC-Regular, sans-serif;
+          font-weight: 400;
+          text-align: left;
+          color: #222222;
+          line-height: 24px;
+        }
+
+        .action {
+          margin-left: 28px;
+          font-size: 15px;
+          font-family: PingFangSC, PingFangSC-Semibold, sans-serif;
+          font-weight: 600;
+          text-align: left;
+          color: #308eff;
+          line-height: 24px;
+        }
+      }
+    }
+
+    .col-title {
+      display: inline-flex;
+      align-items: center;
+      height: 43px;
+
+      &::before {
+        content: '';
+        width: 2px;
+        height: 14px;
+        background: #308eff;
+        border-radius: 2px;
+      }
+
+      .label {
+        font-size: 16px;
+        font-family: PingFangSC, PingFangSC-Semibold, sans-serif;
+        font-weight: 600;
+        color: #222222;
+        padding: 0 10px;
+      }
+    }
+
+    .cert-flow {
+      width: 100%;
+      margin-top: 19px;
+      margin-bottom: 4px;
+    }
+
+    .cert-flow-content {
+      width: 100%;
+      color: #999999;
+    }
+
+    .improve_profile {
+      display: flex;
+      align-items: center;
+      width: 100%;
+      margin-top: 22px;
+
+      .label {
+        font-size: 16px;
+        font-family: PingFangSC, PingFangSC-Medium, sans-serif;
+        font-weight: 500;
+        text-align: left;
+        color: #308eff;
+        line-height: 24px;
+      }
+
+      .icon {
+        width: 13px;
+        height: 13px;
+      }
+    }
+
+    .security {
+      display: flex;
+      align-items: center;
+      width: 100%;
+      margin-top: 22px;
+      margin-bottom: 14px;
+
+      .security-title {
+        font-size: 16px;
+        font-family: PingFangSC, PingFangSC-Medium, sans-serif;
+        font-weight: 500;
+        text-align: left;
+        color: #222222;
+        line-height: 24px;
+      }
+
+      .label {
+        margin-left: 10px;
+        font-size: 14px;
+        font-family: PingFangSC, PingFangSC-Medium, sans-serif;
+        font-weight: 500;
+        text-align: left;
+        color: #308eff;
+        line-height: 20px;
+      }
+
+      .icon {
+        width: 16px;
+        height: 16px;
+      }
+    }
+
+    .promise {
+      width: 100%;
+      margin-top: 32px;
+      margin-bottom: 20px;
+      font-size: 16px;
+      font-family: PingFangSC, PingFangSC-Medium, sans-serif;
+      font-weight: 500;
+      text-align: left;
+      color: #222222;
+      line-height: 24px;
+    }
+
+    .promise-content {
+      padding: 43px;
+
+      .title {
+        font-size: 21px;
+        font-family: PingFangSC, PingFangSC-Medium, sans-serif;
+        font-weight: 500;
+        text-align: center;
+        color: #222222;
+        line-height: 24px;
+      }
+
+      .label {
+        margin-top: 13px;
+        font-size: 14px;
+        font-family: PingFangSC, PingFangSC-Regular, sans-serif;
+        font-weight: 400;
+        text-align: center;
+        color: #222222;
+        line-height: 24px;
+      }
+    }
+
+    .agree-action {
+      display: flex;
+      align-items: center;
+      width: 100%;
+      margin-top: 14px;
+      margin-bottom: 22px;
+
+      .icon {
+        width: 20px;
+        height: 20px;
+      }
+
+      .label {
+        margin-left: 4px;
+      }
+    }
+
+    .buttons {
+      width: 100%;
+
+      .submit {
+        width: 208px;
+        height: 58px;
+        margin-top: 22px;
+        margin-bottom: 49px;
+        background: #308eff;
+        border-radius: 8px;
+
+        font-size: 18px;
+        font-family: PingFangSC, PingFangSC-Medium, sans-serif;
+        font-weight: 500;
+        color: #ffffff;
+        line-height: 26px;
+      }
+    }
+  }
+</style>

+ 637 - 0
pages/frontend/skill_cert/profile.vue

@@ -0,0 +1,637 @@
+<template>
+  <div class="container">
+    <skillCertHeader title="技术等级认证"></skillCertHeader>
+    <flow :dataList="flowList"></flow>
+    <div class="description rule">
+      <div class="col-title">
+        <span class="label">评级细则</span>
+      </div>
+      <div class="rule-content">
+        <div class="text" v-for="(item,index) in rules" :key="index">
+          <p class="text-title">{{item.title}}</p>
+          <a v-if="item.linkUrl" class="text-content text-content--a" :href="item.linkUrl" target="view_window">{{item.content}}</a>
+          <p v-else class="text-content" v-html="item.content"></p>
+        </div>
+      </div>
+    </div>
+    <div class="description condition">
+      <div class="col-title">
+        <span class="label">申请条件</span>
+      </div>
+      <div class="item" v-for="(item,index) in conditions" :key="index">
+        <span class="text">{{item.text}}</span>
+        <a v-if="item.linkUrl" class="action" :href="item.linkUrl">{{item.linkName}}</a>
+      </div>
+    </div>
+    <div class="col-title width-infinity">
+      <span class="label">填写表单</span>
+    </div>
+    <div class="field-selector">
+      <span class="selector-title">选择领域:</span>
+      <div :class="['fields',item.disabled?'tag--disabled':'']" v-for="(item) in fields" :key="item.occupation_id"
+           @click="fieldSelected(item.occupation_id)">
+        <div class="tag" v-if="item.selected">
+          <span class="text">{{item.occupation_name}}</span>
+        </div>
+        <div class="tag tag--unchecked" v-else>
+          <span class="text text--unchecked">{{item.occupation_name}}</span>
+        </div>
+      </div>
+    </div>
+    <div class="tag-selector">
+      <span class="selector-title">选择标签:</span>
+      <div class="tag" v-for="(item,index) in tagsSelected" :key="index">
+        <span class="text">{{item.skill_name}}</span>
+        <img class="icon" src="@/assets/img/skill_cert/icon_remove.png" alt="" @click="onRemoveTag(index)"/>
+      </div>
+      <el-input class="tag-input" v-model="tagText" placeholder="请输入自定义标签" clearable @change="onInputChange"></el-input>
+    </div>
+    <div class="tags">
+      <div class="tag" v-for="(item) in tags" :key="item.skill_id" @click="onTagSelected(item)">
+        <span class="text">{{item.skill_name}}</span>
+      </div>
+    </div>
+    <div class="level-selector" @click="toLevelsDesc">
+      <div class="col-title">
+        <span class="label">选择认证等级</span>
+      </div>
+      <span class="action">等级对照表</span>
+      <img class="icon" src="@/assets/img/skill_cert/icon_tips.png" alt=""/>
+    </div>
+    <div class="level-selector">
+      <el-select v-model="level" placeholder="认证等级" clearable @change="handleLevelChange">
+        <el-option
+          v-for="dict in levels"
+          :key="dict.item_id"
+          :label="dict.name"
+          :value="dict.item_id"
+        />
+      </el-select>
+    </div>
+    <div class="col-title width-infinity upload-title">
+      <span class="label">上传薪资证明</span>
+    </div>
+    <div class="width-infinity">
+      <el-upload
+        class="upload-demo"
+        drag
+        action="/upload_image"
+        :on-success="handleSuccess"
+        :on-error="handleError"
+        :on-remove="handleRemove"
+        :before-upload="beforeAvatarUpload"
+        :on-preview="handlePreview"
+        :file-list="fileList"
+        list-type="picture"
+        accept=".jpg,.jpeg,.png,.JPG,.PNG,.JPEG"
+        with-credentials
+        multiple>
+        <i class="el-icon-upload"></i>
+        <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
+        <div class="el-upload__tip" slot="tip">支持2M以内的PNG、JPG、JPEG格式的图片,F5、F6需要提供银行流水截图(F5:40W以上薪资;F6:70W以上薪资)</div>
+      </el-upload>
+    </div>
+    <div class="price">
+      <span class="text">¥{{price}}</span>
+      <span class="unit"> /次</span>
+    </div>
+    <div class="buttons">
+      <el-button class="submit" type="primary" @click="submit">确认提交</el-button>
+    </div>
+    <el-dialog :visible.sync="dialogVisible">
+      <img width="100%" :src="dialogImageUrl" alt="">
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+  import flow from "@/components/flow/flow";
+  import skillCertHeader from "@/components/skill_cert_header/skill_cert_header";
+
+  export default {
+    name: "profile",
+    components: {
+      flow, skillCertHeader
+    },
+    data() {
+      return {
+        rules: [{
+          title: '1、认证优势',
+          content: '* 进行过技术认证的用户,将享受平台派单的优先推荐权重' +
+            '<br/>' +
+            '* 技术认证等级标识' +
+            '<br/>' +
+            '* 依据技术等级将有更高的参考薪酬' +
+            '<br/>' +
+            '* 认证4级以上用户有机会被内邀为面试官' +
+            '<br/>' +
+            '* 面试过程中,将与高水平技术官的进行深层次沟通',
+          linkUrl: ''
+        }, {
+          title: '2、等级对照表',
+          content: '《客栈技术等级对照表》',
+          linkUrl: 'https://jishuin.proginn.com/p/763bfbd71939'
+        }, {
+          title: '3、其他',
+          content: '* 最终认证的技术等级以面试官反馈为准' + '<br/>' +
+            '* 认证金额无法退回,请确保已理解以上内容。',
+          linkUrl: ''
+        },],
+        dialogImageUrl: '',
+        dialogVisible: false,
+        levels: [],
+        conditions: [],
+        fields: [],
+        tagsSelected: [],
+        tags: [],
+        flowList: [
+          {
+            active: true,
+            label: '基础信息'
+          }, {
+            active: false,
+            label: '完善简历'
+          }, {
+            active: false,
+            label: '对接面试官'
+          }, {
+            active: false,
+            label: '开始面试'
+          }, {
+            active: false,
+            label: '结束认证'
+          },
+        ],
+        tagText: '',
+        form: {
+          skill: []
+        },
+        fileList: [],
+        level: '',
+        price: ''
+      };
+    },
+    created() {
+      this.conditions = [
+        {
+          text: '* 签约程序员客栈',
+          linkName: "去签约",
+          linkUrl: "https://www.proginn.com/sign/new",
+        }, {
+          text: '* 有助于面试官快速了解你',
+          linkName: "完善简历",
+          linkUrl: `/wo/${this.userinfo.uid}`,
+        }, {
+          text: '* 客栈头像为本人露脸照片',
+          linkName: "",
+          linkUrl: "",
+        }
+      ]
+    },
+    methods: {
+      showPaySuccess() {
+        this.$router.push(`/frontend/skill_cert/resume_improve`);
+      },
+      toLevelsDesc() {
+        window.open(`https://jishuin.proginn.com/p/763bfbd71939`)
+      },
+      onTagSelected(item) {
+        if (this.tagsSelected.find((ele) => {
+          return ele.skill_name === item.skill_name;
+        })) {
+          return
+        }
+        this.tagsSelected.push(item);
+      },
+      onRemoveTag(index) {
+        this.tagsSelected.splice(index, 1);
+      },
+      fieldSelected(id) {
+        this.fields = this.fields.map((ele) => {
+          ele.selected = ele.occupation_id === id;
+          return ele;
+        })
+        this.getSkills()
+        this.getLevels()
+      },
+      onInputChange(value) {
+        if (!value) return;
+        if (this.tagsSelected.find((ele) => {
+          return ele.skill_name === value;
+        })) {
+          return
+        }
+        this.tagsSelected.push({
+          skill_id: 0,
+          skill_name: value
+        });
+        this.tagText = '';
+      },
+      handleLevelChange(value) {
+        const res = this.levels.find((ele) => {
+          return ele.item_id === value
+        });
+        this.price = res.price
+      },
+      handleSuccess(res, file) {
+        this.fileList.push(file);
+      },
+      handleError(err) {
+        console.log(err)
+      },
+      beforeAvatarUpload(file) {
+        const isJPEG = file.type === 'image/jpeg';
+        const isJPG = file.type === 'image/jpg';
+        const isPNG = file.type === 'image/png';
+        const isLt2M = file.size / 1024 / 1024 < 2;
+
+        if (!(isJPG || isPNG || isJPEG)) {
+          this.$message.error('上传图片只能是 PNG/JPG/JPEG 格式!');
+        }
+        if (!isLt2M) {
+          this.$message.error('上传图片大小不能超过 2MB!');
+        }
+        return (isJPG || isJPEG || isPNG) && isLt2M;
+      },
+      handleRemove(file, fileList) {
+        const index = this.fileList.indexOf(file);
+        this.fileList.splice(index, 1);
+      },
+      handlePreview(file) {
+        this.dialogImageUrl = file.url;
+        this.dialogVisible = true;
+      },
+      submit() {
+        if (!this.tagsSelected.length) {
+          this.$message.error('请选择技能标签!');
+          return
+        }
+        if (!this.level) {
+          this.$message.error('请选择认证等级!');
+          return
+        }
+        if (!this.fileList.length) {
+          this.$message.error('请上传薪资证明!');
+          return
+        }
+        const form = {
+          product_type: 12,
+          item_id: this.level,
+          occupation_id: this.fields.find((ele) => ele.selected).occupation_id,
+          skill: JSON.stringify(this.tagsSelected),
+          file: this.fileList.map((ele) => ele.response.filename).join(','),
+        };
+        this.$axios.$post(`/uapi/cert/add`, form).then((value) => {
+          location.href = `/pay?product_type=12&product_id=${form.item_id}&next=frontend/skill_cert/resume_improve`;
+        });
+      },
+      async getOccupation() {
+        let res = await this.$axios.$post(`/wapi/pub/occupation`, {type: 2});
+        if (res.data.list && res.data.list.length) {
+          res.data.list[0].selected = true;
+        }
+        this.fields = res.data.list;
+      },
+      async getSkills() {
+        const id = this.fields.find((ele) => ele.selected).occupation_id;
+        let res = await this.$axios.$post(`/wapi/pub/skill`, {occupation_id: id});
+        this.tags = res.data.list;
+      },
+      async getLevels() {
+        const id = this.fields.find((ele) => ele.selected).occupation_id;
+        let res = await this.$axios.$post(`/uapi/pub/freeworklevel`, {occupation_id: id});
+        this.levels = res.data.list.map((ele) => {
+          ele.name += ` ¥${ele.price}`;
+          return ele
+        });
+        if (!this.level && this.levels.length) {
+          this.level = `${this.levels[0].item_id}`
+        }
+        this.price = this.levels.find((ele) => {
+          return ele.item_id === this.level;
+        }).price || '0.00';
+      },
+      async getDetail() {
+        let res = await this.$axios.$post(`/uapi/cert/info`);
+        this.form = res.data;
+        this.flowList = this.flowList.map((ele, index) => {
+          if (index < parseInt(this.form.step)) {
+            ele.selected = true;
+          }
+          return ele;
+        })
+        if (this.form.occupation_id) {
+          this.fields = this.fields.map((ele) => {
+            ele.selected = this.form.occupation_id === ele.occupation_id;
+            return ele;
+          });
+        }
+        if (this.form.item_id) {
+          this.level = `${this.form.item_id}`;
+        }
+        this.tagsSelected = this.form.skill;
+        if (this.form.img) {
+          this.fileList = this.form.img.split(',').map((ele) => {
+            const arr = ele.split("/")
+            const imageUrl = arr[arr.length - 1]
+            return {
+              name: imageUrl,
+              url: this.form.img,
+              response: {
+                filename: imageUrl
+              }
+            }
+          })
+        }
+      },
+      bindCallback() {
+        if (this.mobile) {
+          window.showPaySuccess = this.showPaySuccess
+        }
+      },
+    },
+    async mounted() {
+      await this.getOccupation();
+      await this.getDetail();
+      await this.getLevels();
+      await this.getSkills();
+      this.bindCallback();
+    }
+  }
+</script>
+<style lang="scss" scoped>
+  .container {
+    display: flex;
+    width: 100%;
+    max-width: 1000px;
+    margin: 10px auto;
+    background: #ffffff;
+    border-radius: 10px;
+    padding: 0 85px;
+    @media(max-width: 768px) {
+      padding: 0 15px;
+    }
+    box-sizing: border-box;
+  }
+
+  .width-infinity {
+    width: 100%;
+  }
+
+  .description {
+    width: 100%;
+    background: #f4f5f9;
+    border-radius: 8px;
+    padding-bottom: 32px;
+  }
+
+  .rule {
+    margin-top: 20px;
+    margin-bottom: 10px;
+
+    .rule-content {
+      display: flex;
+      flex-direction: column;
+      margin: 16px 14px 0 14px;
+
+      .text {
+        display: flex;
+        flex-direction: column;
+
+        &:not(:first-child) {
+          margin-top: 17px;
+        }
+
+        .text-title {
+          font-size: 15px;
+          font-family: PingFangSC, PingFangSC-Semibold, sans-serif;
+          font-weight: 600;
+          color: #222222;
+          line-height: 24px;
+        }
+
+        .text-content {
+          margin-top: 5px;
+          font-size: 15px;
+          font-family: PingFangSC, PingFangSC-Regular, sans-serif;
+          font-weight: 400;
+          color: #222222;
+          line-height: 24px;
+
+          &--a {
+            color: var(--linkColor)
+          }
+        }
+      }
+    }
+  }
+
+  .condition {
+    .item {
+      display: flex;
+      flex-wrap: wrap;
+      margin-top: 17px;
+
+      .text {
+        margin-left: 14px;
+        font-size: 15px;
+        font-family: PingFangSC, PingFangSC-Regular, sans-serif;
+        font-weight: 400;
+        text-align: left;
+        color: #222222;
+        line-height: 24px;
+      }
+
+      .action {
+        margin-left: 28px;
+        margin-right: 14px;
+        font-size: 15px;
+        font-family: PingFangSC, PingFangSC-Semibold, sans-serif;
+        font-weight: 600;
+        text-align: left;
+        color: #308eff;
+        line-height: 24px;
+      }
+    }
+  }
+
+  .col-title {
+    display: inline-flex;
+    align-items: center;
+    height: 43px;
+
+    &::before {
+      content: '';
+      width: 2px;
+      height: 14px;
+      background: #308eff;
+      border-radius: 2px;
+    }
+
+    .label {
+      font-size: 16px;
+      font-family: PingFangSC, PingFangSC-Semibold, sans-serif;
+      font-weight: 600;
+      color: #222222;
+      padding: 0 10px;
+    }
+  }
+
+  .field-selector {
+    display: flex;
+    align-items: center;
+    flex-wrap: wrap;
+    width: 100%;
+    margin-top: 33px;
+  }
+
+  .tag-selector {
+    display: flex;
+    align-items: center;
+    flex-wrap: wrap;
+    width: 100%;
+    margin-top: 23px;
+    margin-bottom: 12px;
+
+    .tag-input {
+      width: 200px;
+      margin-bottom: 10px;
+    }
+  }
+
+  .selector-title {
+    font-size: 14px;
+    font-family: PingFangSC, PingFangSC-Semibold, sans-serif;
+    font-weight: 600;
+    text-align: left;
+    color: #222222;
+    line-height: 20px;
+    margin-right: 16px;
+    margin-bottom: 10px;
+  }
+
+  .tag {
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    height: 38px;
+    background: #308eff;
+    border-radius: 4px;
+    padding: 0 10px;
+    margin-right: 7px;
+    margin-bottom: 10px;
+    cursor: pointer;
+
+    &--disabled {
+      cursor: not-allowed;
+      pointer-events: none;
+    }
+
+    &--unchecked {
+      opacity: 0.8;
+      background: #f4f5f9;
+      border-radius: 4px;
+    }
+
+    .text {
+      font-size: 14px;
+      font-family: PingFangSC, PingFangSC-Regular, sans-serif;
+      font-weight: 400;
+      color: #ffffff;
+
+      &--unchecked {
+        opacity: 0.8;
+        font-size: 14px;
+        font-weight: 400;
+        color: #666666;
+        line-height: 20px;
+      }
+    }
+
+    .icon {
+      width: 16px;
+      height: 16px;
+      margin-left: 6px;
+    }
+  }
+
+  .tags {
+    display: flex;
+    align-content: flex-start;
+    flex-wrap: wrap;
+    width: 100%;
+    background: #ffffff;
+    border: 1px solid #d7dfe8;
+    border-radius: 8px;
+    padding: 20px;
+    margin-bottom: 10px;
+  }
+
+  .level-selector {
+    display: flex;
+    align-items: center;
+    width: 100%;
+    margin-top: 12px;
+
+    .action {
+      margin-left: 15px;
+      font-size: 14px;
+      font-family: PingFangSC, PingFangSC-Medium, sans-serif;
+      font-weight: 500;
+      color: #308eff;
+      line-height: 20px;
+      cursor: pointer;
+    }
+
+    .icon {
+      width: 16px;
+      height: 16px;
+      cursor: pointer;
+    }
+  }
+
+  .upload-title {
+    margin-top: 51px;
+    margin-bottom: 14px;
+  }
+
+  .price {
+    display: flex;
+    align-items: baseline;
+    width: 100%;
+    margin-top: 42px;
+    margin-bottom: 20px;
+
+    .text {
+      font-size: 30px;
+      font-family: DIN Alternate, sans-serif;
+      font-weight: 600;
+      color: #ff6600;
+    }
+
+    .unit {
+      font-size: 16px;
+      font-family: PingFangSC, PingFangSC-Regular, sans-serif;
+      font-weight: 600;
+      color: #ff6600;
+    }
+  }
+
+  .buttons {
+    width: 100%;
+
+    .submit {
+      margin-bottom: 87px;
+      width: 267px;
+      height: 58px;
+      background: #308eff;
+      border-radius: 8px;
+
+      font-size: 18px;
+      font-family: PingFangSC, PingFangSC-Medium, sans-serif;
+      font-weight: 500;
+      color: #ffffff;
+      line-height: 26px;
+    }
+  }
+</style>

+ 98 - 0
pages/frontend/skill_cert/ready_interview.vue

@@ -0,0 +1,98 @@
+<template>
+  <div class="container">
+    <skillCertHeader title="技术等级认证"></skillCertHeader>
+    <flow :dataList="flowList"></flow>
+    <p class="status">工作人员正在审核, 审核通过后将为您对接面试官</p>
+    <p class="time">时间: 1-3个工作日</p>
+    <img class="qr-code" src="@/assets/img/skill_cert/icon_service_wechat.png" alt=""/>
+    <p class="label">微信客服</p>
+  </div>
+</template>
+
+<script>
+  import flow from "@/components/flow/flow";
+  import skillCertHeader from "@/components/skill_cert_header/skill_cert_header";
+
+  export default {
+    name: "ready_interview",
+    components: {
+      flow, skillCertHeader
+    },
+    data() {
+      return {
+        flowList: [
+          {
+            active: true,
+            label: '基础信息'
+          }, {
+            active: true,
+            label: '完善简历'
+          }, {
+            active: true,
+            label: '对接面试官'
+          }, {
+            active: false,
+            label: '开始面试'
+          }, {
+            active: false,
+            label: '结束认证'
+          },
+        ],
+      }
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+  .container {
+    display: flex;
+    flex-direction: column;
+    width: 100%;
+    max-width: 1000px;
+    margin: 10px auto;
+    background: #ffffff;
+    border-radius: 10px;
+    padding: 0 85px;
+    @media(max-width: 768px) {
+      padding: 0 15px;
+    }
+    box-sizing: border-box;
+
+    .status {
+      margin-top: 43px;
+      margin-bottom: 11px;
+      font-size: 18px;
+      font-family: PingFangSC, PingFangSC-Regular, sans-serif;
+      font-weight: 400;
+      text-align: center;
+      color: #222222;
+      line-height: 25px;
+    }
+
+    .time {
+      font-size: 18px;
+      font-family: PingFangSC, PingFangSC-Regular, sans-serif;
+      font-weight: 400;
+      text-align: center;
+      color: #308eff;
+      line-height: 25px;
+    }
+
+    .qr-code {
+      width: 286px;
+      height: 286px;
+      margin-top: 6px;
+      margin-bottom: -9px;
+    }
+
+    .label {
+      margin-bottom: 43px;
+      font-size: 14px;
+      font-family: PingFangSC, PingFangSC-Regular, sans-serif;
+      font-weight: 400;
+      text-align: right;
+      color: #666666;
+      line-height: 14px;
+    }
+  }
+</style>

+ 121 - 0
pages/frontend/skill_cert/resume_improve.vue

@@ -0,0 +1,121 @@
+<template>
+  <div class="container">
+    <skillCertHeader title="技术等级认证"></skillCertHeader>
+    <flow :dataList="flowList"></flow>
+    <div class="page-content">
+      <p class="tips col1">
+        1、<a :href="`/wo/${userinfo.uid}`" target="view_window">完善简历</a>,有助于面试官快速了解你哦
+      </p>
+      <p class="tips">2、填写可面试时间</p>
+      <el-input class="input" type="textarea" placeholder="请输入..." :autosize="{ minRows: 8}"
+                v-model="memo"></el-input>
+      <el-button class="submit" type="primary" @click="submit">确认提交</el-button>
+      <p class="bottom-tips">* 点击提交后,工作人员将和您确认时间并为您对接面试官</p>
+    </div>
+  </div>
+</template>
+
+<script>
+  import flow from "@/components/flow/flow";
+  import skillCertHeader from "@/components/skill_cert_header/skill_cert_header";
+
+  export default {
+    name: "resume_improve",
+    components: {
+      flow, skillCertHeader
+    },
+    data() {
+      return {
+        flowList: [
+          {
+            active: true,
+            label: '基础信息'
+          }, {
+            active: true,
+            label: '完善简历'
+          }, {
+            active: false,
+            label: '对接面试官'
+          }, {
+            active: false,
+            label: '开始面试'
+          }, {
+            active: false,
+            label: '结束认证'
+          },
+        ],
+        memo: ''
+      }
+    },
+    methods: {
+      submit() {
+        this.$axios.$post(`uapi/cert/memo`, {memo: this.memo}).then((value) => {
+          this.$router.push(`/frontend/skill_cert/ready_interview`);
+        });
+      }
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+  .container {
+    display: flex;
+    flex-direction: column;
+    width: 100%;
+    max-width: 1000px;
+    margin: 10px auto;
+    background: #ffffff;
+    border-radius: 10px;
+    padding: 0 85px;
+    @media(max-width: 768px) {
+      padding: 0 15px;
+    }
+    box-sizing: border-box;
+
+    .page-content {
+      display: flex;
+      flex-direction: column;
+      width: 100%;
+      padding: 0 161px;
+      @media(max-width: 768px) {
+        padding: 0;
+      }
+
+      .tips {
+        width: 100%;
+        font-size: 16px;
+        font-family: PingFangSC, PingFangSC-Regular, sans-serif;
+        font-weight: 400;
+        text-align: left;
+        color: #222222;
+        line-height: 22px;
+        margin-top: 20px;
+      }
+
+      .col1 {
+        margin-top: 57px;
+      }
+
+      .input {
+        margin-top: 17px;
+      }
+
+      .submit {
+        width: 100%;
+        height: 51px;
+        margin-top: 17px;
+      }
+
+      .bottom-tips {
+        width: 100%;
+        margin-top: 12px;
+        margin-bottom: 85px;
+        font-size: 14px;
+        font-family: PingFangSC, PingFangSC-Regular, sans-serif;
+        font-weight: 400;
+        color: #919aa7;
+        line-height: 14px;
+      }
+    }
+  }
+</style>

+ 86 - 0
pages/frontend/skill_cert/start_interview.vue

@@ -0,0 +1,86 @@
+<template>
+  <div class="container">
+    <skillCertHeader title="技术等级认证"></skillCertHeader>
+    <flow :dataList="flowList"></flow>
+    <img class="icon" src="@/assets/img/skill_cert/icon_coffee.png" alt=""/>
+    <p class="status">已对接面试官,注意查看通知</p>
+    <p class="tips">如已面试,面试结果将在1-3个工作日通知给您</p>
+  </div>
+</template>
+
+<script>
+  import flow from "@/components/flow/flow";
+  import skillCertHeader from "@/components/skill_cert_header/skill_cert_header";
+
+  export default {
+    name: "start_interview",
+    components: {
+      flow, skillCertHeader
+    },
+    data() {
+      return {
+        flowList: [
+          {
+            active: true,
+            label: '基础信息'
+          }, {
+            active: true,
+            label: '完善简历'
+          }, {
+            active: true,
+            label: '对接面试官'
+          }, {
+            active: true,
+            label: '开始面试'
+          }, {
+            active: false,
+            label: '结束认证'
+          },
+        ],
+      }
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+  .container {
+    display: flex;
+    flex-direction: column;
+    width: 100%;
+    max-width: 1000px;
+    margin: 10px auto;
+    background: #ffffff;
+    border-radius: 10px;
+    padding: 0 85px;
+    @media(max-width: 768px) {
+      padding: 0 15px;
+    }
+    box-sizing: border-box;
+
+    .icon {
+      width: 86px;
+      height: 78px;
+      margin-top: 54px;
+    }
+
+    .status{
+      margin-top: 36px;
+      margin-bottom: 7px;
+      font-size: 18px;
+      font-family: PingFangSC, PingFangSC-Semibold,sans-serif;
+      font-weight: 600;
+      text-align: right;
+      color: #222222;
+      line-height: 25px;
+    }
+
+    .tips{
+      font-size: 14px;
+      font-family: PingFangSC, PingFangSC-Regular,sans-serif;
+      font-weight: 400;
+      text-align: left;
+      color: #999999;
+      line-height: 20px;
+    }
+  }
+</style>

+ 43 - 18
plugins/seoRouter.js

@@ -33,11 +33,11 @@ const extendRoutes = (routes, resolve) => {
       name: 'JobListSeoDetail',
       path: '/job/d/:id?',
       component: resolve(__dirname, '../pages/job/detail/_id.vue')
-    },{
+    }, {
       name: 'CompanySeoList',
       path: '/job/company/list/:city?',
       component: resolve(__dirname, '../pages/job/company/list/_city.vue')
-    },{
+    }, {
       name: 'JobListSeoIndex',
       path: '/job/*',
       component: resolve(__dirname, '../pages/job/index.vue')
@@ -48,15 +48,15 @@ const extendRoutes = (routes, resolve) => {
       name: 'SeoCompanyList',
       path: '/company',
       component: resolve(__dirname, '../pages/company/list.vue')
-    },{
+    }, {
       name: 'SeoCompanyList_0',
       path: '/company/',
       component: resolve(__dirname, '../pages/company/list.vue')
-    },{
+    }, {
       name: 'SeoCompanyList_1',
       path: '/company/*',
       component: resolve(__dirname, '../pages/company/list.vue')
-    },{
+    }, {
       name: 'SeoCompanyDetail',
       path: '/companyDetail/:id?',
       component: resolve(__dirname, '../pages/company/detail/_id.vue')
@@ -82,52 +82,77 @@ const extendRoutes = (routes, resolve) => {
       name: 'SeoSkillList_0',
       path: '/frontend/skill/list/*',
       component: resolve(__dirname, '../pages/frontend/skill/list.vue')
-    },{
+    }, {
       name: 'SeoConsultList_0',
       path: '/frontend/consult/list/*',
       component: resolve(__dirname, '../pages/frontend/consult/list.vue')
-    },{
+    }, {
       name: 'SeoLearnList_0',
       path: '/frontend/learn/list/*',
       component: resolve(__dirname, '../pages/frontend/learn/list.vue')
-    },{
+    }, {
       name: 'SeoLearnList',
       path: '/learn',
       component: resolve(__dirname, '../pages/frontend/learn/list.vue')
-    },{
+    }, {
       name: 'SeoLearnList_1',
       path: '/learn/*',
       component: resolve(__dirname, '../pages/frontend/learn/list.vue')
-    },{
+    }, {
       name: 'SeoLearnDetail',
       path: '/l/:id?',
       component: resolve(__dirname, '../pages/frontend/learn/detail/_id.vue')
-    },{
+    }, {
       name: 'SeoConsultList',
       path: '/consult',
       component: resolve(__dirname, '../pages/frontend/consult/list.vue')
-    },{
+    }, {
       name: 'SeoConsultList_1',
       path: '/consult/*',
       component: resolve(__dirname, '../pages/frontend/consult/list.vue')
-    },{
+    }, {
       name: 'SeoConsultUser',
       path: '/c/:id?',
       component: resolve(__dirname, '../pages/frontend/consult/user/_id.vue')
-    },{
+    }, {
       name: 'SeoSkillList',
       path: '/skill',
       component: resolve(__dirname, '../pages/frontend/skill/list.vue')
-    },{
+    }, {
       name: 'SeoSkillList_1',
       path: '/skill/*',
       component: resolve(__dirname, '../pages/frontend/skill/list.vue')
-    },{
+    }, {
       name: 'SeoSkillDetail',
       path: '/s/:id?',
       component: resolve(__dirname, '../pages/frontend/skill/detail/_id.vue')
-    }]
-  )
+    }],
+
+    // 技术等级认证
+    // ...[
+    //   {
+    //     name: 'SkillCertProfile',
+    //     path: '/skill_cert/profile',
+    //     component: resolve(__dirname, '../pages/skill_cert/profile.vue')
+    //   }, {
+    //     name: 'SkillCertResume',
+    //     path: '/skill_cert/resume_improve',
+    //     component: resolve(__dirname, '../pages/skill_cert/resume_improve.vue')
+    //   }, {
+    //     name: 'SkillCertReadyInterview',
+    //     path: '/skill_cert/ready_interview',
+    //     component: resolve(__dirname, '../pages/skill_cert/ready_interview.vue')
+    //   }, {
+    //     name: 'SkillCertStartInterview',
+    //     path: '/skill_cert/start_interview',
+    //     component: resolve(__dirname, '../pages/skill_cert/start_interview.vue')
+    //   }, {
+    //     name: 'SkillCertForm',
+    //     path: '/skill_cert/cert_form',
+    //     component: resolve(__dirname, '../pages/skill_cert/cert_form.vue')
+    //   },
+    // ]
+  );
 
   /**
    * 404