martin.ma il y a 4 ans
Parent
commit
cfce10f166

+ 278 - 0
assets/css/dynamic/info.scss

@@ -0,0 +1,278 @@
+.dynamic-info-wrapper-mobile {
+  //   margin-top: 10px;
+}
+
+.dynamic-info-main {
+  margin-left: 10px;
+  margin-right: 10px;
+  background-color: #fff;
+  border-radius: 10px;
+  padding: 13px 10px 18px;
+}
+
+.dynamic-info-user {
+  display: flex;
+  align-items: center;
+}
+
+.dynamic-info-user-avatar {
+  width: 38px;
+  height: 38px;
+  border-radius: 100%;
+  overflow: hidden;
+  img {
+    width: 100%;
+    height: 100%;
+    border-radius: 100%;
+  }
+}
+
+.dynamic-info-user-info {
+  margin-left: 10px;
+  flex: 1;
+  margin-right: 10px;
+}
+
+.dynamic-info-user-nickname {
+  font-size: 14px;
+  font-family: PingFangSC, PingFangSC-Semibold;
+  font-weight: 600;
+  color: #222222;
+  line-height: 20px;
+}
+
+.dynamic-info-user-level {
+  display: inline-block;
+  background: #ffd257;
+  border-radius: 3px;
+  height: 13px;
+  padding: 0 5px;
+
+  font-size: 10px;
+  font-family: FontName, FontName-Regular;
+  font-weight: 400;
+  color: #222222;
+  line-height: 13px;
+  letter-spacing: -0.95px;
+}
+
+.dynamic-info-user-title {
+  font-size: 11px;
+  font-family: PingFangSC, PingFangSC-Regular;
+  font-weight: 400;
+  text-align: left;
+  color: #999999;
+  line-height: 16px;
+}
+
+.dynamic-info-user-menu {
+  width: 20px;
+  height: 20px;
+    background:url("~@/assets/img/dynamic/menu.png") no-repeat 50% 50%;
+    background-size: 100% auto;
+}
+
+.dynamic-info-content {
+  margin-top: 9px;
+  font-size: 15px;
+  font-family: PingFangSC, PingFangSC-Medium;
+  font-weight: 500;
+  color: #222222;
+  line-height: 21px;
+}
+.dynamic-info-type-area {
+  margin-top: 8px;
+  margin-bottom: 8px;
+}
+.dynamic-info-type {
+  display: inline-block;
+  padding: 0 10px;
+  line-height: 24px;
+  height: 24px;
+  background: rgba(48, 142, 255, 0.11);
+  border-radius: 3px;
+
+  font-size: 11px;
+  font-family: PingFangSC, PingFangSC-Regular;
+  font-weight: 400;
+  color: #308eff;
+}
+
+.dynamic-info-img-list {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.dynamic-info-img-item {
+  width: 27.8vw;
+  img {
+    width: 100%;
+    border-radius: 6px;
+  }
+}
+
+.dynamic-info-resources-link {
+  margin-top: 10px;
+  background: rgba(244, 245, 249, 0.8);
+  border-radius: 10px;
+  padding: 7px 10px 8px;
+  display: flex;
+  align-items: center;
+}
+
+.dynamic-info-resources-img {
+  width: 34px;
+  height: 34px;
+  border-radius: 5px;
+  overflow: hidden;
+  img {
+    width: 100%;
+    height: 100%;
+    border-radius: 5px;
+  }
+}
+
+.dynamic-info-resources-title {
+  flex: 1;
+  height: 34px;
+  line-height: 34px;
+  margin-left: 10px;
+  font-size: 14px;
+  font-family: PingFangSC, PingFangSC-Regular;
+  font-weight: 400;
+  text-align: left;
+  color: #222222;
+}
+
+.dynamic-info-like {
+  margin-top: 15px;
+  display: flex;
+  font-size: 14px;
+  font-family: PingFangSC, PingFangSC-Regular;
+  font-weight: 400;
+  color: #222222;
+}
+
+.dynamic-info-like-item {
+  height: 20px;
+  padding-left: 23px;
+  display: flex;
+  align-items: center;
+}
+.dynamic-info-like-count {
+    background:url("~@/assets/img/dynamic/like.png") no-repeat left 50%;
+    background-size: auto 100%;
+}
+.dynamic-info-comment-count {
+    background:url("~@/assets/img/dynamic/comment.png") no-repeat left 50%;
+    background-size: auto 100%;
+  margin-left: 30px;
+}
+
+.dynamic-comment-main {
+  margin-left: 10px;
+  margin-right: 10px;
+}
+.dynamic-comment-title {
+  margin-top: 9px;
+  margin-bottom: 5px;
+  font-size: 16px;
+  font-family: PingFangSC, PingFangSC-Medium;
+  font-weight: 500;
+  text-align: left;
+  color: #222222;
+  line-height: 22px;
+}
+
+.dynamic-comment-list {
+  background: #ffffff;
+  margin-bottom: 20px;
+  border-radius: 10px;
+  
+}
+
+.dynamic-comment-item {
+  display: flex;
+  padding: 21px 10px 0px;
+  &:nth-last-child(1){
+    .dynamic-comment-content{
+        &::after{
+            display: none
+        }
+    }
+ }
+}
+
+.dynamic-comment-avatar {
+  width: 40px;
+  height: 40px;
+  border-radius: 40px;
+  overflow: hidden;
+  img {
+    width: 40px;
+    height: 40px;
+    border-radius: 40px;
+  }
+}
+
+.dynamic-comment-info {
+  flex: 1;
+  margin-left: 10px;
+}
+
+.dynamic-comment-item-top {
+  display: flex;
+  align-items: center;
+}
+.dynamic-comment-nickname {
+  flex: 1;
+  margin-right: 10px;
+  font-size: 14px;
+  font-family: PingFangSC, PingFangSC-Regular;
+  font-weight: 400;
+  text-align: left;
+  color: #666666;
+  line-height: 20px;
+}
+
+.dynamic-comment-time {
+  font-size: 12px;
+  font-family: PingFangSC, PingFangSC-Regular;
+  font-weight: 400;
+  text-align: center;
+  color: #999999;
+  line-height: 17px;
+  margin-right: 4px;
+}
+.dynamic-comment-more {
+  width: 20px;
+  height: 20px;
+  background:url("~@/assets/img/dynamic/menu.png") no-repeat 50% 50%;
+  background-size: 100% auto;
+}
+
+.dynamic-comment-content {
+    // padding-right: 10px;
+  margin-top: 4px;
+  position: relative;
+  overflow: hidden;
+  font-size: 15px;
+  font-family: PingFangSC, PingFangSC-Regular;
+  font-weight: 400;
+  text-align: left;
+  color: #333333;
+  line-height: 1.5em;
+  padding-bottom: 18px;
+  &::after {
+    content: "";
+    position: absolute;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    height: 1px;
+    background-color: #000;
+    opacity: 0.08;
+    transform: translateY(50%);
+  }
+}

BIN
assets/img/dynamic/comment.png


BIN
assets/img/dynamic/like.png


BIN
assets/img/dynamic/menu.png


+ 115 - 0
components/dynamic/DynamicInfo.js

@@ -0,0 +1,115 @@
+export default class DynamicInfoData {
+    constructor({$axios, req, app, redirect, error}) {
+        this.$axios = $axios
+        this.req = req
+        this.app = app
+        this.redirect = redirect
+        this.error = error
+        this.consultDetail= {}
+        this.from = ''
+        this.isExist = true
+    }
+
+    async dealData() {
+        let {
+            name,
+            path,
+            params,
+            fullPath,
+            query
+        } = this.app.context.route
+        const dynamicId = params.id || ''
+
+        // 重定向
+        // if (path.indexOf('/frontend/consult/user') > -1) {
+        //     this.redirect(301, '/c/' + uid)
+        // }
+
+        const dynamicDetail = await this._fetchData(dynamicId)
+
+        return {
+            isExist: this.isExist,
+            dynamicDetail,
+            mobile: this.app.$deviceType.isMobile(),
+            // head: this.dealThisMeta(),
+        }
+    }
+
+    async _fetchData(dynamicId){
+        let res = await this.$axios.$post('/uapi/dynamic/get_dynamic_detail', { 
+            dynamic_id:dynamicId
+         })
+
+         let dynamicDetail = {}
+         if (Number(res.status) === 1) {
+            let dynamicList = res.data.list
+            console.log(dynamicList)
+            dynamicDetail = {...dynamicList[2]}
+         }else{
+            this.isExist = false
+         }
+         return dynamicDetail
+    }   
+
+    dealThisMeta() {
+        let title = ''
+        let descriptionTitle = ''
+        let description = ''
+        let canonical = ''
+
+        if (!this.isExist) {
+            // 页面不存在时
+            return {
+                title: "页面不存在-程序员客栈",
+                keyword: "",
+                description: "",
+                h1: "",
+                canonical: "",
+                metaLocation: ""
+            }
+        }
+        const nickname = this.consultDetail.user.nickname
+
+        this.consultDetail.sale_list.forEach((item, index) => {
+            if (index === 0) {
+                title = item.title.trim()
+                description = item.content.trim()
+            }
+            // if (index !== this.consultDetail.sale_list.length - 1) {
+            //     description += `${item.title}、`
+            // } else {
+            //     description += item.title
+            // }
+        })
+
+        if (description.length > 15) {
+            description = description.substring(0, 15)
+        }
+        if (title.length > 15) {
+            descriptionTitle = title.substring(0, 15)
+        } else {
+            descriptionTitle = title
+        }
+
+        if (this.req) {
+            const { headers: { host }, url } = this.req
+
+            //拼接canonical
+            if (host.indexOf('local') !== -1) {
+                canonical = 'http://' + host + url
+            } else {
+                canonical = 'https://' + host + url
+            }
+        }
+
+        let head = {
+            title: `${title}-程序员客栈咨询服务`,
+            keyword: `${this.consultDetail.user.company},${this.consultDetail.user.direction_name},${title}`,
+            description: `${nickname}可以为您提供:${descriptionTitle},程序员客栈邀请到国内外互联网名企资深工作者,为您提供1对1技术咨询服务。`,
+            h1: "",
+            canonical: canonical,
+            metaLocation: ""
+        }
+        return head
+    }
+}

+ 209 - 0
pages/frontend/dynamic/info.vue

@@ -0,0 +1,209 @@
+<template>
+<ErrorPage404 v-if="!isExist"></ErrorPage404>
+<div v-else :class="mobile ? 'mobileMain' : 'mobileWeb'" :style="{marginTop: mainMarginTop}">
+    <div class="dynamic-info-wrapper-mobile">
+        <div class="dynamic-info-main">
+            <div class="dynamic-info-user">
+                <div class="dynamic-info-user-avatar">
+                    <img :src="dynamicDetail.user_info.icon_url" />
+                </div>
+                <div class="dynamic-info-user-info">
+                    <div class="dynamic-info-user-nickname">{{dynamicDetail.title}} <span class="dynamic-info-user-level">F{{dynamicDetail.user_info.freework_level}}</span></div>
+                    <div class="dynamic-info-user-title">
+                        <span>{{dynamicDetail.title}}</span>
+                        <span>{{dynamicDetail.user_info.tag[1] ? "· "+dynamicDetail.user_info.tag[1].name : ''}}</span>
+                    </div>
+                </div>
+                <div class="dynamic-info-user-menu"></div>
+            </div>
+            <div class="dynamic-info-content">
+                {{dynamicDetail.title}}
+            </div>
+            <div class="dynamic-info-type-area">
+                <div class="dynamic-info-type">
+                    # {{dynamicDetail.type_text}}
+                </div>
+            </div>
+            <div class="dynamic-info-img-list">
+                <div class="dynamic-info-img-item" v-for="item in dynamicDetail.img" :key="item.img">
+                    <img :src="item.img" />
+                </div>
+            </div>
+
+            <div class="dynamic-info-resources-link">
+                <div class="dynamic-info-resources-img">
+                    <img src="https://iph.href.lu/100x100?fg=666666&bg=cccccc" />
+                </div>
+                <div class="dynamic-info-resources-title">仿微信Flutter开发源码</div>
+            </div>
+
+            <div class="dynamic-info-like">
+                <div class="dynamic-info-like-item dynamic-info-like-count">
+                    {{dynamicDetail.z_num}}
+                </div>
+                <div class="dynamic-info-like-item dynamic-info-comment-count">
+                    {{dynamicDetail.c_num}}
+                </div>
+            </div>
+        </div>
+
+        <div class="dynamic-comment-main">
+            <div class="dynamic-comment-title">评论</div>
+
+            <ul class="dynamic-comment-list">
+                <li class="dynamic-comment-item">
+                    <div class="dynamic-comment-avatar">
+                        <img src="https://iph.href.lu/100x100?fg=666666&bg=cccccc" />
+                    </div>
+                    <div class="dynamic-comment-info">
+                        <div class="dynamic-comment-item-top">
+                            <div class="dynamic-comment-nickname">Keimi</div>
+                            <div class="dynamic-comment-time">2020-03-12</div>
+                            <div class="dynamic-comment-more"></div>
+                        </div>
+                        <div class="dynamic-comment-content">
+                            我还在很愉快 期待下次合作
+                        </div>
+                    </div>
+                </li>
+                <li class="dynamic-comment-item">
+                    <div class="dynamic-comment-avatar">
+                        <img src="https://iph.href.lu/100x100?fg=666666&bg=cccccc" />
+                    </div>
+                    <div class="dynamic-comment-info">
+                        <div class="dynamic-comment-item-top">
+                            <div class="dynamic-comment-nickname">Keimi</div>
+                            <div class="dynamic-comment-time">2020-03-12</div>
+                            <div class="dynamic-comment-more"></div>
+                        </div>
+                        <div class="dynamic-comment-content">
+                            我还在很愉快 期待下次合作期待下次合作期待下次合作期待下次合作期待下次合作期待下次合作期待下次合作期待下次合作期待下次合作期待下次合作期待下次合作期待下次合作
+                        </div>
+                    </div>
+                </li>
+                <li class="dynamic-comment-item">
+                    <div class="dynamic-comment-avatar">
+                        <img src="https://iph.href.lu/100x100?fg=666666&bg=cccccc" />
+                    </div>
+                    <div class="dynamic-comment-info">
+                        <div class="dynamic-comment-item-top">
+                            <div class="dynamic-comment-nickname">Keimi</div>
+                            <div class="dynamic-comment-time">2020-03-12</div>
+                            <div class="dynamic-comment-more"></div>
+                        </div>
+                        <div class="dynamic-comment-content">
+                            我还在很愉快 期待下次合作
+                        </div>
+                    </div>
+                </li>
+            </ul>
+        </div>
+    </div>
+</div>
+</template>
+
+<script>
+import {
+    mapState
+} from "vuex"
+import DynamicInfoData from "@/components/dynamic/DynamicInfo.js"
+import qs from "qs"
+import ErrorPage404 from "@/components/error_page/404.vue"
+
+export default {
+    name: 'SeoDynamicDetail',
+    data() {
+        return {
+            baseUrl: '',
+            isWeixinApp: true,
+            isExist: true,
+        }
+    },
+    components: {
+        ErrorPage404
+    },
+    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
+            }, {
+                name: "viewport",
+                content: "width=device-width, initial-scale=1.0, viewport-fit=cover"
+            }],
+            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 '0 !important'
+            } else if (this.mobile) {
+                return '0px !important'
+            } else {
+                return '20px !important'
+            }
+        }
+    },
+    async asyncData({
+        ...params
+    }) {
+        let dynamicInfoData = new DynamicInfoData(params)
+        let ans = await dynamicInfoData.dealData()
+
+        return {
+            ...ans
+        }
+    },
+    mounted() {
+        const self = this
+        this.isWeixinApp = navigator.userAgent.indexOf("miniProgram") > -1
+    },
+    methods: {
+
+    }
+}
+</script>
+
+<style lang="scss" scoped>
+@import "@/assets/css/dynamic/info.scss";
+</style>
+<style lang="scss">
+strong {
+    font-weight: bold !important;
+}
+
+em {
+    font-style: italic !important;
+}
+</style>

+ 11 - 0
plugins/seoRouter.js

@@ -128,6 +128,8 @@ const extendRoutes = (routes, resolve) => {
       component: resolve(__dirname, '../pages/frontend/skill/detail/_id.vue')
     }],
 
+
+
     // 技术等级认证
     // ...[
     //   {
@@ -152,6 +154,15 @@ const extendRoutes = (routes, resolve) => {
     //     component: resolve(__dirname, '../pages/skill_cert/cert_form.vue')
     //   },
     // ]
+
+    // 分享动态详情页
+    ...[
+      {
+        name: 'SeoDynamicDetail',
+        path: '/dynamic/info/:id?',
+        component: resolve(__dirname, '../pages/frontend/dynamic/info.vue')
+      }
+    ]
   );
 
   /**