浏览代码

发布需求表单

martin.ma 4 年之前
父节点
当前提交
23cd423b3a

+ 107 - 21
assets/css/common.css

@@ -6,18 +6,79 @@
   font-size: 14px;
 }
 
-html, body, div, span, applet, object, iframe,
-blockquote, pre, abbr, acronym, address, big, cite, code,
-del, dfn, em, img, ins, kbd, q, s, samp,
-small, strike, strong, sub, sup, tt, var,
-b, u, i, center,
-dl, dt, dd, ol, ul, li,
-fieldset, form, label, legend,
-table, caption, tbody, tfoot, thead, tr, th, td,
-article, aside, canvas, details, embed,
-figure, figcaption, footer, header, hgroup,
-menu, nav, output, ruby, section, summary,
-time, mark, audio, video {
+html,
+body,
+div,
+span,
+applet,
+object,
+iframe,
+blockquote,
+pre,
+abbr,
+acronym,
+address,
+big,
+cite,
+code,
+del,
+dfn,
+em,
+img,
+ins,
+kbd,
+q,
+s,
+samp,
+small,
+strike,
+strong,
+sub,
+sup,
+tt,
+var,
+b,
+u,
+i,
+center,
+dl,
+dt,
+dd,
+ol,
+ul,
+li,
+fieldset,
+form,
+label,
+legend,
+table,
+caption,
+tbody,
+tfoot,
+thead,
+tr,
+th,
+td,
+article,
+aside,
+canvas,
+details,
+embed,
+figure,
+figcaption,
+footer,
+header,
+hgroup,
+menu,
+nav,
+output,
+ruby,
+section,
+summary,
+time,
+mark,
+audio,
+video {
   margin: 0;
   padding: 0;
   border: 0;
@@ -27,8 +88,17 @@ time, mark, audio, video {
 }
 
 /* HTML5 display-role reset for older browsers */
-article, aside, details, figcaption, figure,
-footer, header, hgroup, menu, nav, section {
+article,
+aside,
+details,
+figcaption,
+figure,
+footer,
+header,
+hgroup,
+menu,
+nav,
+section {
   display: block;
 }
 
@@ -42,16 +112,20 @@ body {
   background: #F4F5F9;
 }
 
-ol, ul {
+ol,
+ul {
   list-style: none;
 }
 
-blockquote, q {
+blockquote,
+q {
   quotes: none;
 }
 
-blockquote:before, blockquote:after,
-q:before, q:after {
+blockquote:before,
+blockquote:after,
+q:before,
+q:after {
   content: '';
   content: none;
 }
@@ -218,7 +292,7 @@ img {
   color: rgba(153, 153, 153, 1)
 }
 
-.app__fc_b{
+.app__fc_b {
   color: #629eff
 }
 
@@ -271,8 +345,8 @@ img {
   text-align: center;
 }
 
-.app__bd_g{
-  border:1px solid #ddd
+.app__bd_g {
+  border: 1px solid #ddd
 }
 
 /* common empty */
@@ -285,10 +359,12 @@ img {
   justify-content: center;
   background: #ffffff;
 }
+
 .result-empty-wrapper img {
   width: 154px;
   height: 154px;
 }
+
 .result-empty-wrapper span {
   margin-top: -20px;
   height: 20px;
@@ -297,3 +373,13 @@ img {
   font-family: PingFangSC, PingFangSC-Regular;
   color: #999999;
 }
+
+
+.text-line-1 {
+  display: -webkit-box;
+  word-break: break-all;
+  -webkit-box-orient: vertical;
+  -webkit-line-clamp: 1;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}

二进制
assets/img/recommend/bg1.png


二进制
assets/img/recommend/bg2.png


二进制
assets/img/recommend/bg3.png


二进制
assets/img/recommend/close.png


二进制
assets/img/recommend/icon1.png


二进制
assets/img/recommend/icon2.png


二进制
assets/img/recommend/icon3.png


+ 1 - 1
components/header.vue

@@ -581,7 +581,7 @@ export default {
       // 点击发布需求
       // 如没登录,跳到登录页
       if(this.myInfo.nickname){
-
+        location.href = "/frontend/requirements"
       }else{
         location.href = this.loginUrl 
       }

+ 520 - 0
pages/frontend/requirements/components/Form_recommend.vue

@@ -0,0 +1,520 @@
+<template>
+<div class="recommend-container" v-if="isShow">
+    <div class="recommend-main">
+        <div class="close" @click="close"></div>
+        <div class="recommend-title">为您精选以下开发者</div>
+        <div class="recommend-second-title">选择后,需求审核通过就会帮您对接哦~</div>
+        <div class="recommend-list">
+            <div class="recommend-item first">
+                <div class="recommend-icon"></div>
+                <div class="recommend-item-title">性价比最高</div>
+                <div class="recommend-item-worker" v-for="user in RateUser" :key="user.uid">
+                    <div class="recommend-worker-info-area">
+                        <div class="worder-avatar">
+                            <img :src="user.user_logo" />
+                        </div>
+                        <div class="worder-info">
+                            <p class="recommend-worder-name text-line-1">{{user.nickname}}</p>
+                            <p class="recommend-worder-detail">日薪:{{user.work_price}} | <a href="">查看简历</a></p>
+                        </div>
+                    </div>
+                    <div class="recommend-choice-btn-area">
+                        <span class="recommend-choice-btn" :class="{'cur':firstUser.uid == user.uid}" @click="setFirstChoice(user)">设为主选</span>
+                        <span class="recommend-choice-btn" :class="{'cur':secondUser.uid == user.uid}" @click="setSecondChoice(user)">设为次选</span>
+                    </div>
+                </div>
+
+            </div>
+            <div class="recommend-item second">
+                <div class="recommend-icon"></div>
+                <div class="recommend-item-title">好评最多</div>
+                <div class="recommend-item-worker" v-for="user in ScoreUser" :key="user.uid">
+                    <div class="recommend-worker-info-area">
+                        <div class="worder-avatar">
+                            <img :src="user.user_logo" />
+                        </div>
+                        <div class="worder-info">
+                            <p class="recommend-worder-name text-line-1">{{user.nickname}}</p>
+                            <p class="recommend-worder-detail">日薪:{{user.work_price}} | <a href="">查看简历</a></p>
+                        </div>
+                    </div>
+                    <div class="recommend-choice-btn-area">
+                        <span class="recommend-choice-btn" :class="{'cur':firstUser.uid == user.uid}" @click="setFirstChoice(user)">设为主选</span>
+                        <span class="recommend-choice-btn" :class="{'cur':secondUser.uid == user.uid}" @click="setSecondChoice(user)">设为次选</span>
+                    </div>
+                </div>
+            </div>
+            <div class="recommend-item third">
+                <div class="recommend-icon"></div>
+                <div class="recommend-item-title">技术最牛</div>
+                <div class="recommend-item-worker" v-for="user in PriceUser" :key="user.uid">
+                    <div class="recommend-worker-info-area">
+                        <div class="worder-avatar">
+                            <img :src="user.user_logo" />
+                        </div>
+                        <div class="worder-info">
+                            <p class="recommend-worder-name text-line-1">{{user.nickname}}</p>
+                            <p class="recommend-worder-detail">日薪:{{user.work_price}} | <a href="">查看简历</a></p>
+                        </div>
+                    </div>
+                    <div class="recommend-choice-btn-area">
+                        <span class="recommend-choice-btn" :class="{'cur':firstUser.uid == user.uid}" @click="setFirstChoice(user)">设为主选</span>
+                        <span class="recommend-choice-btn" :class="{'cur':secondUser.uid == user.uid}" @click="setSecondChoice(user)">设为次选</span>
+                    </div>
+                </div>
+            </div>
+        </div>
+        <div class="recommend-tips">我的选择:</div>
+        <div class="recommend-my-choice">
+            <div class="recommend-worker">
+                <div class="recommend-worder-choiced">
+                    <div class="recommend-worder-choiced-avatar" :style="{
+                       'background-image': 'url('+firstUser.user_logo+')'
+                    }"></div>
+                    <p class="recommend-worder-choiced-state text-line-1">{{firstUser.nickname? firstUser.nickname:'未选择'}}</p>
+                    <p class="recommend-worder-sort">主选</p>
+                </div>
+                <div class="recommend-worder-choiced">
+                    <div class="recommend-worder-choiced-avatar" :style="{
+                       'background-image': 'url('+secondUser.user_logo+')'
+                    }"></div>
+                    <p class="recommend-worder-choiced-state text-line-1">{{secondUser.nickname? secondUser.nickname:'未选择'}}</p>
+                    <p class="recommend-worder-sort second-choice ">次选</p>
+                </div>
+            </div>
+            <div class="recommend-submit">
+                <div class="recommend-submit-btn" @click="submit">确认选择</div>
+                <div class="recommend-submit-btn pass">
+                    <span>跳过选择</span>
+                    <span class="recommend-submit-btn-tips">客户经理为您选择</span>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+</template>
+
+<script>
+export default {
+    props: ['projectid'],
+    data() {
+        return {
+            firstUser: {},
+            secondUser: {},
+            recommend: {},
+            isShow: true,
+        }
+    },
+    mounted() {
+        this.fetchData()
+    },
+    computed: {
+        RateUser() {
+            return this.recommend ? this.recommend['rate_user'] : []
+        },
+        ScoreUser() {
+            return this.recommend ? this.recommend['score_user'] : []
+        },
+        PriceUser() {
+            return this.recommend ? this.recommend['price_user'] : []
+        }
+    },
+    methods: {
+        setFirstChoice(user) {
+            if (this.secondUser.uid == user.uid) {
+                this.secondUser = {}
+            }
+            this.firstUser = {
+                ...user
+            }
+        },
+        setSecondChoice(user) {
+            if (this.firstUser.uid == user.uid) {
+                this.firstUser = {}
+            }
+            this.secondUser = {
+                ...user
+            }
+        },
+        fetchData() {
+            this.$axios
+                .$post(
+                    "/wapi/work/recommend", {
+                        type: 3,
+                        projectId: this.projectid
+                    }
+                )
+                .then((res) => {
+                    if (res.status == 1) {
+                        this.recommend = {
+                            ...res.data
+                        }
+                    } else {
+                        this.$message.error(res.info)
+                    }
+                })
+        },
+        close() {
+            this.isShow = false
+        },
+        submit() {
+            if(!this.firstUser.uid && !this.secondUser.uid){
+                this.$message.error("请选择开发者")
+                return
+            }
+            let uids = []
+            if (this.firstUser.uid) {
+                uids.push(this.firstUser.uid)
+            }
+            if (this.secondUser.uid) {
+                uids.push(this.secondUser.uid)
+            }
+            this.$axios
+                .$post(
+                    "/wapi/work/updataJobRecommend", {
+                        yy_developer_uid: uids.join(","),
+                        projectId: this.projectid
+                    }
+                )
+                .then((res) => {
+                    if (res.status == 1) {
+
+                    } else {
+                        this.$message.error(res.info)
+                    }
+                })
+        }
+
+    }
+}
+</script>
+
+<style lang="scss" scoped>
+.recommend-container {
+    position: fixed;
+    background: rgba(0, 0, 0, 0.7);
+    z-index: 50;
+    top: 0;
+    bottom: 0;
+    left: 0;
+    right: 0;
+}
+
+.recommend-main {
+    position: absolute;
+    left: 50%;
+    transform: translateX(-50%);
+    top: 194px;
+    width: 880px;
+    height: 636px;
+    background: #FFFFFF;
+    border-radius: 8px;
+
+    .close {
+        width: 16px;
+        height: 16px;
+        position: absolute;
+        right: 19px;
+        top: 19px;
+        background: url("~@/assets/img/recommend/close.png") no-repeat 50% 50%;
+        background-size: cover;
+        cursor: pointer;
+    }
+}
+
+.recommend-title {
+    margin-top: 40px;
+    text-align: center;
+    font-size: 22px;
+    font-family: PingFangSC-Medium, PingFang SC;
+    font-weight: 500;
+    color: #0B121A;
+    line-height: 30px
+}
+
+.recommend-second-title {
+    margin-top: 4px;
+    text-align: center;
+    font-size: 14px;
+    font-family: PingFangSC-Regular, PingFang SC;
+    font-weight: 400;
+    color: #445973;
+    line-height: 20px;
+}
+
+.recommend-list {
+    margin-top: 36px;
+    margin-left: 50px;
+    margin-right: 50px;
+    display: flex;
+    justify-content: space-between;
+}
+
+.recommend-item {
+    width: 240px;
+    height: 314px;
+    position: relative;
+}
+
+.recommend-icon {
+    position: absolute;
+    left: 46px;
+    top: 0;
+    width: 39px;
+    height: 46px;
+    // border: 1px solid #f00;
+}
+
+.recommend-item-title {
+    margin-top: 16px;
+    margin-left: 93px;
+    font-size: 16px;
+    font-family: PingFangSC-Semibold, PingFang SC;
+    font-weight: 600;
+    line-height: 22px
+}
+
+.recommend-item-worker {
+    margin-top: 16px;
+    margin-left: 16px;
+    margin-right: 16px;
+    // width: 208px;
+    height: 114px;
+    background: #FFFFFF;
+    border-radius: 4px;
+    overflow: hidden;
+}
+
+.recommend-worker-info-area {
+    display: flex;
+    margin-top: 12px;
+}
+
+.worder-avatar {
+    width: 40px;
+    height: 40px;
+    border-radius: 40px;
+    font-size: 0;
+    margin-left: 12px;
+    overflow: hidden;
+
+    img {
+        width: 40px;
+        height: 40px;
+        border-radius: 40px;
+    }
+}
+
+.worder-info {
+    flex: 1;
+    margin-left: 8px;
+    display: flex;
+    flex-direction: column;
+    // align-items: center;
+    justify-content: space-between;
+}
+
+.recommend-worder-name {
+    font-size: 14px;
+    font-family: PingFangSC-Medium, PingFang SC;
+    font-weight: 500;
+    color: #0B121A;
+    line-height: 20px
+}
+
+.recommend-worder-detail {
+    margin-top: 2px;
+    font-size: 12px;
+    font-family: PingFangSC-Regular, PingFang SC;
+    font-weight: 400;
+    color: #828C99;
+    line-height: 17px;
+
+    a {
+        color: #4D81BF;
+    }
+}
+
+.recommend-choice-btn-area {
+    margin-top: 20px;
+    text-align: center;
+}
+
+.recommend-choice-btn {
+    display: inline-block;
+    width: 72px;
+    height: 28px;
+
+    border-radius: 14px;
+
+    line-height: 28px;
+    font-size: 13px;
+    font-weight: 400;
+
+    margin-left: 6px;
+    margin-right: 6px;
+    cursor: pointer;
+    background: #FFFFFF;
+    border: 1px solid #4B5D73;
+    color: #4B5D73;
+
+    &.cur {
+        background: #4B5D73;
+        color: #FFFFFF;
+    }
+}
+
+.first {
+    background: url("~@/assets/img/recommend/bg1.png") no-repeat 50% 50%;
+    background-size: cover;
+
+    .recommend-icon {
+        background: url("~@/assets/img/recommend/icon1.png") no-repeat 50% 50%;
+        background-size: cover;
+    }
+
+    .recommend-item-title {
+        color: #458EE6;
+    }
+}
+
+.second {
+    background: url("~@/assets/img/recommend/bg2.png") no-repeat 50% 50%;
+    background-size: cover;
+
+    .recommend-icon {
+        background: url("~@/assets/img/recommend/icon2.png") no-repeat 50% 50%;
+        background-size: cover;
+    }
+
+    .recommend-item-title {
+        color: #E66445;
+    }
+}
+
+.third {
+    background: url("~@/assets/img/recommend/bg3.png") no-repeat 50% 50%;
+    background-size: cover;
+
+    .recommend-icon {
+        background: url("~@/assets/img/recommend/icon3.png") no-repeat 50% 50%;
+        background-size: cover;
+    }
+
+    .recommend-item-title {
+        color: #AB7A42;
+    }
+}
+
+.recommend-tips {
+    margin: 30px 0 0 50px;
+    font-size: 14px;
+    font-family: PingFangSC-Regular, PingFang SC;
+    font-weight: 400;
+    color: #637192;
+    line-height: 20px;
+}
+
+.recommend-my-choice {
+    margin-left: 50px;
+    margin-right: 50px;
+    margin-top: 20px;
+    display: flex;
+    justify-content: space-between;
+}
+
+.recommend-worker {
+    display: flex;
+
+}
+
+.recommend-worder-choiced {
+    display: flex;
+    margin-right: 29px;
+    width: 80px;
+    flex-direction: column;
+    // justify-content: center;
+
+    align-items: center;
+    text-align: center;
+}
+
+.recommend-worder-choiced-avatar {
+    width: 36px;
+    height: 36px;
+    background-color: #EBECED;
+    border-radius: 100%;
+    background-repeat: no-repeat;
+    background-position: 50% 50%;
+    background-size: cover;
+}
+
+.recommend-worder-choiced-state {
+    font-size: 13px;
+    font-family: PingFangSC-Regular, PingFang SC;
+    font-weight: 400;
+    color: #828C99;
+    line-height: 18px;
+    margin-top: 4px;
+}
+
+.recommend-worder-sort {
+    margin-top: 6px;
+    width: 32px;
+    height: 18px;
+    background: #FFFFFF;
+    border-radius: 2px;
+    border: 1px solid #308EFF;
+
+    line-height: 18px;
+    text-align: center;
+
+    font-size: 12px;
+    font-family: PingFangSC-Regular, PingFang SC;
+    font-weight: 400;
+    color: #308EFF;
+
+    &.second-choice {
+        border-color: #445973;
+        color: #445973;
+    }
+}
+
+.recommend-submit {
+    display: flex
+}
+
+.recommend-submit-btn {
+    margin-right: 20px;
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    align-items: center;
+    width: 120px;
+    height: 54px;
+    background: #96C6FF;
+    border-radius: 2px;
+    color: #FFFFFF;
+    cursor: pointer;
+
+    &:hover {
+        background: #308EFF
+    }
+}
+
+.recommend-submit-btn-tips {
+    font-size: 12px;
+    font-family: PingFangSC-Regular, PingFang SC;
+    font-weight: 400;
+    color: rgba(255, 255, 255, 0.5);
+    line-height: 17px;
+}
+
+.pass {
+    background: #99AABF;
+
+    &:hover {
+        background: #4B5D73
+    }
+}
+</style>

+ 463 - 0
pages/frontend/requirements/components/Form_yunduan.vue

@@ -0,0 +1,463 @@
+<template>
+<div class="form-xuqiu">
+    <!-- <div class="form-title">当前类型:项目整包</div> -->
+
+    <el-form ref="modalForm" :model="modalFormData" :rules="rules" size="medium" label-width="100px">
+
+        <div class="form-label">1.您期望雇佣开发者的技术栈</div>
+        <el-form-item label-width="0" prop="match_directions">
+            <el-cascader :options="directionData" clearable @change="directionChange"></el-cascader>
+        </el-form-item>
+
+        <div class="form-label">2.开发者需要具备哪些核心技能标签? (1-5个)</div>
+
+        <el-form-item label-width="0" prop="skills">
+            <el-select v-model="modalFormData.skills" @change="skillChange" multiple filterable remote reserve-keyword placeholder="请输入关键词" :remote-method="remoteMethod" :loading="loading" :style="{width: '50%'}">
+                <el-option v-for="item in skills" :key="item.value" :label="item.label" :value="item.value">
+                </el-option>
+            </el-select>
+        </el-form-item>
+
+        <div class="form-label">3.您需要全职开发者吗?</div>
+        <el-form-item label-width="0" prop="full">
+            <el-radio-group v-model="modalFormData.full" size="medium">
+                <el-radio v-for="(item, index) in fullOptions" :key="index" :label="item.value" :disabled="item.disabled">{{item.label}}</el-radio>
+            </el-radio-group>
+            <div v-if="modalFormData.full == 2" class="form-tips">会导致可选开发者变少</div>
+        </el-form-item>
+
+        <div class="form-label">4.您需要开发者驻场开发吗?</div>
+        <el-form-item label-width="0" prop="is_incompany">
+            <el-radio-group v-model="modalFormData.is_incompany" size="medium">
+                <el-radio v-for="(item, index) in fullOptions" :key="index" :label="item.value" :disabled="item.disabled">{{item.label}}</el-radio>
+            </el-radio-group>
+            <div v-if="modalFormData.is_incompany == 2" class="form-tips">会导致可选开发者变少</div>
+            <template v-if="modalFormData.is_incompany == 2">
+                <div class="">我需要开发者来自以下地区</div>
+                <el-cascader :options="cityData" clearable @change="cityChange"></el-cascader>
+            </template>
+
+        </el-form-item>
+
+        <!-- <el-form-item label-width="0" prop="city">
+            
+        </el-form-item> -->
+
+        <div class="form-label">5.简短描述您的需求</div>
+        <el-form-item label-width="0" prop="description">
+            <el-input v-model="modalFormData.description" type="textarea" :placeholder="tips" show-word-limit :autosize="{ minRows: 10, maxRows: 4 }" :style="{ width: '100%' }"></el-input>
+        </el-form-item>
+
+        <el-form-item label-width="0" prop="field103">
+            <div class="form-label">
+                <span style="margin-right:10px">我有附件要上传</span>
+                <el-switch v-model="modalFormData.field103"></el-switch>
+            </div>
+        </el-form-item>
+        <el-form-item label-width="0" prop="field104" v-show="modalFormData.field103">
+            <el-upload class="upload-demo" :file-list="field104fileList" drag :show-file-list="true" action="/file/prepareUpload" :multiple="false" :http-request="uploadFile" :on-remove="remove">
+                <i class="el-icon-upload"></i>
+                <div class="el-upload__text">
+                    将文件拖到此处,或<em>点击上传</em>传需求文档
+                </div>
+            </el-upload>
+        </el-form-item>
+
+        <div class="form-label">6.您的预算大概多少?</div>
+        <el-form-item label-width="0" prop="ys_money_type">
+            <el-select v-model="modalFormData.ys_money_type" placeholder="请选择计划合作的预算" clearable :style="{width: '100%'}">
+                <el-option v-for="(item, index) in ys_money_typeOptions" :key="index" :label="item.label" :value="item.value" :disabled="item.disabled"></el-option>
+            </el-select>
+        </el-form-item>
+
+        <el-form-item label-width="0" prop="is_need_manager">
+            <div class="form-label">
+                <span style="margin-right:10px">我需要项目经理</span>
+                <el-switch v-model="modalFormData.is_need_manager"></el-switch>
+
+                <el-popover placement="right" width="250" trigger="hover">
+                    <div class="form-tooltip">
+                        合理的项目分析和规划将提高34.7%的项目成功率<br /><br />
+                        预计将支出5-10%项目费用,具体费用将由客户经理和您沟通
+                    </div>
+
+                    <i class="el-icon-info form-info-icon" slot="reference"></i>
+                </el-popover>
+
+            </div>
+        </el-form-item>
+
+        <el-form-item size="large">
+            <div class="form-submit-area">
+                <el-button type="primary" @click="submitForm">确定</el-button>
+            </div>
+        </el-form-item>
+    </el-form>
+
+    <Loading ref='loading'></Loading>
+</div>
+</template>
+
+<script>
+import uploadFile from "@/mixins/uploadFile";
+import Loading from './Loading.vue'
+
+function directionDataParse(arr) {
+
+    arr = arr.sort(function (a, b) {
+        return Number(a.display_order) - Number(b.display_order) > 0
+    })
+    let result = [];
+    let len = arr.length
+    for (let i = 0; i < len; i++) {
+        let {
+            children,
+            ...other
+        } = arr[i]
+        let label = arr[i].occupation_name || arr[i].direction_name
+        let value = arr[i].occupation_id || arr[i].direction_id
+        let item
+        if (children) {
+            children = directionDataParse(children)
+            item = {
+                ...other,
+                label,
+                value,
+                children
+            }
+        } else {
+            item = {
+                ...other,
+                label,
+                value
+            }
+        }
+        result.push(item)
+    }
+    return result
+}
+
+function cityDataParse(arr) {
+    let result = [];
+    let len = arr.length
+    for (let i = 0; i < len; i++) {
+        let {
+            child,
+            ...other
+        } = arr[i]
+        let label = arr[i].name
+        let value = arr[i].id || arr[i].id
+        let item
+        if (child && child.length > 0) {
+            let children = cityDataParse(child)
+            item = {
+                ...other,
+                label,
+                value,
+                children
+            }
+        } else {
+            item = {
+                ...other,
+                label,
+                value
+            }
+        }
+        result.push(item)
+    }
+    return result
+}
+export default {
+    components: {
+        Loading
+    },
+    props: [],
+    data() {
+        return {
+            modalFormData: {
+                match_directions: "",
+                direction_id: "",
+                skills: [],
+
+                description: "",
+                field103: false,
+                field104: null,
+                is_need_manager: false,
+                budget: "",
+                full: 1,
+                is_incompany: 1,
+
+                city: "",
+                province_id: "",
+                city_id: "",
+                ys_money_type: "",
+
+            },
+            rules: {
+                description: [{
+                    required: true,
+                    message: "请输入您的需求",
+                    trigger: "blur"
+                }],
+                ys_money_type: [{
+                    required: true,
+                    message: "请选择您的预算",
+                    trigger: "blur"
+                }],
+                match_directions: [{
+                    required: true,
+                    message: "请选择开发者的技术栈",
+                    trigger: "blur"
+                }],
+                skills: [{
+                    required: true,
+                    message: "请选择核心技能标签",
+                    trigger: "blur"
+                }],
+
+            },
+            field104Action: "",
+            field104fileList: [],
+
+            fullOptions: [{
+                "label": "不需要",
+                "value": 1
+            }, {
+                "label": "需要",
+                "value": 2
+            }],
+
+            ys_money_typeOptions: [{
+                "label": "1-6K",
+                "value": 1
+            }, {
+                "label": "6-12K",
+                "value": 2
+            }, {
+                "label": "12-18K",
+                "value": 3
+            }, {
+                "label": "18K以上",
+                "value": 4
+            }],
+
+            directionData: [],
+            cityData: [],
+            skills: [],
+            loading: false,
+            tips: `可参考以下内容:\n1、您的产品/公司/业务的简短介绍\n2、需求的核心工作内容\n3、对开发者的其他需求`,
+        };
+    },
+    computed: {},
+    watch: {
+        modalFormData: {
+            deep: true,
+            handler: function (val) {
+                if (!val.field103) {
+                    this.field104fileList = []
+                }
+                this.$emit("formChange", 2, "process");
+            }
+        }
+    },
+    created() {},
+    mounted() {
+        this.$emit("formChange", 2, "process");
+        this.fetchDirectionData()
+        this.fetchCityData()
+    },
+    mixins: [uploadFile],
+    methods: {
+        submitForm() {
+            this.$refs["modalForm"].validate(valid => {
+                if (!valid) return;
+                // TODO 提交表单
+                this.requestSubmit();
+            });
+        },
+        resetForm() {
+            this.$refs["modalForm"].resetFields();
+        },
+        uploadFile(file, type) {
+            this.uploading = true;
+            this.apiPrepareUpload(
+                file.file,
+                res => {
+                    if (res.data && res.data.status === 1) {
+                        let uploadId = res.data.data._upload_id;
+                        let url = res.data.data.url;
+
+                        this.field104fileList.push({
+                            data: {
+                                ...res.data.data
+                            },
+                            name: res.data.data.name,
+                            url: url,
+                            uploadId: uploadId
+                        });
+                        this.$message.success("上传成功");
+                    } else {
+                        this.$message.error("上传失败");
+                    }
+                },
+                4
+            );
+            return false;
+        },
+        remove(file, fileList) {
+            this.field104fileList = [...fileList];
+        },
+
+        fetchDirectionData() {
+            this.$axios
+                .$post(
+                    "/api/direction/get_all_data"
+                )
+                .then(res => {
+                    // console.log(res)
+                    let aa = directionDataParse(res.data);
+                    this.directionData = [...aa]
+                })
+        },
+        directionChange(selectedOption) {
+            this.modalFormData.match_directions = selectedOption[0]
+            this.modalFormData.direction_id = selectedOption[1]
+        },
+
+        fetchCityData() {
+            this.$axios
+                .$post(
+                    "/wapi/pub/getAllCity"
+                )
+                .then(res => {
+                    // console.log(res)
+                    let aa = cityDataParse(res.data.list);
+                    console.log(aa)
+                    this.cityData = [...aa]
+                })
+        },
+        cityChange(selectedOption) {
+            this.modalFormData.province_id = selectedOption[0]
+            this.modalFormData.city_id = selectedOption[1]
+        },
+
+        remoteMethod(query) {
+            if (query !== '') {
+                this.loading = true;
+
+                this.$axios.$post("/api/simple_data/select_skill", {
+                    keyword: query
+                }).then(res => {
+                    this.loading = false;
+                    let list = res.data;
+                    this.skills = list.map((item) => {
+                        return {
+                            ...item,
+                            value: item.id,
+                            label: item.name,
+                        }
+                    })
+                })
+            } else {
+                this.skills = [];
+            }
+        },
+        skillChange(a, b) {
+            console.log(111, a, b)
+        },
+        async requestSubmit() {
+            let form = this.modalFormData
+            let params = {
+                match_directions: form.match_directions,
+                direction_id: form.direction_id,
+                match_skills: form.skills.join(','),
+                description: form.description,
+                ys_money_type: form.ys_money_type,
+                province_id: form.province_id,
+                city_id: form.city_id,
+                is_need_manager: form.is_need_manager ? 1 : 0
+
+            };
+            if (this.field104fileList.length > 0) {
+                let arr = this.field104fileList.map(item => {
+                    return {
+                        ...item.data
+                    };
+                });
+                params["job_files"] = JSON.stringify(arr);
+            }
+
+            this.$axios
+                .$post(
+                    "/api/job/publish3", {
+                        ...params
+                    }
+                )
+                .then(res => {
+                    if (Number(res.status) === 1) {
+                        this.$message.success("提交成功");
+                        let id = res.data.id
+                        return Promise.resolve(id);
+                    } else {
+                        this.$message.error("提交失败");
+                    }
+                })
+                .then(async (id) => {
+                    await this.$refs['loading'].start()
+                    this.$emit('formSubmit', 2, id)
+                    // 跳转
+                })
+        }
+
+    }
+};
+</script>
+
+<style lang="scss">
+.form-xuqiu {
+    margin: 80px 80px 0;
+}
+
+.form-title {
+    font-size: 22px;
+    font-weight: 500;
+    color: #0b121a;
+    line-height: 30px;
+    margin-bottom: 40px;
+}
+
+.form-label {
+    height: 30px;
+    line-height: 30px;
+    font-size: 13px;
+    font-family: PingFangSC-Medium;
+    font-weight: 500;
+    color: #19222e;
+}
+
+.form-submit-area {
+    text-align: center;
+}
+
+.form-info-icon {
+    color: #409EFF;
+    font-size: 18px;
+    cursor: pointer;
+    position: relative;
+    top: 5px;
+}
+
+.form-tooltip {
+    // width: 200px;
+    line-height: 2em;
+}
+
+.form-tips {
+    font-size: 12px;
+    font-family: PingFangSC-Regular, PingFang SC;
+    font-weight: 400;
+    color: #E6A23C;
+    line-height: 17px;
+    display: inline-block;
+}
+</style>

+ 6 - 5
pages/frontend/requirements/components/Form_zhengbao.vue

@@ -39,13 +39,14 @@
                 <span style="margin-right:10px">我需要项目经理</span>
                 <el-switch v-model="modalFormData.is_need_manager"></el-switch>
 
-                <el-tooltip placement="right">
-                    <div class="form-tooltip" slot="content">
-                        合理的项目分析和规划将提高34.7%的项目成功率<br />
+                 <el-popover placement="right" width="250" trigger="hover">
+                    <div class="form-tooltip">
+                        合理的项目分析和规划将提高34.7%的项目成功率<br /><br />
                         预计将支出5-10%项目费用,具体费用将由客户经理和您沟通
                     </div>
-                    <i class="el-icon-info form-info-icon"></i>
-                </el-tooltip>
+
+                    <i class="el-icon-info form-info-icon" slot="reference"></i>
+                </el-popover>
 
             </div>
         </el-form-item>

+ 248 - 0
pages/frontend/requirements/components/Loading.vue

@@ -0,0 +1,248 @@
+<template>
+<div class="form-loading" v-if="loadingAnimate">
+    <div class="form-loading-container">
+        <div class="loading3">
+            <div class="circle circle1">
+                <span></span>
+                <span></span>
+                <span></span>
+                <span></span>
+            </div>
+            <div class="circle circle2">
+                <span></span>
+                <span></span>
+                <span></span>
+                <span></span>
+            </div>
+            <div class="circle circle3">
+                <span></span>
+                <span></span>
+                <span></span>
+                <span></span>
+            </div>
+        </div>
+
+        <div class="form-loading-text">
+            <p>正在为您匹配人才...</p>
+            <p>倒计时{{times}}秒</p>
+        </div>
+    </div>
+</div>
+</template>
+
+<script>
+let timer
+export default {
+    data() {
+        return {
+            times: 5,
+            loadingAnimate: false
+        }
+    },
+    destroy() {
+        clearInterval(timer)
+        timer = null
+    },
+    methods: {
+        start() {
+            return new Promise((resolve, reject) => {
+                this.loadingAnimate = true;
+                timer = setInterval(() => {
+                    this.times--
+                    if (this.times == 0) {
+                        clearInterval(timer)
+                        timer = null
+                        this.times = 5
+                        this.loadingAnimate = false
+                        resolve()
+                    }
+                }, 1000)
+            })
+        },
+        stop() {
+            clearInterval(timer)
+            timer = null
+            this.times = 5
+            this.loadingAnimate = false
+        }
+    }
+}
+</script>
+
+<style lang="scss" scoped>
+.form-loading {
+    position: fixed;
+    z-index: 100;
+    left: 0;
+    top: 0;
+    right: 0;
+    bottom: 0;
+    background-color: rgba(0, 0, 0, 0.7);
+}
+
+.form-loading-container {
+    position: absolute;
+    left: 50%;
+    top: 45%;
+    transform: translate(-50%, -50%);
+}
+
+.form-loading-text {
+    text-align: center;
+
+    p {
+        line-height: 2em;
+        color: #409EFF;
+    }
+}
+
+.loading3 {
+    width: 70px;
+    height: 70px;
+    margin: 50px auto;
+    position: relative;
+
+}
+
+.circle {
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+}
+
+.circle span {
+    width: 8px;
+    height: 8px;
+    display: inline-block;
+    background: #409EFF;
+    border-radius: 100%;
+    position: absolute;
+    -webkit-animation: mycircle 1.2s infinite ease-in-out;
+    animation: mycircle 1.2s infinite ease-in-out;
+    -webkit-animation-fill-mode: both;
+    animation-fill-mode: both;
+}
+
+.circle2 {
+    -webkit-transform: rotateZ(45deg);
+    transform: rotateZ(45deg);
+}
+
+.circle3 {
+    -webkit-transform: rotateZ(90deg);
+    transform: rotateZ(90deg);
+}
+
+.circle>span:nth-child(1) {
+    top: 0;
+    left: 0;
+}
+
+.circle>span:nth-child(2) {
+    top: 0;
+    right: 0;
+}
+
+.circle>span:nth-child(3) {
+    right: 0;
+    bottom: 0;
+}
+
+.circle>span:nth-child(4) {
+    left: 0;
+    bottom: 0;
+}
+
+.circle2>span:nth-child(1) {
+    -webkit-animation-delay: -1.1s;
+    animation-delay: -1.1s;
+}
+
+.circle3>span:nth-child(1) {
+    -webkit-animation-delay: -1.0s;
+    animation-delay: -1.0s;
+}
+
+.circle1>span:nth-child(2) {
+    -webkit-animation-delay: -0.9s;
+    animation-delay: -0.9s;
+}
+
+.circle2>span:nth-child(2) {
+    -webkit-animation-delay: -0.8s;
+    animation-delay: -0.8s;
+}
+
+.circle3>span:nth-child(2) {
+    -webkit-animation-delay: -0.7s;
+    animation-delay: -0.7s;
+}
+
+.circle1>span:nth-child(3) {
+    -webkit-animation-delay: -0.6s;
+    animation-delay: -0.6s;
+}
+
+.circle2>span:nth-child(3) {
+    -webkit-animation-delay: -0.7s;
+    animation-delay: -0.7s;
+}
+
+.circle3>span:nth-child(3) {
+    -webkit-animation-delay: -0.4s;
+    animation-delay: -0.4s;
+}
+
+.circle1>span:nth-child(4) {
+    -webkit-animation-delay: -0.3s;
+    animation-delay: -0.3s;
+}
+
+.circle2>span:nth-child(4) {
+    -webkit-animation-delay: -0.2s;
+    animation-delay: -0.2s;
+}
+
+.circle3>span:nth-child(4) {
+    -webkit-animation-delay: -0.1s;
+    animation-delay: -0.1s;
+}
+
+@-webkit-keyframes mycircle {
+    0% {
+        transform: scale(0.0);
+    }
+
+    40% {
+        transform: scale(1.0);
+    }
+
+    80% {
+        transform: scale(0.0);
+    }
+
+    100% {
+        transform: scale(0.0);
+    }
+}
+
+@keyframes mycircle {
+    0% {
+        transform: scale(0.0);
+    }
+
+    40% {
+        transform: scale(1.0);
+    }
+
+    80% {
+        transform: scale(0.0);
+    }
+
+    100% {
+        transform: scale(0.0);
+    }
+}
+</style>

+ 48 - 24
pages/frontend/requirements/index.vue

@@ -43,7 +43,10 @@
         <FormFirstStep v-if="isFirstFormShow" v-on:formSubmit="formSubmit" v-on:formChange="formChange"></FormFirstStep>
         <FormXuQiu v-if="isFormXuqiuShow" v-on:formChange="formChange" v-on:formSubmit="formSubmit"></FormXuQiu>
         <FormZhengBao v-if="isFormZhengBaoShow" v-on:formChange="formChange" v-on:formSubmit="formSubmit"></FormZhengBao>
+        <FormYunDuan v-if="isFormYunDuanShow" v-on:formChange="formChange" v-on:formSubmit="formSubmit"></FormYunDuan>
         <StepEnd v-if="isStepEndShow"></StepEnd>
+
+        <FormRecomment v-if="isShowFormRecomment" :projectid="projectid"></FormRecomment>
     </div>
 </div>
 </template>
@@ -55,19 +58,25 @@ import {
 import qs from "qs";
 import FormFirstStep from "./components/FormFirstStep.vue";
 // 发布需求梳理表单
-import FormXuQiu from "./components/Form_xuqiu.vue"
+import FormXuQiu from "./components/Form_xuqiu.vue";
 // 发布整包表单
-import FormZhengBao from "./components/Form_zhengbao"
+import FormZhengBao from "./components/Form_zhengbao";
+// 发布云端项目
+import FormYunDuan from "./components/Form_yunduan";
+
+import FormRecomment from "./components/Form_recommend.vue";
 
 // 发布成功页
-import StepEnd from './components/StepEnd.vue'
+import StepEnd from "./components/StepEnd.vue";
 export default {
     name: "SeoLearnList",
     components: {
         FormFirstStep,
         FormXuQiu,
         FormZhengBao,
-        StepEnd
+        FormYunDuan,
+        StepEnd,
+        FormRecomment
     },
     data() {
         return {
@@ -82,7 +91,10 @@ export default {
             // 当前激活的状态
             active: 0,
             // wait process finish
-            activeStatus: "wait"
+            activeStatus: "wait",
+
+            isShowFormRecomment: false,
+            projectid: ""
         };
     },
     head() {
@@ -192,66 +204,78 @@ export default {
             return status;
         },
         isFirstFormShow: function () {
-            return this.active == 1
+            return this.active == 1;
         },
         isFormXuqiuShow: function () {
-            return this.active == 2 && this.type == 3
+            return this.active == 2 && this.type == 3;
         },
         isFormZhengBaoShow: function () {
-            return this.active == 2 && this.type == 2
+            return this.active == 2 && this.type == 2;
+        },
+        isFormYunDuanShow: function () {
+            return this.active == 2 && this.type == 1;
         },
         isStepEndShow: function () {
-            return this.active == 3
+            return this.active == 3;
         }
     },
     watch: {
-        '$route.query': function (val) {
+        "$route.query": function (val) {
             if (val.step) {
-                this.active = Number(val.step)
+                this.active = Number(val.step);
             } else {
-                this.active = 1
+                this.active = 1;
             }
             if (val.type) {
-                this.type = Number(val.type)
+                this.type = Number(val.type);
             } else {
-                this.type = 0
+                this.type = 0;
             }
-            this.activeStatus = "wait"
+            this.activeStatus = "wait";
         }
     },
     mounted() {
         let query = this.$route.query;
         if (query.step) {
-            this.active = Number(query.step)
+            this.active = Number(query.step);
         } else {
-            this.active = 1
+            this.active = 1;
         }
         if (query.type) {
-            this.type = Number(query.type)
+            this.type = Number(query.type);
         } else {
-            this.type = 0
+            this.type = 0;
+        }
+
+        if (query.type == 1 && query.step == 3 && query.projectid) {
+            this.isShowFormRecomment = true
+            this.projectid = query.projectid
         }
         this.baseUrl = this.$store.state.domainConfig.siteUrl;
         this.isWeixinApp = navigator.userAgent.indexOf("miniProgram") > -1;
     },
     methods: {
         formSubmit(step, data) {
-            let query = {}
+            let query = {};
             if (step == 1) {
                 this.type = data;
-                query['type'] = this.type
+                query["type"] = this.type;
+            }
+            if (step == 2 && this.type == 1) {
+                query["projectid"] = data;
+                query["type"] = this.type;
             }
             // 进入下一步
             step++;
-            this.active = step
-            query['step'] = this.active
+            this.active = step;
+            query["step"] = this.active;
             this.activeStatus = "process";
             this.$nextTick(() => {
                 this.$router.push({
                     path: "/frontend/requirements",
                     query: query
                 });
-            })
+            });
         },
         formChange(step, type) {
             this.active = step;