Explorar o código

Merge branch 'dev' into dev_mjj_v4.36.1

* dev:
  发布文章
  添加新个人页面
  等级面板,绑定数据
  添加等级介绍

# Conflicts:
#	pages/frontend/developer/index.vue
martin.ma %!s(int64=4) %!d(string=hai) anos
pai
achega
dad5e25e0a

+ 178 - 23
assets/css/developer/index.scss

@@ -8,7 +8,7 @@
 .block {
   padding: 24px;
   background: #fff;
-   border-radius: 6px;
+  border-radius: 6px;
   margin-bottom: 20px;
 }
 .title {
@@ -168,7 +168,6 @@
   background-position: center;
   background-repeat: no-repeat;
   background-size: cover;
-
 }
 
 @for $i from 1 through 5 {
@@ -288,6 +287,11 @@
 
 .developer-workbench-user {
   display: flex;
+  
+  overflow: hidden;
+  // &-logined{
+  //   cursor: pointer;
+  // }
 }
 .developer-user-avatar {
   width: 48px;
@@ -309,16 +313,19 @@
   justify-content: space-around;
 }
 .developer-user-name {
-  font-size: 16px;
+  font-size: 12px;
   font-weight: 500;
   color: #0b121a;
-  line-height: 22px;
+  // line-height: 22px;
 }
 .developer-user-level {
-  font-size: 14px;
+  font-size: 12px;
   font-weight: 400;
   color: #828c99;
-  line-height: 20px;
+  // margin-top: 5px;
+  line-height: 1;
+  // line-height: 20px;
+  cursor: pointer;
 }
 
 .user-title {
@@ -718,7 +725,8 @@
 }
 
 .dynamic-link-content {
-  a,span {
+  a,
+  span {
     font-size: 14px;
 
     font-weight: 400;
@@ -768,27 +776,174 @@
   background-image: url("~@/assets/img/developer/ic_ community_praise_s@2x.png");
 }
 
-.arrow_icon{
-  display: inline-block;
-  height:18px;
-  width:18px;
+.arrow_icon {
   position: relative;
-  top: 1px;
-  &::after{
-    content:"";
+  &::after {
+    content: "";
     position: absolute;
-    left:50%;
-    top:50%;
-
-    width:7px;
-    height:7px;
-    border-right: 1px solid #828C99;
-    border-bottom: 1px solid #828C99;
-    transform: rotate(-45deg) translate(-50%,-50%);
+    right: -12px;
+    top: 50%;
+    width: 8px;
+    height: 8px;
+    margin-top: -4px;
+    border-right: 1px solid #828c99;
+    border-bottom: 1px solid #828c99;
+    transform: rotate(-45deg);
   }
 }
 
-.nologin{
+.nologin {
   cursor: pointer;
 }
 
+// 经验等级介绍
+.ma-level-dialog {
+  background: #ffffff;
+  border-radius: 8px !important;
+}
+
+.dev-level-container {
+  display: flex;
+  justify-content: space-between;
+}
+.dev-level-highline {
+  color: #308eff;
+}
+.dev-level-detail {
+  width: 328px;
+  margin-left: 59px;
+}
+.dev-level-introduce {
+  width: 360px;
+  margin-right: 46px;
+  background: #f7f8fa;
+  border-radius: 8px;
+  padding-bottom: 20px;
+}
+
+.dev-level-info {
+  width: 108px;
+  height: 98px;
+  margin: 0 auto;
+  background: url('"~@/assets/img/developer/level_bg.png') no-repeat 50% 50%;
+  background-size: 100% auto;
+  position: relative;
+}
+.dev-level-info-tips {
+  position: absolute;
+  top: 35px;
+  left: 50%;
+  transform: translateX(-50%);
+  font-size: 16px;
+  font-family: Arial-BoldMT, Arial;
+  font-weight: normal;
+  color: #308eff;
+  line-height: 18px;
+}
+
+.dev-level-line-container {
+  margin-top: 17px;
+  overflow: hidden;
+
+  p {
+    font-size: 14px;
+    font-family: PingFangSC-Regular, PingFang SC;
+    font-weight: 400;
+    color: #666666;
+    line-height: 22px;
+    margin-top: 6px;
+  }
+}
+
+.dev-level-line {
+  height: 6px;
+  background: #d8d8d8;
+  border-radius: 3px;
+  overflow: hidden;
+  position: relative;
+  span {
+    position: absolute;
+    left: 0;
+    top: 0;
+    bottom: 0;
+    width: 100%;
+    transform: translateX(-100%);
+    transition: all 0.8s;
+    transition-delay: 0.5s;
+    background: #308eff;
+    border-radius: 3px;
+  }
+}
+
+.dev-level-line-left {
+  float: left;
+}
+.dev-level-line-right {
+  float: right;
+}
+
+.dev-level-details-list {
+  margin-top: 31px;
+}
+
+.dev-level-details-item {
+  display: flex;
+  margin-bottom: 12px;
+}
+.dev-level-details-tit {
+  flex: 1;
+  font-size: 14px;
+  font-family: PingFangSC-Regular, PingFang SC;
+  font-weight: 400;
+  color: #000000;
+  line-height: 20px;
+  .label {
+    color: #999999;
+    font-size: 12px;
+    margin-left: 12px;
+  }
+}
+
+.dev-level-introduce-section {
+  margin-left: 20px;
+  margin-right: 20px;
+  padding-top: 40px;
+  &:nth-child(1) {
+    padding-top: 50px;
+  }
+
+  h3 {
+    font-size: 14px;
+    font-family: PingFangSC-Medium, PingFang SC;
+    font-weight: 500;
+    color: #308eff;
+    line-height: 20px;
+  }
+  p {
+    margin-top: 8px;
+    font-size: 14px;
+    font-family: PingFangSC-Regular, PingFang SC;
+    font-weight: 400;
+    color: #000000;
+    line-height: 22px;
+  }
+}
+.dev-level-table-container {
+  margin-left: 20px;
+  margin-right: 20px;
+  margin-top: 30px;
+}
+.dev-level-table {
+  border-collapse: collapse;
+  width: 100%;
+  background:#fff;
+  th,td{
+    border: 1px solid #ccc;
+    background:#fff;
+    color: #000;
+    height:29px;
+    vertical-align: middle;
+    text-align:left;
+    text-indent: 5px;
+  }
+}

+ 248 - 0
assets/css/frontend/personal.scss

@@ -0,0 +1,248 @@
+%block {
+  background: #ffffff;
+  border-radius: 8px;
+}
+
+.personal-container {
+  width: 1100px;
+  margin-left: auto;
+  margin-right: auto;
+  display: flex;
+  justify-content: space-between;
+}
+
+.personal-main {
+  width: 730px;
+}
+.personal-side {
+  width: 350px;
+}
+
+.personal-info-container {
+  display: flex;
+  padding-left: 20px;
+  padding-right: 16px;
+  @extend %block;
+}
+.personal-user {
+  margin-top: 30px;
+  margin-bottom: 35px;
+  flex: 1;
+  display: flex;
+}
+.personal-user-avatar {
+  width: 68px;
+  height: 68px;
+  margin-right: 10px;
+  position: relative;
+  font-size: 0;
+  line-height: 0;
+  letter-spacing: 0;
+}
+.personal-user-avatar-img {
+  width: 100%;
+  height: 100%;
+  border-radius: 100%;
+  overflow: hidden;
+}
+.personal-user-tag {
+  position: absolute;
+  bottom: 0;
+  right: 0;
+  width: 18px;
+  height: 16px;
+  background: url("~@/assets/img/frontend/personal/tag.png") no-repeat 50% 50%;
+  background-size: 18px 16px;
+}
+.personal-user-name {
+  font-size: 16px;
+  font-family: PingFangSC-Medium, PingFang SC;
+  font-weight: 500;
+  color: #0b121a;
+  line-height: 22px;
+  margin-bottom: 4px;
+  display: flex;
+  align-items: center;
+}
+.personal-user-text {
+  font-size: 14px;
+  font-family: PingFangSC-Regular, PingFang SC;
+  font-weight: 400;
+  color: #828c99;
+  line-height: 20px;
+  margin-bottom: 6px;
+  &:nth-last-child(1) {
+    margin-bottom: 0;
+  }
+}
+
+.personal-user-follow {
+  display: flex;
+  flex-direction: column;
+  // justify-content: flex-end;
+  align-items: flex-end;
+}
+.personal-report {
+  margin-top: 8px;
+  a {
+    display: inline-block;
+    font-size: 12px;
+    font-family: PingFangSC-Regular, PingFang SC;
+    font-weight: 400;
+    color: #828c99;
+    line-height: 17px;
+  }
+}
+.personal-share-area {
+  margin-top: 14px;
+}
+.personal-share-btn {
+  display: inline-block;
+  width: 18px;
+  height: 18px;
+  margin-left: 21px;
+  border-radius: 100%;
+  overflow: hidden;
+  background-color: #ccc;
+}
+
+.personal-follow-btn {
+  margin-top: 30px;
+
+  width: 90px;
+  height: 28px;
+  background: #e6f1ff;
+  border-radius: 2px;
+  font-size: 13px;
+  font-family: PingFangSC-Medium, PingFang SC;
+  font-weight: 500;
+  color: #308eff;
+  line-height: 28px;
+  text-align: center;
+  cursor: pointer;
+}
+
+// 侧栏
+
+.personal-page-link {
+  padding: 16px;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+
+  font-size: 14px;
+  font-family: PingFangSC-Medium, PingFang SC;
+  font-weight: 500;
+  color: #0b121a;
+  margin-bottom: 20px;
+  cursor: pointer;
+  @extend %block;
+  .arrow {
+    display: block;
+    width: 7px;
+    height: 7px;
+    border-color: #828c99;
+    border-style: solid;
+    border-width: 0 1px 1px 0;
+    transform: rotate(-45deg);
+  }
+}
+
+// 关注统计
+.personal-follow-info{
+  display: flex;
+  position: relative;
+  margin-bottom: 20px;
+  @extend %block;
+  &::after {
+    content: "";
+    position: absolute;
+    width: 1px;
+    height: 33px;
+    background: #aeb6ca;
+    left: 50%;
+    top: 50%;
+    transform: translate(-50%, -50%);
+  }
+}
+.personal-follow-item {
+  width: 50%;
+  height: 85px;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+}
+.personal-follow-label {
+  font-size: 14px;
+  font-family: PingFangSC-Medium, PingFang SC;
+  font-weight: 500;
+  color: #828c99;
+  line-height: 20px;
+}
+.personal-follow-count {
+    margin-top: 4px;
+  font-size: 16px;
+  font-family: PingFangSC-Medium, PingFang SC;
+  font-weight: 500;
+  color: #0b111a;
+  line-height: 21px;
+}
+
+.personal-ad-container{
+    height:247px;
+    @extend %block;
+}
+
+
+// tab框样式
+.personal-content-container{
+    margin-top: 20px;
+    @extend %block;
+}
+.personal-content-tab{
+    display: flex;
+    border-bottom: 1px solid #EBECED;
+}
+.personal-tab-item{
+    width: 122px;
+    height: 62px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+
+    font-size: 16px;
+    font-family: PingFangSC-Regular, PingFang SC;
+    font-weight: 400;
+    color: #4B5D73;
+
+    cursor: pointer;
+
+    transition: all 0.3s;
+    position: relative;
+    z-index: 1;
+    &::after{
+        content: "";
+        position: absolute;
+        z-index: 2;
+        bottom: -1px;
+        left: 50%;
+        transform:translateX(-50%);
+        width: 24px;
+        height: 4px;
+        background: #308EFF;
+        border-radius: 3px;
+        transition: all 0.3s;
+        opacity: 0;
+    }
+    &.cur{
+        font-size: 16px;
+        font-family: PingFangSC-Medium, PingFang SC;
+        font-weight: 500;
+        color: #0B121A;
+        &::after{
+            opacity: 1;
+        }
+        
+    }
+}

BIN=BIN
assets/img/developer/level_bg.png


BIN=BIN
assets/img/frontend/personal/comment.png


BIN=BIN
assets/img/frontend/personal/comment2.png


BIN=BIN
assets/img/frontend/personal/like.png


BIN=BIN
assets/img/frontend/personal/like2.png


BIN=BIN
assets/img/frontend/personal/read.png


BIN=BIN
assets/img/frontend/personal/share.png


BIN=BIN
assets/img/frontend/personal/tag.png


+ 125 - 0
pages/frontend/developer/component/level-introduce.vue

@@ -0,0 +1,125 @@
+<template>
+<div class="dev-level-container">
+    <!-- 等级明细 -->
+    <div class="dev-level-detail">
+        <div class="dev-level-info">
+            <span class="dev-level-info-tips">Lv.{{user.dynamic_rand}}</span>
+        </div>
+        <div class="dev-level-line-container">
+            <div class="dev-level-line">
+                <span :style="{
+                    'transform': 'translateX(-'+(100-lineW)+'%)'  }"></span>
+            </div>
+            <p class="dev-level-line-left">
+                <span class="dev-level-highline">Lv.{{user.dynamic_rand}}</span>经验值{{user.dynamic_experience}}
+            </p>
+            <p class="dev-level-line-right">
+                升级还需<span class="dev-level-highline">{{user.total_experience - user.dynamic_experience}}</span>经验值
+            </p>
+        </div>
+
+        <ul class="dev-level-details-list">
+            <li v-for="(item,index) in dynamic_rand_task"  :key="index" class="dev-level-details-item">
+                <div class="dev-level-details-tit">
+                    <span>{{item.title}}</span>
+                    <span v-if="item.desc != ''" class="label">{{item.desc}}</span>
+                </div>
+                <div class="dev-level-tips">+{{item.score}}</div>
+            </li>
+           
+        </ul>
+    </div>
+
+    <!-- 等级介绍 -->
+    <div class="dev-level-introduce">
+        <div class="dev-level-introduce-section" v-for="(item,index) in dynamic_rand_desc" :key="index">
+            <h3>{{index+1}}.{{item.title}}</h3>
+            <p>
+                {{item.desc}}
+            </p>
+        </div>
+        <div class="dev-level-table-container">
+            <table class="dev-level-table">
+                <tr>
+                    <th>等级</th>
+                    <th>经验值</th>
+                </tr>
+                <tr v-for="(item,index) in dynamic_rand" :key="index">
+                    <td>{{item.rand}}</td>
+                    <td>{{item.score}}</td>
+                </tr>
+            </table>
+        </div>
+    </div>
+</div>
+</template>
+
+<script>
+export default {
+    props: {
+        show: {
+            type: Boolean,
+            default: false
+        },
+        levelInfo: {
+            type: Object,
+            default: function () {
+                return {
+                    user: {
+                        dynamic_experience: 0,
+                        dynamic_rand: 0,
+                        total_experience: 0
+                    },
+                    dynamic_rand: [],
+                    dynamic_rand_task: [],
+                    dynamic_rand_desc: []
+                }
+            }
+        }
+    },
+    computed: {
+        'user': function () {
+            return this.levelInfo.user
+        },
+        'dynamic_rand': function () {
+            return this.levelInfo.dynamic_rand
+        },
+        'dynamic_rand_task': function () {
+            return this.levelInfo.dynamic_rand_task
+        },
+        'dynamic_rand_desc': function () {
+            return this.levelInfo.dynamic_rand_desc
+        }
+    },
+
+    watch: {
+        'show': function (val) {
+            if (val) {
+                this.$nextTick(() => {
+                    let total = this.user.total_experience
+                    let result = (this.user.dynamic_experience / total) * 100
+                    this.lineW = parseInt(result, 10)
+                })
+            } else {
+                this.lineW = 0
+            }
+        }
+    },
+    created() {
+        if (this.show) {
+            this.$nextTick(() => {
+                let total = this.user.total_experience
+                let result = (this.user.dynamic_experience / total) * 100
+                this.lineW = parseInt(result, 10)
+            })
+        } else {
+            this.lineW = 0
+        }
+    },
+    data() {
+        return {
+            lineW: 0
+        };
+    }
+};
+</script>

+ 23 - 2
pages/frontend/developer/developData.js

@@ -31,13 +31,15 @@ export default class DealSeoData {
             balanceInfo,
             workPlatInfo,
             typeList,
-            hotInfo
+            hotInfo,
+            levelInfo
         ] = await Promise.all([
             this._getUserInfo(),
             this._getUserBalance(),
             this._getWorkPlatInfo(),
             this._getTypeList(),
-            this._getHotData()
+            this._getHotData(),
+            this._getLevelInfo()
         ])
 
         let firstType = typeList[0]
@@ -54,6 +56,7 @@ export default class DealSeoData {
             workPlatInfo,
             typeList,
             hotInfo,
+            levelInfo,
             dynamicList,
             firstType,
             isMore
@@ -206,6 +209,24 @@ export default class DealSeoData {
         return hotInfo
     }
 
+
+    // 获取经验接口
+    async _getLevelInfo(){
+        let res = await this.$axios.$post('/uapi/dynamic/get_dynamic_experience_info')
+        let levelInfo = {}
+        if (Number(res.status) === 1) {
+            let {user,dynamic_rand,dynamic_rand_task,dynamic_rand_desc} = res.data
+
+            levelInfo = {
+                user,dynamic_rand,dynamic_rand_task,dynamic_rand_desc
+            }
+        } else if (Number(res.status) === 40001) {
+            this.isExist = false
+        }
+        return levelInfo
+
+    }
+
     dealThisMeta() {
         let title = ''
         let descriptionTitle = ''

+ 179 - 0
pages/frontend/personal/index.vue

@@ -0,0 +1,179 @@
+<template>
+<div :class="mobile ? 'mobileMain' : ''" :style="{marginTop: mainMarginTop, marginBottom: mobile ? '0px' : '30px !important'}">
+
+    <div v-if="!mobile" class="personal-container">
+        <div class="personal-main">
+            <!-- 个人信息框 -->
+            <div class="personal-info-container">
+                <div class="personal-user">
+                    <div class="personal-user-avatar">
+                        <img class="personal-user-avatar-img" src="https://iph.href.lu/200x200?fg=666666&bg=cccccc" />
+                        <span class="personal-user-tag"></span>
+                    </div>
+                    <div class="personal-user-info">
+                        <div class="personal-user-name">
+                            <span class="name-cotent">陈世杰jackey(web前端工程师)</span>
+                            <LevelTag :level="2"></LevelTag>
+                        </div>
+                        <div class="personal-user-text">2天前在线 · 431浏览</div>
+                        <div class="personal-user-text">2022年2月加入</div>
+                    </div>
+                </div>
+                <div class="personal-user-follow">
+                    <p class="personal-report"><a href="">举报</a></p>
+                    <div class="personal-share-area">
+                        <span class="personal-share-btn"></span>
+                        <span class="personal-share-btn"></span>
+                    </div>
+                    <div class="personal-follow-btn">
+                        关注
+                    </div>
+                </div>
+            </div>
+
+            <!-- 内容tab -->
+            <div class="personal-content-container">
+                <div class="personal-content-tab">
+                    <!-- tab框 -->
+                    <div v-for="item in tabs" @click="tabSelected = item.id" :key="item.id" class="personal-tab-item" :class="{'cur':tabSelected == item.id}">
+                        {{item.label}}  {{item.count}}
+                    </div>
+                </div>
+                <div class="personal-content-main">
+                    <!-- 列表内容 -->
+                </div>
+            </div>
+        </div>
+        <div class="personal-side">
+            <div class="personal-page-link">
+                <span>Ta的开发工作主页</span>
+                <span class="arrow"></span>
+            </div>
+
+            <div class="personal-follow-info">
+                <div class="personal-follow-item">
+                    <span class="personal-follow-label">关注了</span>
+                    <span class="personal-follow-count">5</span>
+                </div>
+                <div class="personal-follow-item">
+                    <span class="personal-follow-label">关注者</span>
+                    <span class="personal-follow-count">5</span>
+                </div>
+            </div>
+
+            <div class="personal-ad-container">
+
+            </div>
+        </div>
+    </div>
+</div>
+</template>
+
+<script>
+import {
+    mapState
+} from "vuex"
+import qs from "qs"
+
+import LevelTag from "@/components/level-tag.vue";
+export default {
+    name: 'PersonalIndex',
+    components:{
+        LevelTag
+    },
+    data() {
+        return {
+            baseUrl: '',
+            mobile: false,
+            // firstLoad: true,
+            isWeixinApp: true,
+            tabSelected:1,
+            tabs:[
+                {
+                    id:1,
+                    label: "动态",
+                    count:10
+                },
+                {
+                    id:2,
+                    label: "点赞",
+                    count:10
+                },
+                {
+                    id:3,
+                    label: "文章",
+                    count:10
+                },
+                {
+                    id:4,
+                    label: "视频课程",
+                    count:10
+                }
+            ]
+        }
+    },
+    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">
+@import "@/assets/css/frontend/personal.scss";
+</style>

+ 5 - 0
plugins/seoRouter.js

@@ -191,6 +191,11 @@ const extendRoutes = (routes, resolve) => {
         path: '/frontend/name_cert_fail',
         component: resolve(__dirname, '../pages/frontend/name_cert/fail.vue')
       },
+      {
+        name: "PersonalIndex",
+        path: '/frontend/personal',
+        component: resolve(__dirname, '../pages/frontend/personal/index.vue')
+      }
     ],
 
     // 发布需求