瀏覽代碼

Merge branch 'dev-majunjie' into dev

ccf 4 年之前
父節點
當前提交
75f3df8e98
共有 38 個文件被更改,包括 2897 次插入347 次删除
  1. 107 21
      assets/css/common.css
  2. 169 0
      assets/css/developer/index.scss
  3. 93 0
      assets/css/requirements/index.scss
  4. 二進制
      assets/img/account/step_cur.png
  5. 二進制
      assets/img/account/type1.png
  6. 二進制
      assets/img/account/type2.png
  7. 二進制
      assets/img/account/type3.png
  8. 二進制
      assets/img/developer/order-1.png
  9. 二進制
      assets/img/developer/order-2.png
  10. 二進制
      assets/img/developer/order-3.png
  11. 二進制
      assets/img/developer/order-4.png
  12. 二進制
      assets/img/developer/order-5.png
  13. 二進制
      assets/img/developer/process1.png
  14. 二進制
      assets/img/developer/process2.png
  15. 二進制
      assets/img/developer/process3.png
  16. 二進制
      assets/img/developer/process4.png
  17. 二進制
      assets/img/recommend/bg1.png
  18. 二進制
      assets/img/recommend/bg2.png
  19. 二進制
      assets/img/recommend/bg3.png
  20. 二進制
      assets/img/recommend/close.png
  21. 二進制
      assets/img/recommend/icon1.png
  22. 二進制
      assets/img/recommend/icon2.png
  23. 二進制
      assets/img/recommend/icon3.png
  24. 0 125
      components/account_change/accountChangePanel.vue
  25. 2 2
      components/header.vue
  26. 7 7
      mixins/uploadFile.js
  27. 74 192
      pages/frontend/account/change.vue
  28. 187 0
      pages/frontend/developer/index.vue
  29. 194 0
      pages/frontend/requirements/components/FormFirstStep.vue
  30. 520 0
      pages/frontend/requirements/components/Form_recommend.vue
  31. 186 0
      pages/frontend/requirements/components/Form_xuqiu.vue
  32. 463 0
      pages/frontend/requirements/components/Form_yunduan.vue
  33. 231 0
      pages/frontend/requirements/components/Form_zhengbao.vue
  34. 248 0
      pages/frontend/requirements/components/Loading.vue
  35. 20 0
      pages/frontend/requirements/components/StepEnd.vue
  36. 290 0
      pages/frontend/requirements/index.vue
  37. 88 0
      pages/frontend/requirements/template.vue
  38. 18 0
      plugins/seoRouter.js

+ 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;
+}

+ 169 - 0
assets/css/developer/index.scss

@@ -0,0 +1,169 @@
+.developer-container {
+  width: 1100px;
+  margin-left: auto;
+  margin-right: auto;
+  overflow: hidden;
+}
+
+.block {
+  padding: 24px;
+  background: #fff;
+  border-radius: 8px;
+  margin-bottom: 20px;
+}
+.title {
+  font-size: 20px;
+  font-family: PingFangSC-Medium, PingFang SC;
+  font-weight: 500;
+  color: #0b121a;
+  line-height: 28px;
+}
+.tips {
+  margin-top: 2px;
+  font-size: 14px;
+  font-family: PingFangSC-Regular, PingFang SC;
+  font-weight: 400;
+  color: #828c99;
+  line-height: 21px;
+}
+.developer-left {
+  float: left;
+  width: 730px;
+}
+.developer-right {
+  float: right;
+  width: 350px;
+}
+
+// 接单流程
+
+.developer-process-list {
+  margin-top: 24px;
+  display: flex;
+  justify-content: space-between;
+}
+.developer-process-item {
+  width: 139px;
+  flex: 1;
+  &:nth-last-child(1) {
+    flex: 0;
+  }
+}
+
+.developer-process-step {
+  width: 139px;
+  height: 42px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  border-radius: 8px;
+
+  font-size: 16px;
+  font-family: PingFangSC-Regular, PingFang SC;
+  font-weight: 400;
+
+  &.normal {
+    border: 1px solid #ced3d9;
+    background: #ffffff;
+    color: #0b121a;
+  }
+  &.cur {
+    background-color: #288bff;
+    color: #fff;
+  }
+}
+.developer-process-tips {
+  margin-top: 12px;
+  width: 139px;
+  text-align: center;
+
+  font-size: 13px;
+  font-family: PingFangSC-Regular, PingFang SC;
+  font-weight: 400;
+  color: #828c99;
+  line-height: 18px;
+}
+
+.developer-process-step-icon {
+  width: 20px;
+  height: 20px;
+  background-position: 50% 50%;
+  background-repeat: no-repeat;
+  background-size: cover;
+  margin-right: 8px;
+}
+@for $i from 1 through 4 {
+  .developer-process-step-icon.icon-#{$i} {
+    background-image: url("~@/assets/img/developer/process#{$i}.png");
+  }
+}
+
+// 新人接单
+
+.develop-order-list {
+  font-size: 0;
+}
+.develop-order-item {
+  width: 333px;
+  height: 116px;
+  background: #ffffff;
+  border-radius: 16px;
+  border: 1px solid #ebeced;
+
+  box-sizing: border-box;
+  padding: 16px 16px 0;
+  float: left;
+  box-sizing: border-box;
+  margin-top: 16px;
+  &:nth-child(2n + 1) {
+    margin-right: 16px;
+  }
+}
+
+.develop-order-list {
+  overflow: hidden;
+}
+
+.develop-order-title-area {
+  display: flex;
+  align-items: center;
+}
+.develop-order-tips {
+  margin-top: 8px;
+  font-size: 14px;
+  font-family: PingFangSC-Regular, PingFang SC;
+  font-weight: 400;
+  color: #4b5d73;
+  line-height: 20px;
+}
+.develop-order-icon {
+  width: 32px;
+  height: 32px;
+  margin-right: 10px;
+  background-position: 50% 50%;
+  background-repeat: no-repeat;
+  background-size: cover;
+}
+
+@for $i from 1 through 5 {
+  .develop-order-icon.icon-#{$i} {
+    background-image: url("~@/assets/img/developer/order-#{$i}.png");
+  }
+}
+
+.develop-order-title {
+  font-size: 16px;
+  font-family: PingFangSC-Medium, PingFang SC;
+  font-weight: 500;
+  color: #0b121a;
+  line-height: 22px;
+  margin-right: 16px;
+}
+
+.develop-order-link{
+    font-size: 14px;
+font-family: PingFangSC-Regular, PingFang SC;
+font-weight: 400;
+color: #308EFF;
+line-height: 20px;
+}

+ 93 - 0
assets/css/requirements/index.scss

@@ -0,0 +1,93 @@
+.requirements-container {
+  width: 1100px;
+  padding-top: 50px;
+  padding-bottom: 80px;
+  background: #ffffff;
+  border-radius: 8px;
+}
+.requirements-title {
+  text-align: center;
+  height: 40px;
+  font-size: 28px;
+  font-weight: 500;
+  color: #0b121a;
+  line-height: 40px;
+}
+
+.requirements-step-container {
+  width: 462px;
+  margin-left: auto;
+  margin-right: auto;
+  margin-top: 40px;
+  display: flex;
+  height: 80px;
+}
+.requirements-step {
+  flex: 1;
+  height: 32px;
+  position: relative;
+  &:nth-last-child(1) {
+    flex: 0;
+  }
+}
+.requirements-step-status {
+  height: 32px;
+  position: relative;
+}
+.requirements-step-icon {
+  position: relative;
+  z-index: 5;
+  width: 32px;
+  height: 32px;
+  background: #ebeced;
+  border-radius: 100%;
+  transition: all .3s;
+  &.success {
+    background: url("~@/assets/img/account/step_cur.png") no-repeat 50% 50%;
+    background-size: 32px auto;
+  }
+  &.wait {
+    background: #ebeced;
+  }
+  &.process {
+    background: #45c47a;
+  }
+}
+.requirements-step-line {
+  position: absolute;
+  z-index: 2;
+  left: 0;
+  right: 0;
+  top: 50%;
+  transform: translateY(-50%);
+  height: 6px;
+  background: #ebeced;
+  transition: all .3s;
+  &.success {
+    background: #d0f2de;
+  }
+  &.wait {
+    background: #ebeced;
+  }
+  &.process {
+    background: #d0f2de;
+  }
+}
+
+.requirements-step-tips {
+  position: absolute;
+  margin-top: 14px;
+  font-size: 14px;
+  font-family: PingFangSC-Regular, PingFang SC;
+  font-weight: 400;
+  color: #828c99;
+  line-height: 20px;
+  transform: translateX(-50%);
+  left: 50%;
+  top: 46px;
+  white-space: nowrap;
+}
+.requirements-type-submit {
+    text-align: center;
+    margin-top: 80px;
+}

二進制
assets/img/account/step_cur.png


二進制
assets/img/account/type1.png


二進制
assets/img/account/type2.png


二進制
assets/img/account/type3.png


二進制
assets/img/developer/order-1.png


二進制
assets/img/developer/order-2.png


二進制
assets/img/developer/order-3.png


二進制
assets/img/developer/order-4.png


二進制
assets/img/developer/order-5.png


二進制
assets/img/developer/process1.png


二進制
assets/img/developer/process2.png


二進制
assets/img/developer/process3.png


二進制
assets/img/developer/process4.png


二進制
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


+ 0 - 125
components/account_change/accountChangePanel.vue

@@ -1,125 +0,0 @@
-<template>
-    <div class="account-change-panel-container">
-        <div class="account-change-title">选择您的角色</div>
-        <div class="account-change-main">
-            <div class="account-change-item">
-                <div class="account-change-img">
-                    <img src="https://iph.href.lu/100x100?fg=666666&bg=cccccc" />
-                </div>
-                <div class="account-change-tips">
-                    我是需求方
-                </div>
-                <div class="account-change-cur"></div>
-            </div>
-
-            <div class="account-change-item">
-                <div class="account-change-img">
-                    <img src="https://iph.href.lu/100x100?fg=666666&bg=cccccc" />
-                </div>
-                <div class="account-change-tips">
-                    我是开发者
-                </div>
-                <div class="account-change-cur"></div>
-            </div>
-        </div>
-        <div @click="submit" class="account-change-submit">确认3选择</div>
-    </div>
-</template>
-
-<script>
-export default {
-    data(){
-        return {
-            accountType:"",
-
-        }
-    },
-    computed:{
-        myInfo() {
-            return this.$store.state.userinfo;
-        },
-    },
-    mounted() {
-
-    },
-    methods: {
-        submit(){
-            alert(234)
-            let userinfo = this.$store.state.userinfo
-            console.log(userinfo)
-        }
-    },
-}
-</script>
-
-
-<style lang="scss">
-.account-change-panel-container{
-    width: 880px;
-    height: 600px;
-    background: #FFFFFF;
-    border-radius: 8px;
-}
-.account-change-title{
-    margin-top: 40px;
-    margin-bottom: 50px;
-    text-align: center;
-    font-size: 28px;
-    font-family: PingFangSC-Medium, PingFang SC;
-    font-weight: 500;
-    color: #0B121A;
-    line-height: 40px
-}
-.account-change-main{
-    margin-left:58px;
-    margin-right: 58px;
-    overflow: hidden;
-    display: flex;
-    justify-content:space-between;
-}
-
-.account-change-item{
-    width: 360px;
-    height: 300px;
-    background: #FFFFFF;
-    border-radius: 16px;
-    border: 2px dotted #CED3D9;
-}
-
-.account-change-img{
-    margin:60px auto 0;
-    width:90px;
-    height:90px;
-    border-radius:100%;
-    overflow: hidden;
-    img{
-        width:90px;
-        height:90px;
-        border-radius:100%;
-    }
-}
-
-.account-change-tips{
-    margin-top: 24px;
-    font-size: 24px;
-    font-family: PingFangSC-Medium, PingFang SC;
-    font-weight: 500;
-    color: #0B121A;
-    line-height: 33px
-    text-align: center
-}
-
-.account-change-submit{
-    margin:58px auto 0 ;
-    width: 140px;
-    height: 48px;
-    border-radius: 24px;
-    border: 1px solid #4A5D74;
-    line-height: 48px;
-    text-align: center;
-
-    font-size: 18px;
-    font-weight: 500;
-    color: #4B5D73
-}
-</style>

+ 2 - 2
components/header.vue

@@ -333,7 +333,7 @@
           <a
             class="account-change"
             style="margin-left: 20px;margin-right:40px"
-            :href="baseUrl + '/index/app'"
+            :href="baseUrl + '/frontend/accountchange'"
             >切换身份</a
           >
 
@@ -581,7 +581,7 @@ export default {
       // 点击发布需求
       // 如没登录,跳到登录页
       if(this.myInfo.nickname){
-
+        location.href = "/frontend/requirements"
       }else{
         location.href = this.loginUrl 
       }

+ 7 - 7
mixins/uploadFile.js

@@ -1,6 +1,6 @@
 import axios from 'axios'
 export default {
-  mounted() {},
+  mounted() { },
   data() {
     return {
       uploadInfo: {},
@@ -15,11 +15,11 @@ export default {
     }
   },
   methods: {
-    apiPrepareUpload(file, cb) {
+    apiPrepareUpload(file, cb, type = 3) {
       let uploadInfo = null;
       let formData = new FormData()
       formData.append("filename", file.name);
-      formData.append("target", '{"type":3}');
+      formData.append("target", JSON.stringify({ "type": type }));
       this.$axios.post('/file/prepareUpload', formData, {
         headers: {
           "Content-Type": "multipart/form-data"
@@ -28,19 +28,19 @@ export default {
         this.uploadInfo = data.data.data.post_params;
         this.post_url = data.data.data.post_url;
         this.upload_id = data.data.data.upload_id;
-        this.apiSend(file, cb)
+        this.apiSend(file, cb,type)
       })
       return uploadInfo;
     },
-    apiSend(file, cb) {
+    apiSend(file, cb,type = 3) {
       let formData = new FormData()
       formData.append("file", file);
       formData.append("name", file.name);
-      formData.append("target", '{"type":3}');
+      formData.append("target", JSON.stringify({ "type": type }));
       for (let key in this.uploadInfo) {
         formData.append(key, this.uploadInfo[key])
       }
-      axios.post('/file/proxyUpload', formData, {
+      this.$axios.post('/file/proxyUpload', formData, {
         headers: {
           "Content-Type": "multipart/form-data"
         }

+ 74 - 192
pages/frontend/account/change.vue

@@ -4,27 +4,27 @@
     <div class="account-change-panel-container" v-if="!mobile">
         <div class="account-change-title">选择您的角色</div>
         <div class="account-change-main">
-            <div class="account-change-item">
+            <div class="account-change-item" @click="changeType(1)" :class="isCompany? 'cur':''">
                 <div class="account-change-img">
-                    <img src="https://iph.href.lu/100x100?fg=666666&bg=cccccc" />
+                    <img :src=" isCompany ?userInfo.icon_url: companyIcon " />
                 </div>
                 <div class="account-change-tips">
                     我是需求方
                 </div>
-                <div class="account-change-cur"></div>
+                <div v-if="isCompany" class="account-change-cur"></div>
             </div>
 
-            <div class="account-change-item">
+            <div class="account-change-item" @click="changeType(2)" :class="isPersonal? 'cur':''">
                 <div class="account-change-img">
-                    <img src="https://iph.href.lu/100x100?fg=666666&bg=cccccc" />
+                    <img :src="isPersonal ?userInfo.icon_url:personalIcon" />
                 </div>
                 <div class="account-change-tips">
                     我是开发者
                 </div>
-                <div class="account-change-cur"></div>
+                <div v-if="isPersonal" class="account-change-cur"></div>
             </div>
         </div>
-        <div class="account-change-submit">确认选择</div>
+        <div @click="submit" class="account-change-submit">确认选择</div>
     </div>
 </div>
 </template>
@@ -35,18 +35,20 @@ import {
 } from "vuex"
 import DealSeoList from "@/components/learn/dealSeoList"
 import qs from "qs"
-
+import companyIcon from "@/assets/img/account/company.png"
+import personalIcon from "@/assets/img/account/personal.png"
 export default {
     name: 'SeoLearnList',
     data() {
         return {
             baseUrl: '',
+            mobile: false,
             // firstLoad: true,
             isWeixinApp: true,
-            categoryExpanded: true, // 更多按钮不要,默认为展开状态
-            showCategoryDrawer: false,
-            currentDrawerCategoryId: 0,
-            currentDrawerCategoryIndex: 0
+            personalIcon,
+            companyIcon,
+
+            home_page_type: ""
         }
     },
     head() {
@@ -97,194 +99,60 @@ export default {
             } else {
                 return '20px !important'
             }
+        },
+        userInfo() {
+            return this.$store.state.userinfo
+        },
+        isCompany() {
+            return this.home_page_type == 1
+        },
+        isPersonal() {
+            return this.home_page_type == 2
         }
     },
-    async asyncData({
-        ...params
-    }) {
-        let dealDataObj = new DealSeoList(params)
-        let ans = await dealDataObj.dealData()
 
-        return {
-            ...ans
-        }
-    },
     mounted() {
+        this.home_page_type = this.userInfo.home_page_type
+        //  console.log(444,)
         this.baseUrl = this.$store.state.domainConfig.siteUrl
         this.isWeixinApp = navigator.userAgent.indexOf("miniProgram") > -1
     },
     methods: {
-        /** 分页获取技能列表数据 */
-        _getLearnList() {
-            const self = this
-            const data = {
-                type: 1,
-                page: this.pagination.page,
-                page_size: this.pagination.pagesize,
-                cate_id: this.pagination.selectedCateIdTwo,
-                status: 2,
-                owner_type: 1,
-                root_type: this.root_type
-            }
-
-            this.pagination.loading = true
-            this.pagination.noMore = false
-
-            this.$axios.$post('/api/sale/saleList', data).then(res => {
-                if (Number(res.status) === 1) {
-                    let learnList = res.data.list || []
-                    learnList.forEach((item) => {
-                        let imageList = item.image.split(',')
-                        item.coverImage = imageList[0] || ''
-                        imageList.splice(0, 1)
-                        item.imageList = imageList
-                    })
-
-                    if (self.mobile) {
-                        self.learnList = self.learnList.concat(learnList)
-                    } else {
-                        self.learnList = learnList
-                    }
-
-                    self.pagination.total = res.data.total
-                    self.pagination.pagesize = res.data.page_size || 9
-                    if (self.pagination.page * self.pagination.pagesize >= self.pagination.total) {
-                        console.log('noMore true', self.pagination)
-                        self.pagination.noMore = true
-                    } else {
-                        console.log('noMore false', self.pagination)
-                        self.pagination.noMore = false
-                    }
-                }
-            }).then(() => {
-                self.pagination.loading = false
-            })
-        },
-        /** 点击展开、收起 */
-        handleClickExpandCategory() {
-            this.categoryExpanded = !this.categoryExpanded
-        },
-        /** 点击一级分类时 */
-        handleClickCategoryOne(id) {
-            if (id === 0) {
-                // 点击全部时,移除筛选分类
-                this.pagination.selectedCateIdOne = id
-                this.pagination.selectedCateIdTwo = ''
-                this.currentDrawerCategoryId = ''
-                this.pagination.page = 1
-                this.learnList = []
-                window.scroll(0, 0)
-
-                this._getLearnList()
-                return
-            }
-            if (this.pagination.selectedCateIdOne !== id) {
-                this.pagination.selectedCateIdOne = id
-            }
-        },
-        /** 点击二级分类时:移动端 */
-        handleClickCategoryTwo(id) {
-            if (this.pagination.selectedCateIdTwo === id) {
-                this.pagination.selectedCateIdTwo = ''
-                this.currentDrawerCategoryId = ''
-            } else {
-                this.pagination.selectedCateIdTwo = id
-                this.currentDrawerCategoryId = id
-            }
-            this.pagination.page = 1
-            this.learnList = []
-            window.scroll(0, 0)
-
-            this._getLearnList()
+        changeType(e) {
+            console.log(e)
+            this.home_page_type = e
         },
-        /** 分页页码改变时 */
-        handlePageChange(val) {
-            let query = {
-                page: val
-            }
-            if (this.root_type && Number(this.root_type) > 0) {
-                query.root_type = this.root_type
-            }
-            window.location.href = `${window.location.origin}${window.location.pathname}?${qs.stringify(query)}`
-        },
-        /** mobile 加载更多 */
-        handleLoadMoreSkill() {
-            if (this.pagination.loading) {
-                return
+        async submit() {
+            
+            let home_page_type  = this.userInfo.home_page_type
+            if(home_page_type != this.home_page_type){
+                let res = await this.$axios.$post("/api/user/update_info", {
+                    home_page_type: this.home_page_type
+                });
+
+                if(Number(res.status) !== 1){
+                    this.$message.error(res.info);
+                }
             }
 
-            this.pagination.page++
-            this._getLearnList()
-        },
-        /** 点击筛选时 */
-        handleShowCategoryDrawer() {
-            this.showCategoryDrawer = true
-        },
-        /**
-         * 点击 mobile 分类 drawer 一级分类
-         */
-        handleClickDrawerCategoryOne(id) {
-            if (id === 0) {
-                this.showCategoryDrawer = false
-                return
-            }
-            if (id !== this.currentDrawerCategoryIndex) {
-                this.currentDrawerCategoryIndex = id
-            }
-        },
-        /**
-         * 点击 mobile 分类 drawer 二级分类
-         */
-        handleClickDrawerCategoryTwo(id) {
-            if (this.currentDrawerCategoryId === id) {
-                this.pagination.selectedCateIdTwo = ''
-            } else {
-                this.pagination.selectedCateIdTwo = id
-            }
-            this.currentDrawerCategoryId = id
-            this.showCategoryDrawer = false
-            this.pagination.page = 1
-            this.learnList = []
-            window.scroll(0, 0)
+            // TODO:跳转对应的首页
 
-            this._getLearnList()
-        },
-        /**
-         * 点击 mobile 的一项技能时
-         */
-        handleClickSkillItem(saleId) {
-            if (this.deviceType.android || this.deviceType.ios) {
-                // 端跳转
-                let jumpUrl = `${this.baseUrl}/l/${saleId}`
-                location.href = `proginn://webview?url=${jumpUrl}`
-            } else {
-                // web 跳转
-                location.href = `/l/${saleId}`
-            }
-        },
-        /**
-         * 点击成为讲师
-         */
-        handleClickAdd() {
-            location.href = '/workbench/skill/index'
+            location.href = "/"
         }
     }
 }
 </script>
 
-<style lang="scss" scoped>
-// @import "@/assets/css/learn/list.scss";
-</style>
 <style lang="scss">
-
-.account-change-panel-container{
+.account-change-panel-container {
     width: 880px;
     height: 600px;
     background: #FFFFFF;
     border-radius: 8px;
     overflow: hidden;
 }
-.account-change-title{
+
+.account-change-title {
     margin-top: 40px;
     margin-bottom: 50px;
     text-align: center;
@@ -294,37 +162,43 @@ export default {
     color: #0B121A;
     line-height: 40px
 }
-.account-change-main{
-    margin-left:58px;
+
+.account-change-main {
+    margin-left: 58px;
     margin-right: 58px;
     overflow: hidden;
     display: flex;
-    justify-content:space-between;
+    justify-content: space-between;
 }
 
-.account-change-item{
+.account-change-item {
     width: 360px;
     height: 300px;
     background: #FFFFFF;
     border-radius: 16px;
     border: 2px dotted #CED3D9;
     cursor: pointer;
+
+    &.cur {
+        border: 2px solid #308EFF;
+    }
 }
 
-.account-change-img{
-    margin:60px auto 0;
-    width:90px;
-    height:90px;
-    border-radius:100%;
+.account-change-img {
+    margin: 60px auto 0;
+    width: 90px;
+    height: 90px;
+    border-radius: 100%;
     overflow: hidden;
-    img{
-        width:90px;
-        height:90px;
-        border-radius:100%;
+
+    img {
+        width: 90px;
+        height: 90px;
+        border-radius: 100%;
     }
 }
 
-.account-change-tips{
+.account-change-tips {
     margin-top: 24px;
     font-size: 24px;
     font-family: PingFangSC-Medium, PingFang SC;
@@ -334,8 +208,8 @@ export default {
     text-align: center
 }
 
-.account-change-submit{
-    margin:58px auto 0 ;
+.account-change-submit {
+    margin: 58px auto 0;
     width: 140px;
     height: 48px;
     border-radius: 24px;
@@ -348,4 +222,12 @@ export default {
     color: #4B5D73;
     cursor: pointer;
 }
+
+.account-change-cur {
+    margin: 15px auto 0;
+    width: 34px;
+    height: 34px;
+    background: url("~@/assets/img/account/cur.png") no-repeat 50% 50%;
+    background-size: 100% auto;
+}
 </style>

+ 187 - 0
pages/frontend/developer/index.vue

@@ -0,0 +1,187 @@
+<template>
+<div :class="mobile ? 'mobileMain' : ''" :style="{marginTop: mainMarginTop, marginBottom: mobile ? '0px' : '30px !important'}">
+
+    <div class="developer-container" v-if="!mobile">
+        <div class="developer-left">
+            <!-- 接单流程:start -->
+            <div class="developer-process block">
+                <h3 class="title">接单流程</h3>
+                <div class="developer-process-list">
+                    <div class="developer-process-item">
+                        <div class="developer-process-step cur">
+                            <div class="developer-process-step-icon icon-1"></div>
+                            <div class="developer-process-step-content">注册</div>
+                        </div>
+                        <div class="developer-process-tips">成为客栈注册用户</div>
+                    </div>
+
+                    <div class="developer-process-item">
+                        <div class="developer-process-step normal">
+                            <div class="developer-process-step-icon icon-2"></div>
+                            <div class="developer-process-step-content">实名认证</div>
+                        </div>
+                        <div class="developer-process-tips">
+                            <p>按国家相关规定,用户需实名,目前尚未实名</p>
+                            <p><a href="">立即使命</a></p>
+                        </div>
+                    </div>
+
+                    <div class="developer-process-item">
+                        <div class="developer-process-step normal">
+                            <div class="developer-process-step-icon icon-3"></div>
+                            <div class="developer-process-step-content">签约开发者</div>
+                        </div>
+                        <div class="developer-process-tips">
+                            <p>尚未签约 <a href="">立即签约</a></p>
+                        </div>
+                    </div>
+
+                    <div class="developer-process-item">
+                        <div class="developer-process-step normal">
+                            <div class="developer-process-step-icon icon-4"></div>
+                            <div class="developer-process-step-content">开始接单</div>
+                        </div>
+                        <div class="developer-process-tips">内容已通过</div>
+                    </div>
+                </div>
+            </div>
+            <!-- 接单流程:end -->
+
+            <!-- 新人如何接单:start -->
+            <div class="developer-order block">
+                <h3 class="title">新人如何接单</h3>
+                <p class="tips">客栈接单采用智能对接池匹配原则,权重越高优先匹配。当前在“前端”对接池的排名100,影响对接池的因素有以下几点:</p>
+                <div class="develop-order-list">
+                    <div class="develop-order-item">
+                        <div class="develop-order-title-area">
+                            <div class="develop-order-icon icon-1"></div>
+                            <h5 class="develop-order-title">完善个人资料</h5>
+                            <div class="develop-order-link"><a href="">立即完善</a></div>
+                        </div>
+                        <p class="develop-order-tips">账号信息完善度(个人信息+简历+作品)越高,客户信任感更高,也更容易为您精准匹配。</p>
+                    </div>
+                    <div class="develop-order-item">
+                        <div class="develop-order-title-area">
+                            <div class="develop-order-icon icon-2"></div>
+                            <h5 class="develop-order-title">技术等级认证</h5>
+                            <div class="develop-order-link"><a href="">立即认证</a></div>
+                        </div>
+                        <p class="develop-order-tips">我们会优先匹配进行过技术认证的开发者,如果您是自由职业者还可以进行自由工作者认证。</p>
+                    </div>
+                    <div class="develop-order-item">
+                        <div class="develop-order-title-area">
+                            <div class="develop-order-icon icon-3"></div>
+                            <h5 class="develop-order-title">Ping一下</h5>
+                            <div class="develop-order-link"><a href="">Ping一下</a></div>
+                        </div>
+                        <p class="develop-order-tips">每日Ping一下,表达你的接单意愿,将提升你的对接池权重。</p>
+                    </div>
+                    <div class="develop-order-item">
+                        <div class="develop-order-title-area">
+                            <div class="develop-order-icon icon-4"></div>
+                            <h5 class="develop-order-title">客户好评</h5>
+                            <!-- <div class="develop-order-link"><a href="">立即完善</a></div> -->
+                        </div>
+                        <p class="develop-order-tips">成功接单后,服务质量优秀,之后会多多派单哦!</p>
+                    </div>
+                    <div class="develop-order-item">
+                        <div class="develop-order-title-area">
+                            <div class="develop-order-icon icon-5"></div>
+                            <h5 class="develop-order-title">开通开发者会员</h5>
+                            <div class="develop-order-link"><a href="">查看会员介绍</a></div>
+                        </div>
+                        <p class="develop-order-tips">开发者会员出来提高权重,还有更多权益!</p>
+                    </div>
+                </div>
+            </div>
+            <!-- 新人如何接单:end -->
+        </div>
+        <div class="developer-right">
+
+        </div>
+    </div>
+</div>
+</template>
+
+<script>
+import {
+    mapState
+} from "vuex"
+import qs from "qs"
+
+export default {
+    name: 'SeoLearnList',
+    data() {
+        return {
+            baseUrl: '',
+            mobile: false,
+            // firstLoad: true,
+            isWeixinApp: true,
+        }
+    },
+    head() {
+        const {
+            title = "",
+                keyword = "",
+                description = "",
+                h1 = "",
+                canonical = "",
+                metaLocation
+        } = this.head || {}
+        let obj = {
+            title: title,
+            meta: [{
+                name: "keywords",
+                content: keyword
+            }, {
+                name: "description",
+                content: description
+            }, {
+                name: "h1",
+                content: h1
+            }],
+            link: [{
+                rel: "canonical",
+                href: canonical
+            }]
+        }
+        if (metaLocation) {
+            obj.meta.push({
+                name: "location",
+                content: metaLocation
+            })
+        }
+        return obj
+    },
+    computed: {
+        ...mapState(["deviceType"]),
+        showWxHeader() {
+            return !this.deviceType.app && !this.isWeixinApp &&
+                (this.deviceType.android || this.deviceType.ios)
+        },
+        mainMarginTop() {
+            if (this.mobile && this.showWxHeader) {
+                return '64px !important'
+            } else if (this.mobile) {
+                return '0px !important'
+            } else {
+                return '20px !important'
+            }
+        },
+
+    },
+
+    mounted() {
+        //  console.log(444,)
+        this.baseUrl = this.$store.state.domainConfig.siteUrl
+        this.isWeixinApp = navigator.userAgent.indexOf("miniProgram") > -1
+    },
+    methods: {
+
+    }
+}
+</script>
+
+<style lang="scss">
+@import "@/assets/css/developer/index.scss";
+</style>

+ 194 - 0
pages/frontend/requirements/components/FormFirstStep.vue

@@ -0,0 +1,194 @@
+<template>
+<div class="requirements-type-container">
+    <div class="requirements-type-title">请选择一种需求类型:</div>
+    <div class="requirements-type-list">
+        <div class="requirements-type-item" :class="type == 1?'cur':''" @click="selectType(1)">
+            <div class="requirements-type-title-area">
+                <div class="requirements-type-icon type1"></div>
+                <div class="requirements-type-title">云端工作:</div>
+            </div>
+            <div class="requirements-type-purpose">我想明确找个人</div>
+            <ul class="requirements-type-purpose-list">
+                <li>我需要个前端开发者?</li>
+                <li>我需要个java开发者?</li>
+                <li>我需要个爬虫工程师?</li>
+                <li>我需要个安卓/IOS开发者?</li>
+            </ul>
+            <div class="requirements-type-payment">按月付费</div>
+        </div>
+        <div class="requirements-type-item" :class="type == 2?'cur':''" @click="selectType(2)">
+            <div class="requirements-type-title-area">
+                <div class="requirements-type-icon type2"></div>
+                <div class="requirements-type-title">项目整包</div>
+            </div>
+            <div class="requirements-type-purpose">我有个项目要开发:</div>
+
+            <ul class="requirements-type-purpose-list">
+                <li>我要开发个网站?我要开发个小程序?</li>
+                <li>我要开发个APP?</li>
+                <li>我要做个游戏?我要爬个网站?</li>
+            </ul>
+            <div class="requirements-type-payment">按项目付费</div>
+        </div>
+        <div class="requirements-type-item" :class="type == 3?'cur':''" @click="selectType(3)">
+            <div class="requirements-type-title-area">
+                <div class="requirements-type-icon type3"></div>
+                <div class="requirements-type-title">需求梳理</div>
+            </div>
+            <div class="requirements-type-purpose">
+                我有个idea需要请产品经理梳理:
+            </div>
+
+            <ul class="requirements-type-purpose-list">
+                <li>我的想法要落地,需要产品结构图和流程图?</li>
+                <li>用户交互怎么设计?</li>
+                <li>原型图不会画?</li>
+            </ul>
+            <div class="requirements-type-payment">固定付费¥1980</div>
+        </div>
+    </div>
+    <div class="requirements-type-submit">
+        <el-button :disabled="!type" type="primary" @click="submit" round>下一步</el-button>
+    </div>
+</div>
+</template>
+
+<script>
+export default {
+    data() {
+        return {
+            type: ""
+        }
+    },
+    methods: {
+        selectType(type) {
+            this.type = type
+            this.$emit('formChange', 1,'process')
+        },
+        submit() {
+            this.$emit('formSubmit',1, this.type)
+        }
+    }
+}
+</script>
+
+<style lang="scss" scoped>
+.requirements-type-container {
+    margin: 80px 80px 0;
+}
+
+.requirements-type-container {
+    font-size: 22px;
+    font-weight: 500;
+    color: #0b121a;
+    line-height: 30px;
+}
+
+.requirements-type-list {
+    margin-top: 40px;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+}
+
+.requirements-type-item {
+    box-sizing: border-box;
+    width: 286px;
+    height: 246px;
+    cursor: pointer;
+    position: relative;
+
+    &::after {
+        content: "";
+        position: absolute;
+        width: 100%;
+        height: 100%;
+        left: 0;
+        top: 0;
+        border-radius: 16px;
+        border: 1px solid #ced3d9;
+        // transition: all 0.3s;
+    }
+
+    &.cur {
+        &::after {
+            border: 2px solid #308EFF;
+        }
+    }
+}
+
+.requirements-type-title-area {
+    margin-top: 17px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+}
+
+.requirements-type-icon {
+    width: 36px;
+    height: 36px;
+    margin-right: 8px;
+    background-position: 50% 50%;
+    background-repeat: no-repeat;
+    background-size: 36px auto;
+
+    &.type1 {
+        background-image: url("~@/assets/img/account/type1.png");
+    }
+
+    &.type2 {
+        background-image: url("~@/assets/img/account/type2.png");
+    }
+
+    &.type3 {
+        background-image: url("~@/assets/img/account/type3.png");
+    }
+}
+
+.requirements-type-title {
+    font-size: 16px;
+    font-weight: 500;
+    color: #0b121a;
+    line-height: 22px;
+}
+
+.requirements-type-purpose {
+    margin-left: 24px;
+    margin-top: 17px;
+    font-size: 14px;
+    font-weight: 500;
+    color: #0b121a;
+    line-height: 20px;
+}
+
+.requirements-type-purpose-list {
+    margin-top: 6px;
+    margin-left: 24px;
+
+    li {
+        width: 224px;
+        margin-left: 20px;
+        list-style: disc;
+        font-size: 14px;
+        font-family: PingFangSC-Regular, PingFang SC;
+        font-weight: 400;
+        color: #828c99;
+        line-height: 20px;
+    }
+}
+
+.requirements-type-payment {
+    margin-left: 24px;
+    margin-top: 20px;
+    font-size: 14px;
+    font-family: PingFangSC-Regular, PingFang SC;
+    font-weight: 400;
+    color: #828c99;
+    line-height: 20px;
+}
+
+.requirements-type-submit {
+    text-align: center;
+    margin-top: 80px;
+}
+</style>

+ 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>

+ 186 - 0
pages/frontend/requirements/components/Form_xuqiu.vue

@@ -0,0 +1,186 @@
+<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="pro_descrption">
+            <el-input v-model="modalFormData.pro_descrption" 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>
+                <!-- <div class="el-upload__tip" slot="tip">
+            拖拽或点击上
+          </div> -->
+            </el-upload>
+        </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>
+</div>
+</template>
+
+<script>
+import uploadFile from "@/mixins/uploadFile";
+export default {
+    components: {},
+    props: [],
+    data() {
+        return {
+            modalFormData: {
+                pro_descrption: "",
+                field103: false,
+                field104: null
+            },
+            rules: {
+                pro_descrption: [{
+                    required: true,
+                    message: "请输入您的需求",
+                    trigger: "blur"
+                }]
+            },
+            field104Action: "",
+            field104fileList: [],
+
+            successUploadFileMap: {},
+
+            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");
+    },
+    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];
+            // console.log(111,file, fileList)
+        },
+
+        async requestSubmit() {
+            let params = {
+                is_package: 1,
+                // hotsale_id:0,
+                pro_name: "需求梳理",
+                // budget:0,
+                pro_descrption: 0,
+                projectfiles: []
+            };
+            if (this.field104fileList.length > 0) {
+                let arr = this.field104fileList.map(item => {
+                    return {
+                        ...item.data
+                    };
+                });
+                params["projectfiles"] = JSON.stringify(arr);
+            }
+            params["pro_descrption"] = this.modalFormData.pro_descrption;
+
+            let that = this;
+            this.$axios
+                .$post(
+                    "/api/project/publish", {
+                        ...params
+                    }
+                )
+                .then(res => {
+                    if (Number(res.status) === 1) {
+                        this.$message.success("提交成功");
+                        this.$emit('formSubmit', 2, res.data)
+                    } else {
+                        this.$message.error("提交失败");
+                    }
+                });
+        }
+    }
+};
+</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;
+}
+</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>

+ 231 - 0
pages/frontend/requirements/components/Form_zhengbao.vue

@@ -0,0 +1,231 @@
+<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="pro_descrption">
+            <el-input v-model="modalFormData.pro_descrption" 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">2.您的预算大概多少?</div>
+        <el-row :gutter="15">
+
+            <el-col :span="6">
+                <el-form-item label-width="0" prop="budget">
+                    <el-input v-model="modalFormData.budget" placeholder="请输入预算" clearable :style="{width: '100%'}">
+                        <template slot="append">元</template>
+                    </el-input>
+                </el-form-item>
+            </el-col>
+        </el-row>
+
+        <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>
+</div>
+</template>
+
+<script>
+import uploadFile from "@/mixins/uploadFile";
+export default {
+    components: {},
+    props: [],
+    data() {
+        return {
+            modalFormData: {
+                pro_descrption: "",
+                field103: false,
+                field104: null,
+                is_need_manager: false,
+                budget: ""
+            },
+            rules: {
+                pro_descrption: [{
+                    required: true,
+                    message: "请输入您的需求",
+                    trigger: "blur"
+                }]
+            },
+            field104Action: "",
+            field104fileList: [],
+
+            successUploadFileMap: {},
+
+            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");
+    },
+    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];
+            // console.log(111,file, fileList)
+        },
+
+        async requestSubmit() {
+            let params = {
+                is_package: 1,
+                // hotsale_id:0,
+                pro_name: "项目整包",
+                // budget:0,
+                pro_descrption: "",
+                budget: 0,
+                projectfiles: []
+            };
+            if (this.field104fileList.length > 0) {
+                let arr = this.field104fileList.map(item => {
+                    return {
+                        ...item.data
+                    };
+                });
+                params["projectfiles"] = JSON.stringify(arr);
+            }
+            params["pro_descrption"] = this.modalFormData.pro_descrption;
+            params["budget"] = this.modalFormData.budget;
+            params["is_need_manager"] = this.modalFormData.is_need_manager == 1 ? 1 : 0;
+
+            let that = this;
+            this.$axios
+                .$post(
+                    "/api/project/publish", {
+                        ...params
+                    }
+                )
+                .then(res => {
+                    if (Number(res.status) === 1) {
+                        this.$message.success("提交成功");
+                        this.$emit('formSubmit', 2, res.data)
+                    } else {
+                        this.$message.error("提交失败");
+                    }
+                });
+        }
+    }
+};
+</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;
+}
+</style>

+ 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>

+ 20 - 0
pages/frontend/requirements/components/StepEnd.vue

@@ -0,0 +1,20 @@
+<template>
+<div class="step3">
+    <p>恭喜您,项目已提交功能!</p>
+    <p>官方客服将尽快联系您</p>
+</div>
+</template>
+
+<style lang="scss">
+.step3 {
+    margin-top:130px;
+    margin-bottom: 350px;
+    text-align: center;
+    p {
+        font-size: 20px;
+        font-weight: 400;
+        color: #0B121A;
+        line-height: 30px
+    }
+}
+</style>

+ 290 - 0
pages/frontend/requirements/index.vue

@@ -0,0 +1,290 @@
+<template>
+<div :class="mobile ? 'mobileMain' : ''" :style="{
+      marginTop: mainMarginTop,
+      marginBottom: mobile ? '0px' : '30px !important'
+    }">
+    <div class="requirements-container" v-if="!mobile">
+        <div class="requirements-title">发布需求</div>
+
+        <div class="requirements-step-container">
+            <div class="requirements-step">
+                <div class="requirements-step-status">
+                    <div class="requirements-step-icon" :class="FirstStepStatus">
+                        <div class="requirements-step-tips">
+                            选择需求类型
+                        </div>
+                    </div>
+                </div>
+                <div class="requirements-step-line" :class="FirstStepStatus"></div>
+            </div>
+
+            <div class="requirements-step">
+                <div class="requirements-step-status">
+                    <div class="requirements-step-icon" :class="secondStepStatus">
+                        <div class="requirements-step-tips">
+                            填写需求描述
+                        </div>
+                    </div>
+                </div>
+                <div class="requirements-step-line" :class="secondStepStatus"></div>
+            </div>
+
+            <div class="requirements-step">
+                <div class="requirements-step-status">
+                    <div class="requirements-step-icon" :class="thirdStepStatus">
+                        <div class="requirements-step-tips">
+                            发布成功
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+
+        <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>
+
+<script>
+import {
+    mapState
+} from "vuex";
+import qs from "qs";
+import FormFirstStep from "./components/FormFirstStep.vue";
+// 发布需求梳理表单
+import FormXuQiu from "./components/Form_xuqiu.vue";
+// 发布整包表单
+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";
+export default {
+    name: "SeoLearnList",
+    components: {
+        FormFirstStep,
+        FormXuQiu,
+        FormZhengBao,
+        FormYunDuan,
+        StepEnd,
+        FormRecomment
+    },
+    data() {
+        return {
+            baseUrl: "",
+            mobile: false,
+            // firstLoad: true,
+            isWeixinApp: true,
+
+            // 0 默认 1云端工作 2 项目整包 3 需求梳理
+            type: 0,
+
+            // 当前激活的状态
+            active: 0,
+            // wait process finish
+            activeStatus: "wait",
+
+            isShowFormRecomment: false,
+            projectid: ""
+        };
+    },
+    head() {
+        const {
+            title = "",
+                keyword = "",
+                description = "",
+                h1 = "",
+                canonical = "",
+                metaLocation
+        } = this.head || {};
+        let obj = {
+            title: title,
+            meta: [{
+                    name: "keywords",
+                    content: keyword
+                },
+                {
+                    name: "description",
+                    content: description
+                },
+                {
+                    name: "h1",
+                    content: h1
+                }
+            ],
+            link: [{
+                rel: "canonical",
+                href: canonical
+            }]
+        };
+        if (metaLocation) {
+            obj.meta.push({
+                name: "location",
+                content: metaLocation
+            });
+        }
+        return obj;
+    },
+    computed: {
+        ...mapState(["deviceType"]),
+        showWxHeader() {
+            return (
+                !this.deviceType.app &&
+                !this.isWeixinApp &&
+                (this.deviceType.android || this.deviceType.ios)
+            );
+        },
+        mainMarginTop() {
+            if (this.mobile && this.showWxHeader) {
+                return "64px !important";
+            } else if (this.mobile) {
+                return "0px !important";
+            } else {
+                return "20px !important";
+            }
+        },
+        FirstStepStatus: function () {
+            let status = "";
+            switch (this.active) {
+                case 1:
+                    status = this.activeStatus;
+                    break;
+                case 2:
+                    status = "success";
+                    break;
+                case 3:
+                    status = "success";
+                    break;
+                default:
+                    status = "wait";
+            }
+            return status;
+        },
+        secondStepStatus: function () {
+            let status = "";
+            switch (this.active) {
+                case 1:
+                    status = "wait";
+                    break;
+                case 2:
+                    status = this.activeStatus;
+                    break;
+                case 3:
+                    status = "success";
+                    break;
+                default:
+                    status = "wait";
+            }
+            return status;
+        },
+        thirdStepStatus: function () {
+            let status = "";
+            switch (this.active) {
+                case 1:
+                    status = "wait";
+                    break;
+                case 2:
+                    status = "wait";
+                    break;
+                case 3:
+                    status = "success";
+                    break;
+                default:
+                    status = "wait";
+            }
+            return status;
+        },
+        isFirstFormShow: function () {
+            return this.active == 1;
+        },
+        isFormXuqiuShow: function () {
+            return this.active == 2 && this.type == 3;
+        },
+        isFormZhengBaoShow: function () {
+            return this.active == 2 && this.type == 2;
+        },
+        isFormYunDuanShow: function () {
+            return this.active == 2 && this.type == 1;
+        },
+        isStepEndShow: function () {
+            return this.active == 3;
+        }
+    },
+    watch: {
+        "$route.query": function (val) {
+            if (val.step) {
+                this.active = Number(val.step);
+            } else {
+                this.active = 1;
+            }
+            if (val.type) {
+                this.type = Number(val.type);
+            } else {
+                this.type = 0;
+            }
+            this.activeStatus = "wait";
+        }
+    },
+    mounted() {
+        let query = this.$route.query;
+        if (query.step) {
+            this.active = Number(query.step);
+        } else {
+            this.active = 1;
+        }
+        if (query.type) {
+            this.type = Number(query.type);
+        } else {
+            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 = {};
+            if (step == 1) {
+                this.type = data;
+                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.activeStatus = "process";
+            this.$nextTick(() => {
+                this.$router.push({
+                    path: "/frontend/requirements",
+                    query: query
+                });
+            });
+        },
+        formChange(step, type) {
+            this.active = step;
+            this.activeStatus = type;
+        }
+    }
+};
+</script>
+
+<style lang="scss">
+@import "@/assets/css/requirements/index.scss";
+</style>

+ 88 - 0
pages/frontend/requirements/template.vue

@@ -0,0 +1,88 @@
+<template>
+<div :class="mobile ? 'mobileMain' : ''" :style="{marginTop: mainMarginTop, marginBottom: mobile ? '0px' : '30px !important'}">
+
+    <div v-if="!mobile"></div>
+</div>
+</template>
+
+<script>
+import {
+    mapState
+} from "vuex"
+import qs from "qs"
+
+export default {
+    name: 'SeoLearnList',
+    data() {
+        return {
+            baseUrl: '',
+            mobile: false,
+            // firstLoad: true,
+            isWeixinApp: true,
+        }
+    },
+    head() {
+        const {
+            title = "",
+                keyword = "",
+                description = "",
+                h1 = "",
+                canonical = "",
+                metaLocation
+        } = this.head || {}
+        let obj = {
+            title: title,
+            meta: [{
+                name: "keywords",
+                content: keyword
+            }, {
+                name: "description",
+                content: description
+            }, {
+                name: "h1",
+                content: h1
+            }],
+            link: [{
+                rel: "canonical",
+                href: canonical
+            }]
+        }
+        if (metaLocation) {
+            obj.meta.push({
+                name: "location",
+                content: metaLocation
+            })
+        }
+        return obj
+    },
+    computed: {
+        ...mapState(["deviceType"]),
+        showWxHeader() {
+            return !this.deviceType.app && !this.isWeixinApp &&
+                (this.deviceType.android || this.deviceType.ios)
+        },
+        mainMarginTop() {
+            if (this.mobile && this.showWxHeader) {
+                return '64px !important'
+            } else if (this.mobile) {
+                return '0px !important'
+            } else {
+                return '20px !important'
+            }
+        },
+
+    },
+
+    mounted() {
+        this.baseUrl = this.$store.state.domainConfig.siteUrl
+        this.isWeixinApp = navigator.userAgent.indexOf("miniProgram") > -1
+    },
+    methods: {
+
+    }
+}
+</script>
+
+<style lang="scss">
+
+</style>

+ 18 - 0
plugins/seoRouter.js

@@ -173,6 +173,24 @@ const extendRoutes = (routes, resolve) => {
         component: resolve(__dirname, '../pages/frontend/account/change.vue')
       }
     ],
+
+    // 发布需求
+    ...[
+      {
+        name: 'requirements',
+        path: "/frontend/requirements",
+        component: resolve(__dirname, '../pages/frontend/requirements/index.vue')
+      }
+    ],
+
+    // 开发者
+    ...[
+      {
+        name: 'SeoDeveloperIndex',
+        path: "/frontend/developer",
+        component: resolve(__dirname, '../pages/frontend/developer/index.vue')
+      }
+    ]
   );
 
   /**