Browse Source

合并yb5.0,解决冲突

cxmo 2 years ago
parent
commit
3a92d323bf

+ 60 - 0
api/question.js

@@ -0,0 +1,60 @@
+//问答社区模块
+import { httpGet, httpPost } from "@/utils/request.js";
+
+/**
+ * 问答列表
+ * @param chart_permission_id
+ * @param reply_status 0-全部 2-待回答 3-已回答
+ */
+ export const apiQuestionList=params=>{
+    return httpGet('/community/question/list',params)
+}
+/**
+ * FICC品种权限
+ */
+ export const apiOptionList=params=>{
+    return httpGet('/company/permission/tree',params)
+}
+/**
+ * 发布提问
+ * @param question_content
+ */
+ export const apiPubAsk=params=>{
+    return httpPost('/community/question/ask',params)
+}
+/**
+ * 问答列表数量统计
+ */
+ export const apiBarTotal=params=>{
+    return httpGet('/community/question/list/total',params)
+}
+
+/**
+ * 发布回答
+ * @param question_id 
+ * @param audio_list 
+ */
+ export const apiReplayAsk=params=>{
+    return httpPost('/community/question/reply',params)
+}
+
+/**
+ * 问答详情
+ * @param question_id 
+ */
+ export const apiGetQuestion=params=>{
+    return httpGet('/community/question/detail',params)
+}
+/**
+ * 我的-未读数
+ */
+ export const apiGetUnread=params=>{
+    return httpGet('/community/question/unread',params)
+}
+/**
+ * 问答已读(批量)
+ * @param question_ids
+ */
+export const apiSetRead = params=>{
+    return httpPost('/community/question/reply/read',params)
+}

+ 3 - 2
mixin/index.js

@@ -5,7 +5,7 @@ moment.locale('zh-cn');
 import {globalImgUrls} from "../utils/config"
 import store from '@/store'
 
-const tabbarPathList=['pages/activity/activity','pages/buy/buy','pages/chart/chart','pages/user/user','pages/report/report']
+const tabbarPathList=['pages/activity/activity','pages/buy/buy','pages/chart/chart',/* 'pages/user/user', */'pages/report/report','pages/question/question']
 
 module.exports = {
   watch: {
@@ -57,7 +57,8 @@ module.exports = {
   data() {
     return {
       globalImgUrls:globalImgUrls,// 图片资源
-      globalBgMusic:uni.getBackgroundAudioManager()
+      globalBgMusic:uni.getBackgroundAudioManager(),
+      globalRecorder:uni.getRecorderManager(),//录音
     };
   },
   onLoad(options) {

+ 295 - 0
mixin/questionMixin.js

@@ -0,0 +1,295 @@
+/* 问答社区 公共逻辑 */
+import {
+    apiQuestionList
+} from '@/api/question'
+import { apiApplyPermission, apiUserInfo } from '@/api/user'
+const moment = require('@/utils/moment-with-locales.min')
+moment.locale('zh-cn');
+export default {
+    data() {
+        return {
+            moment: moment,
+            innerAudio: null, //该页面的音频
+            currentAudioMsg: {
+                id: '',
+                audioCurrentTime: 0, //音频播放实时时间
+                audioTime: 0, //当前音频时间
+                audioCurrentUrl: '', //当前音频地址
+            }, //当前正在播放音频的一些信息
+            pupData: {
+                show: false,
+                content: '', //弹窗html字符串
+                type: '',
+                mobile: "",
+                customer_info: {}
+            },
+            page: 1,
+            pageSize: 20,
+            finished: false,
+            selectId: -1,
+        }
+    },
+    computed:{
+        isUserResearcher(){
+			//内部人员+研究员
+			if(this.userInfo.is_inner===1&&this.userInfo.is_researcher===1){
+				return true	
+			}
+			return false
+		}
+    },
+    onLoad() {
+        //this.initAudio()
+    },
+    onShow(){
+        this.initAudio()
+    },
+    onHide(){
+        this.resetAudio()
+        this.destroyAudio()
+    },
+    onUnload() {
+        //this.destroyAudio()
+    },
+    methods: {
+        //初始化audio
+        initAudio() {
+            this.innerAudio = uni.createInnerAudioContext()
+            this.handleAudioFun()
+        },
+        //销毁audio
+        destroyAudio() {
+            if (this.innerAudio) {
+                this.innerAudio.destroy()
+            }
+        },
+        //重置音频播放信息
+        resetAudio(){
+            this.innerAudio.pause();
+            this.questionList.map((i) => {
+                if(i.id===this.currentAudioMsg.id){
+                    i.answer.isplay = false
+                    i.answer.ispause = false
+                }
+            })
+            this.changeCurrentAudio({
+                id: '',
+                answer: {
+                    source: '',
+                    audioTime: 0
+                }
+            })
+        },
+        //audio事件
+        handleAudioFun() {
+            this.innerAudio.onPlay(() => {
+                console.log('开始了')
+                this.questionList.map(i => {
+                    if (i.id === this.currentAudioMsg.id) {
+                        i.loading = false
+                    }
+                })
+            })
+            this.innerAudio.onTimeUpdate(() => {
+                //console.log('时间更新')
+                this.currentAudioMsg.audioCurrentTime = this.innerAudio.currentTime * 1000
+            })
+            this.innerAudio.onPause(() => {
+                console.log("暂停");
+            })
+            this.innerAudio.onEnded(() => {
+                console.log('音频播放完毕')
+                const {
+                    id
+                } = this.currentAudioMsg
+                this.questionList.map(i => {
+                    if (i.id === id) {
+                        i.answer.isplay = false
+                        i.answer.ispause = false
+                    }
+                })
+                this.changeCurrentAudio({
+                    id: '',
+                    answer: {
+                        source: '',
+                        audioTime: 0
+                    }
+                })
+            })
+        },
+        //播放音频
+        handleAudioPlay() {
+            this.innerAudio.onCanplay(() => {
+                this.innerAudio.play()
+            })
+        },
+        //切换当前播放音频
+        changeCurrentAudio(item) {
+            const {
+                id
+            } = item
+            const {
+                source,
+                audioTime
+            } = item.answer
+            this.currentAudioMsg = {
+                id: id,
+                audioCurrentTime: 0 * 1000,
+                audioTime: audioTime,
+                audioCurrentUrl: source
+            }
+            this.questionList.map(i => {
+                if (i.id === item.id) {
+                    i.loading = true
+                }
+            })
+        },
+        //获取问答列表:status(问题状态) only_mine(只看我的)
+        async getQuestionList(status,onlyMine=0) {
+            let questionData = []
+            const res = await apiQuestionList({
+                page_index: this.page,
+                page_size: this.pageSize,
+                chart_permission_id: this.selectId === -1 ? '' : this.selectId,
+                reply_status: status,
+                only_mine:onlyMine
+            })
+            if (res.code === 200) {
+                if (res.data) {
+                    questionData = res.data
+                } else {
+                    this.finished = true
+                }
+            }
+            let tempArr = []
+            questionData.forEach(item => {
+                let temp = item
+                let audio_url = '',
+                    audio_play_seconds = 0;
+                //问题状态为已回答,取audio_list第一项为音频
+                if (item.reply_status === 3) {
+                    audio_url = item.audio_list[0].audio_url
+                    audio_play_seconds = item.audio_list[0].audio_play_seconds
+                }
+                //问题状态不为已回答,取默认值
+                temp.answer = {
+                    source: audio_url,
+                    audioTime: parseInt(audio_play_seconds) * 1000,
+                    isplay: false,
+                    ispause: false
+                }
+                temp.id = item.community_question_id
+                temp.loading = false
+                tempArr.push(temp)
+            })
+            if (this.page > 1) {
+                this.questionList = this.questionList.concat(tempArr)
+            } else {
+                this.questionList = tempArr
+            }
+
+        },
+        //点击某条音频
+        handleAudio(item) {
+            //如果没有权限,弹窗并return
+            if (!item.auth_ok) {
+            	this.initPupData(item)
+            	return
+            }
+            const {
+                source,
+                isplay
+            } = item.answer
+            if (isplay) {
+                //说明是播放->暂停
+                this.innerAudio.pause()
+            } else if (item.id === this.currentAudioMsg.id) {
+                //说明是暂停->播放
+                this.innerAudio.play()
+            } else {
+                //console.log('aaa', source, this.innerAudio.src)
+                //说明是第一次播放或点击其他播放项
+                this.changeCurrentAudio(item)
+                this.innerAudio.stop()
+                this.innerAudio.src = source
+                /* this.innerAudio.play() */
+                this.handleAudioPlay()
+
+            }
+            this.questionList.map((i) => {
+                if (i.id === item.id) {
+                    if (i.answer.isplay) {
+                        i.answer.ispause = true
+                    }
+                    i.answer.isplay = !i.answer.isplay
+                } else {
+                    i.answer.isplay = false
+                    i.answer.ispause = false
+                    i.loading = false
+                }
+            })
+        },
+        //初始化无权限弹窗
+        initPupData(item) {
+            let str = '<p>您暂无权限查看语音回复</p>'
+            const {
+                type,
+                mobile,
+                name,
+                customer_info
+            } = item.permission_info
+            if (type === 'apply') {
+                this.pupData.type = 'apply'
+                str += '<p>若想查看可以申请开通</p>'
+            }
+            if (type === 'contact') {
+                this.pupData.mobile = mobile + ''
+                this.pupData.saleName = name
+                this.pupData.type = 'contact'
+                str += `<p>若想查看可以联系对口销售</p>`
+            }
+            this.pupData.customer_info = customer_info
+            this.pupData.content = str
+            this.pupData.show = true
+        },
+        //拨号
+        handleCallPhone(tel) {
+            uni.makePhoneCall({
+                phoneNumber: tel ? tel : '123456',
+                success: () => {
+                    this.pupData.show = false
+                }
+            });
+        },
+        //申请权限
+        async handleApply() {
+            if (this.pupData.customer_info.has_apply) { //已经申请过
+                this.pupData.content = `<p>您已提交过申请,请耐心等待</p>`
+                this.pupData.type = ''
+            } else {
+                if (!this.pupData.customer_info.status || this.pupData.customer_info.status != '流失') {
+                    uni.navigateTo({
+                        url: "/pages-applyPermission/applyPermission?source=5&form_page=问答社区"
+                    })
+                } else { //主动调一次申请权限接口 
+                    const res = await apiApplyPermission({
+                        company_name: this.pupData.customer_info.company_name,
+                        real_name: this.pupData.customer_info.name,
+                        source: 5,
+                        from_page: '问答社区'
+                    })
+                    if (res.code === 200) {
+                        //重新获取页面数据
+                        const pages = getCurrentPages();
+                        const page = pages[pages.length - 1];
+                     /*    page.onLoad(); */
+                        page.onShow();
+                    }
+                    this.pupData.content = `<p>申请已提交</p><p>请等待销售人员与您联系</p>`
+                    this.pupData.type = ''
+
+                }
+            }
+        },
+    }
+}

+ 1 - 1
pages-applyPermission/applyPermission.vue

@@ -78,7 +78,7 @@ export default {
                 tel: '',
                 permission: '',
             },
-            source:"",//来源 1-我的 2-活动 3-图库
+            source:"",//来源 1-我的 2-活动 3-图库 4-研报 5-问答
             from_page:''
         }
     },

+ 780 - 0
pages-question/answerDetail.vue

@@ -0,0 +1,780 @@
+<template>
+  <view class="answerdetail-page flex-column">
+    <template v-if="questionItem">
+      <view class="question-wrap">
+        <view class="question-item">
+          <view class="question-info">
+            <view style="flex: 1" class="question-title">
+              <text class="item-label">{{
+                questionItem.chart_permission_name
+              }}</text>
+              {{ questionItem.question_content }}
+            </view>
+            <view class="item-answer" v-if="questionItem.reply_status === 3">
+              <view class="answer" @click.stop="handleAudio(questionItem)">
+                <template v-if="!questionItem.loading">
+                  <image
+                    class="music-img"
+                    :src="questionItem.answer.isplay ? playImgSrc : pauseImgSrc"
+                    mode="widthFix"
+                  />
+                  <template
+                    v-if="
+                      questionItem.answer.isplay || questionItem.answer.ispause
+                    "
+                  >
+                    <text>{{
+                      questionItem.answer.audioTime -
+                        currentAudioMsg.audioCurrentTime >
+                      0
+                        ? moment(
+                            (questionItem.answer.audioTime -
+                              currentAudioMsg.audioCurrentTime) *
+                              1000
+                          ).format("mm:ss")
+                        : "00:00"
+                    }}</text>
+                  </template>
+                  <template v-else>
+                    <text>{{
+                      moment(questionItem.answer.audioTime * 1000).format(
+                        "mm:ss"
+                      )
+                    }}</text>
+                  </template>
+                </template>
+                <template v-else>
+                  <image
+                    class="load-img"
+                    src="../static/loading.png"
+                    mode="aspectFill"
+                  />
+                  <text>{{
+                    moment(questionItem.answer.audioTime).format("mm:ss")
+                  }}</text>
+                </template>
+              </view>
+            </view>
+          </view>
+          <text class="item-time">提问时间:{{ questionItem.create_time }}</text>
+        </view>
+      </view>
+      <view
+        class="record-wrap flex-column"
+        v-if="questionItem.reply_status === 2&&isUserResearcher"
+      >
+        <view class="record flex-column" v-if="questionItem.recordStatus !== 4">
+          <view class="no-record" v-if="questionItem.recordStatus === 1">
+            <image
+              src="./static/record.png"
+              mode="widthFix"
+              style="width: 90rpx; height: 180rpx"
+            />
+            <view>无录音(录音时长超过三分钟自动结束)</view>
+          </view>
+          <!-- <view class="record-time" v-else>{{ audioTime }}</view> -->
+          <view class="recode-image" v-else>
+            <scroll-view scroll-x style="height: 100rpx" class="scroll-view" :scroll-left="scrollTop" @scrolltolower="handleScrolltolower" :show-scrollbar="false">
+              <view style="width:100%;height:100rpx;display:inline-block;"></view>
+              <image
+                src="./static/record-img.png"
+                mode="scaleToFill"
+              />
+              <image
+                src="./static/record-img.png"
+                mode="scaleToFill"
+              />
+              <image
+                src="./static/record-img.png"
+                mode="scaleToFill"
+              />
+            </scroll-view>
+          </view>
+        </view>
+        <view
+          class="record-tool flex-column"
+          v-if="questionItem.recordStatus !== 4"
+        >
+          <view class="hint" v-if="questionItem.recordStatus === 1"
+            >点击开始录音</view
+          >
+          <view class="record-time" v-else>{{ audioTime }}</view>
+          <view
+            class="record-btn-wrap center"
+            v-if="questionItem.recordStatus === 1"
+          >
+            <view class="switch" @click="changeRecodeStatus"> </view>
+          </view>
+          <view class="record-btn-wrap" v-else>
+            <text
+              @click="questionItem.recordStatus === 3 && handleRecode('delete')"
+              v-if="questionItem.recordStatus >= 2"
+              :class="{ active: questionItem.recordStatus === 3 }"
+              >删除</text
+            >
+            <view class="switch" @click="changeRecodeStatus">
+              <image
+                v-if="questionItem.recordStatus >= 2"
+                :src="
+                  questionItem.recordStatus === 2 ? playImgSrc : pauseImgSrc
+                "
+                :key="playIconKey"
+                :style="{
+                  'margin-left': questionItem.recordStatus !== 2 ? '7rpx' : 0,
+                  width: '36rpx',
+                  height: '44rpx',
+                }"
+                mode="widthFix"
+              />
+            </view>
+            <text
+              @click="handleRecode('finish')"
+              v-if="questionItem.recordStatus >= 2"
+              :class="{ active: questionItem.recordStatus >= 2 }"
+              >完成</text
+            >
+          </view>
+        </view>
+        <view class="record-play" v-if="questionItem.recordStatus === 4">
+          <view class="audio-wrap">
+            <view v-if="pageLoading">音频生成中...</view>
+            <view class="play" v-else>
+              <van-icon
+                :name="isplay ? 'pause' : 'play'"
+                @click="handleAudioByReplay"
+                color="#E6B77DFF"
+                size="64rpx"
+                style="align-items: flex-start; margin-left: -20rpx"
+              />
+
+              <!-- 进度条 -->
+              <view class="slider-box">
+                <slider
+                  :value="currentAudioMsg.audioCurrentTime"
+                  :max="currentAudioMsg.audioTime"
+                  @change="sliderChange($event)"
+                  @changing="sliderChanging"
+                  activeColor="#E6B77DFF"
+                  backgroundColor="#EBEBEBFF"
+                  block-color="#E6B77DFF"
+                  block-size="12"
+                />
+                <view class="slider-time">
+                  <text>{{
+                    moment(currentAudioMsg.audioCurrentTime * 1000).format(
+                      "mm:ss.SS"
+                    )
+                  }}</text>
+                  <text>{{
+                    moment(currentAudioMsg.audioTime * 1000).format("mm:ss.SS")
+                  }}</text>
+                </view>
+              </view>
+            </view>
+          </view>
+          <view class="audio-delete" @click="handleRecode('delete')">
+            <image
+              src="./static/delerecord.png"
+              mode="heightFix"
+              style="width: 32rpx; height: 35rpx"
+            />
+            <text>删除</text></view
+          >
+          <view class="audio-pub" @click="handleRecode('pub')">发布</view>
+        </view>
+      </view>
+    </template>
+  </view>
+</template>
+
+<script>
+import mixin from "../mixin/questionMixin";
+import { apiReplayAsk, apiGetQuestion, apiSetRead } from "@/api/question";
+import { uploadAudioToServer } from "@/utils/upload";
+export default {
+  mixins: [mixin],
+  data() {
+    return {
+      questionItem: null /* {
+        recordStatus: 1, //1:未录音;2:正在录音;3:已暂停;4:完成录音
+      }, */,
+      pauseImgSrc: "../static/question/recordplay.png",
+      playImgSrc: "../static/question/recordpause.png",
+      innerAudio: null, //该页面的音频
+      audioCount: 0, //录音计时,毫秒
+      recordStartTime: null, //录音开始时间
+      recordStopTime: null, //录音停止的时间
+      audioTime: "00:00", //录音时间(格式化后):string
+      timer: null,
+      audioItem: null,
+      pageLoading: false,
+      playIconKey: 0,
+      isplay: false,
+      isSlider: false,
+      scrollTop:0,
+      isStart:false,//判断录音是否开始
+      //globalRecorder:uni.getRecorderManager(),
+      /* userInfo:{
+				is_inner:1,//0:外部客户;1内部员工
+				status:'试用',
+				is_suspend:0,
+				is_researcher:0
+			},//mock用户信息 */
+    };
+  },
+  onLoad(options) {
+    this.initAudio();
+    this.getQuestionItem(options.id);
+  },
+  onShow(){
+    uni.authorize({
+      scope: 'scope.record',
+      success() {
+        
+      },
+      fail(e){
+        console.log('fail',e);
+      }
+    })
+  },
+  onUnload() {
+    this.resetAudio();
+    this.destroyAudio();
+    //录音时误操作退出页面的情况
+    if(this.questionItem.recordStatus!==4&&this.questionItem.recordStatus!==1){
+      this.globalRecorder.stop()
+    } 
+  },
+  methods: {
+    //初始化audio
+    initAudio() {
+      this.innerAudio = uni.createInnerAudioContext();
+      this.handleAudioFun();
+      this.handleRecorderFun();
+    },
+    resetAudio(){
+      this.innerAudio.stop();
+      this.isplay = false;
+      this.changeCurrentAudio({
+          id: '',
+          answer: {
+              source: '',
+              audioTime: 0
+          }
+      })
+    },
+    //audio事件
+    handleAudioFun() {
+      this.innerAudio.onPlay(() => {
+        this.isplay = true;
+        console.log("播放录音了");
+        this.questionItem.loading = false;
+      });
+      this.innerAudio.onTimeUpdate(() => {
+        //console.log("时间更新", this.innerAudio.currentTime);
+        /* this.currentAudioMsg.audioCurrentTime = parseInt(
+          this.innerAudio.currentTime
+        ); */
+        this.currentAudioMsg.audioCurrentTime = this.innerAudio.currentTime;
+      });
+      this.innerAudio.onSeeked(() => {
+        //取this.innerAudio.currentTime为0
+        console.log("seek完成");
+        this.isSlider = false;
+      });
+      this.innerAudio.onPause(() => {
+        this.isplay = false;
+        console.log("暂停");
+        console.log(this.innerAudio.paused);
+      });
+      this.innerAudio.onEnded(() => {
+        console.log("音频播放完毕");
+        this.questionItem.answer.isplay = false;
+        this.questionItem.answer.ispause = false;
+        /* this.changeCurrentAudio({
+          id: "",
+          answer: {
+            source: "",
+            audioTime: 0,
+          },
+        }); */
+        this.currentAudioMsg.audioCurrentTime = 0;
+        this.isplay = false;
+      });
+    },
+    //录音事件
+    handleRecorderFun() {
+      this.globalRecorder.onStart(() => {
+        console.log("开始录音");
+        uni.hideToast();
+        this.clockTime();
+      });
+      this.globalRecorder.onPause(() => {
+        console.log("暂停录音");
+        this.cleanTime();
+      });
+      this.globalRecorder.onStop((res) => {
+        console.log("录音完成");
+        //录音自动结束 和 暂停时点击删除 的情况
+        if (this.questionItem.recordStatus === 2) {
+          this.questionItem.recordStatus = 4;
+        }
+        console.log('status',this.questionItem.recordStatus)
+        console.log("res", JSON.stringify(res));
+        this.cleanTime();
+        //初始化音频播放
+        this.innerAudio.stop();
+        this.isplay = false;
+        this.innerAudio.src = res.tempFilePath;
+        console.log('秒数',this.audioCount)
+        this.changeCurrentAudio({
+          id: "",
+          answer: {
+            source: res.tempFilePath,
+            audioTime: this.audioCount/1000,
+          },
+        });
+        this.pageLoading = false;
+      });
+      this.globalRecorder.onError((res) => {
+        console.log('err',res.errMsg)
+        uni.showToast({
+          title:res.errMsg,
+          icon:'none'
+        })
+        //开始录音失败
+        if(res.errMsg.includes('start')){
+          this.questionItem.recordStatus = 1;
+        }
+      });
+      /* this.globalRecorder.onFrameRecorded((res) => {
+        console.log("?", res);
+      }); */
+    },
+    async getQuestionItem(id) {
+      const res = await apiGetQuestion({
+        question_id: id,
+      });
+      if (res.code === 200) {
+        this.questionItem = res.data;
+        const { audio_list } = res.data;
+        let temp = {};
+        if (audio_list.length > 0) {
+          temp = {
+            source: res.data.audio_list[0].audio_url,
+            audioTime: parseInt(res.data.audio_list[0].audio_play_seconds),
+            isplay: false,
+            ispause: false,
+          };
+        } else {
+          temp = {
+            source: "",
+            audioTime: 0,
+            isplay: false,
+            ispause: false,
+          };
+        }
+        let readKey = "";
+        const { is_inner } = this.userInfo;
+        if (is_inner === 1) {
+          readKey = "replier_is_read";
+        } else {
+          readKey = "is_read";
+        }
+        //console.log('readKey',readKey)
+        this.questionItem[readKey] !== 1 &&
+          (await apiSetRead({
+            question_ids: this.questionItem.community_question_id + "",
+          }));
+        this.questionItem.id = res.data.community_question_id;
+        this.questionItem.answer = temp;
+        this.questionItem.loading = false;
+        this.questionItem.recordStatus = res.data.reply_status === 3 ? 4 : 1;
+      }else{
+        //问题被删除的情况,返回小程序首页
+        setTimeout(()=>{
+          uni.switchTab({ url:'/pages/report/report' });
+        },1000)
+        
+      }
+    },
+    changeRecodeStatus() {
+      console.log('a',this.questionItem.recordStatus)
+      //根据questionItem.recordStatus
+      if (this.questionItem.recordStatus === 1) {
+        const that = this
+        //检查是否有权限
+        uni.getSetting({
+          success(res){
+            console.log('res',res)
+            if(res.authSetting['scope.record']){
+              that.startRecord()
+            }else{
+              that.getRecordAuth()
+            }
+          }
+        });
+      } else if (this.questionItem.recordStatus === 2) {
+        //暂停录音
+        this.globalRecorder.pause();
+        this.questionItem.recordStatus = 3;
+      } else if (this.questionItem.recordStatus === 3) {
+        //继续录音
+        this.globalRecorder.resume();
+        this.clockTime();
+        this.questionItem.recordStatus = 2;
+      } else {
+        //结束录音
+        this.globalRecorder.stop();
+        //this.cleanTime();
+      }
+    },
+    //获取录音授权
+    getRecordAuth(){
+      const {community_question_id} = this.questionItem
+      uni.openSetting({
+        success(res){
+          //刷新页面
+          if(res.authSetting['scope.record']){
+            uni.redirectTo({url: `/pages-question/answerDetail?id=${community_question_id}`})
+          }
+        }
+      })
+      //this.questionItem.recordStatus = 1
+    },
+    //开启录音
+    startRecord(){
+      console.log(this.globalRecorder)
+      this.globalRecorder.start({ duration: 180000, format: "mp3" });
+      uni.showToast({
+        title:'加载中',
+        icon:'loading'
+      })
+      this.questionItem.recordStatus = 2;
+    },
+    //上传音频
+    async uploadAudio() {
+      const res = await uploadAudioToServer(this.innerAudio.src);
+      if (res.code === 200) {
+        this.audioItem = res.data;
+      } else {
+        //重新录
+        this.questionItem.recordStatus = 1;
+        this.audioCount = 0;
+        this.audioTime = this.moment(this.audioCount).format("mm:ss");
+      }
+    },
+    //录音操作:完成/删除/发布
+    async handleRecode(type) {
+      if (type==='finish') {
+        this.questionItem.recordStatus = 4;
+        this.changeRecodeStatus();
+      }
+      if (type === "finish") {
+        //生成音频,更改页面布局
+        this.pageLoading = true;
+      } else if (type === "delete") {
+        //重新录
+        if(this.questionItem.recordStatus===3)this.globalRecorder.stop();
+        this.questionItem.recordStatus = 1;
+        this.innerAudio.stop();
+        this.isplay = false;
+        this.audioItem = null;
+        this.audioCount = 0;
+        this.scrollTop = 0;
+        this.recordStartTime = null;
+        this.audioTime = this.moment(this.audioCount * 1000).format("mm:ss");
+      } else {
+        //发布
+        if (!this.audioItem) {
+          await this.uploadAudio();
+        }
+        //如果上传音频成功
+        if (this.questionItem.recordStatus === 4) {
+          //发布回答
+          const res = await apiReplayAsk({
+            question_id: this.questionItem.community_question_id,
+            audio_list: [{ ...this.audioItem, sort: 1 }],
+          });
+          if (res.code === 200) {
+            uni.showToast({
+              title: "发布成功",
+              icon: "success",
+              duration: 500,
+            });
+            setTimeout(() => {
+              //关闭当前页面,跳转到我的回答
+              uni.navigateBack({ delta: 1 });
+            }, 500);
+          }
+        }
+      }
+    },
+    //(提问者)问题已被回答,点击回答音频
+    handleAudio(item) {
+      const { source, isplay, ispause } = item.answer;
+      if (isplay) {
+        //说明是播放->暂停
+        this.innerAudio.pause();
+        this.questionItem.answer.isplay = false;
+        this.questionItem.answer.ispause = true;
+      } else if (ispause) {
+        //说明是暂停->播放
+        this.innerAudio.play();
+        this.questionItem.answer.isplay = true;
+        this.questionItem.answer.ispause = false;
+      } else {
+        //console.log("aaa", source, this.innerAudio.src);
+        //说明是第一次播放或播放完
+        this.changeCurrentAudio(item);
+        this.innerAudio.stop();
+        this.innerAudio.src = source;
+        this.handleAudioPlay();
+        this.questionItem.answer.isplay = true;
+      }
+    },
+    //(回答者)问题被回答,点击回答音频
+    handleAudioByReplay() {
+      this.isplay = !this.isplay;
+      if (this.innerAudio.paused) {
+        this.innerAudio.play();
+      } else {
+        this.innerAudio.pause();
+      }
+      this.playIconKey++; //更新音频播放图标
+    },
+    //拖动音频进度条
+    sliderChange(e) {
+      console.log("拖动完成?");
+      //this.innerAudio.pause();
+      const value = e.detail.value;
+      this.innerAudio.seek(value);
+      this.currentAudioMsg.audioCurrentTime = value;
+    },
+    sliderChanging() {
+      this.isSlider = true;
+    },
+    //切换当前播放音频
+    changeCurrentAudio(item) {
+      const { id } = item;
+      const { source, audioTime } = item.answer;
+      this.currentAudioMsg = {
+        id: id,
+        audioCurrentTime: 0 * 1000,
+        audioTime: audioTime,
+        audioCurrentUrl: source,
+      };
+      if (id) {
+        console.log("?");
+        this.questionItem.loading = true;
+      }
+    },
+    //录音计时
+    clockTime() {
+      console.log("开始录音计时");
+      if (!this.recordStartTime) {
+        this.recordStartTime = Date.now();
+      }
+      //console.log(this.recordStartTime);
+      this.timer = setInterval(() => {
+        if (this.timer) {
+          this.audioCount += 30;
+          this.audioTime = this.moment(this.audioCount).format("mm:ss.SS");
+          //this.audioTime = this.moment(Date.now() - this.recordStartTime).format("mm:ss.SS");
+          this.scrollTop+=1;
+        }
+      }, 30);
+    },
+    //清除录音计时
+    cleanTime() {
+      console.log("结束录音计时");
+      //console.log('aa',this.recordStopTime)
+      clearTimeout(this.timer);
+      this.playIconKey++; //更新录音暂停播放图标
+      //this.audioTime = this.moment(this.audioCount).format("mm:ss.SS");
+    },
+    //scroll-view滑动到最右
+    handleScrolltolower(){
+      console.log('a',this.scrollTop)
+      this.scrollTop-=150;
+    }
+  },
+};
+</script>
+
+<style scoped lang="scss">
+.flex-column {
+  display: flex;
+  flex-direction: column;
+}
+
+.answerdetail-page {
+  padding: 50rpx 30rpx 0 30rpx;
+  height: calc(100vh - calc(50px + env(safe-area-inset-bottom)));
+  box-sizing: border-box;
+  .question-wrap {
+    .question-item::after {
+      height: 0;
+    }
+  }
+  .record-wrap {
+    margin: 0 -30rpx;
+    flex: 1;
+    .record {
+      flex: 1;
+      justify-content: center;
+      align-items: center;
+      .no-record {
+        text-align: center;
+        color: #999999ff;
+        font-size: 28rpx;
+        height:130rpx;
+        image {
+          width: 94rpx;
+        }
+      }
+    /*   .record-time {
+        justify-self: flex-end;
+        font-size: 60rpx;
+      } */
+      .recode-image {
+        width: 100%;
+        height: 100%;
+        background-color: #fafafaff;
+        display: flex;
+        align-items: center;
+        .scroll-view {
+          width: 100%;
+          white-space: nowrap;
+          ::-webkit-scrollbar{
+            width:0;
+            height:0;
+            display:none;
+            color:transparent;
+          }
+          image {
+            width: 421rpx;
+            height: 100rpx;
+            margin-right: 6rpx;
+          }
+        }
+      }
+    }
+    .record-tool {
+      justify-content: center;
+      align-items: center;
+      border-top: 1rpx solid #e6e6e6ff;
+      height: 400rpx;
+      position: relative;
+      .hint {
+        color: #ee3636ff;
+        font-size: 28rpx;
+        position: absolute;
+        top: 50rpx;
+      }
+      .record-time{
+        font-size: 60rpx;
+      }
+      .record-btn-wrap {
+        margin-top: 38rpx;
+        display: flex;
+        width: 100%;
+        justify-content: space-around;
+        align-items: center;
+        &.center {
+          justify-content: center;
+        }
+        text {
+          color: #999999ff;
+          font-size: 32rpx;
+          &.active {
+            color: #ee3636ff;
+          }
+        }
+        .switch {
+          width: 118rpx;
+          height: 118rpx;
+          padding: 12rpx;
+          box-sizing: border-box;
+          border-radius: 50%;
+          border: 1rpx solid #ee3636ff;
+          /* background-color: rgb(68, 46, 46); */
+          position: relative;
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          image {
+            position: absolute;
+            width: 36rpx;
+            z-index: 5;
+          }
+          &::after {
+            content: "";
+            display: inline-block;
+            width: 100%;
+            height: 100%;
+            border-radius: 50%;
+            background-color: #ee3636ff;
+          }
+        }
+      }
+    }
+    .record-play {
+      margin: 20rpx 30rpx;
+      .audio-wrap {
+        background-color: #fdf8f2ff;
+        padding: 30rpx;
+        border-radius: 16rpx;
+      }
+      .play {
+        display: flex;
+        justify-content: space-between;
+        .slider-box {
+          margin-left: 15rpx;
+          flex: 1;
+          slider {
+            margin: 15rpx 0 0 0;
+          }
+          .slider-time {
+            display: flex;
+            justify-content: space-between;
+            text {
+              color: #999999ff;
+              font-size: 22rpx;
+            }
+          }
+        }
+      }
+      .audio-delete {
+        margin-top: 20rpx;
+        width: 100%;
+        height: 40rpx;
+        display: flex;
+        justify-content: flex-end;
+        align-items: center;
+        image {
+          height: 40rpx;
+        }
+        text {
+          margin-left: 10rpx;
+          color: #999999ff;
+          font-size: 28rpx;
+        }
+      }
+      .audio-pub {
+        margin-top: 120rpx;
+        background-color: #e6b77dff;
+        color: #fff;
+        height: 80rpx;
+        line-height: 80rpx;
+        border-radius: 40rpx;
+        text-align: center;
+        position: relative;
+        width: 390rpx;
+        left: 50%;
+        margin-left: -170rpx;
+      }
+    }
+  }
+}
+</style>

+ 263 - 0
pages-question/answerList.vue

@@ -0,0 +1,263 @@
+<template>
+  <view class="answer-page">
+    <view class="answer-bar">
+      <view
+        class="bar-item"
+        :class="{ active: item.key === selectKey }"
+        @click="changeBar(item)"
+        v-for="item in barList"
+        :key="item.key"
+        >{{ item.label + "(" + item.num + ")" }}</view
+      >
+    </view>
+    <view class="answer-list">
+      <view class="report-empty-box" v-if="questionList.length == 0">
+        <image :src="globalImgUrls.activityNoAuth" mode="widthFix" style="width:100%;"/>
+        <view>暂无数据</view>
+      </view>
+      <view
+        class="question-item"
+        v-for="item in questionList"
+        :key="item.community_question_id"
+        @click="toDetail(item)"
+      >
+        <view class="question-info">
+          <view style="flex: 1" class="question-title">
+            <text
+              class="item-label"
+              v-if="isUserResearcher || item.reply_status === 3"
+              >{{ item.chart_permission_name }}</text
+            >
+            {{ item.question_content }}
+          </view>
+          <view class="item-answer" v-if="item.reply_status === 3">
+            <view class="answer" @click.stop="handleAudio(item)">
+              <template v-if="!item.loading">
+                <image
+                  class="music-img"
+                  :src="item.answer.isplay ? playImgSrc : pauseImgSrc"
+                  mode="widthFix"
+                />
+                <template v-if="item.answer.isplay || item.answer.ispause">
+									<text>{{
+										item.answer.audioTime - currentAudioMsg.audioCurrentTime>0?
+										moment(item.answer.audioTime - currentAudioMsg.audioCurrentTime).format('mm:ss')
+										:'00:00'}}
+									</text>
+								</template>
+                <template v-else>
+                  <text>{{
+                    moment(item.answer.audioTime).format("mm:ss")
+                  }}</text>
+                </template>
+              </template>
+              <template v-else>
+                <image
+                  class="load-img"
+                  src="../static/loading.png"
+                  mode="aspectFill"
+                />
+                <text>{{ moment(item.answer.audioTime).format("mm:ss") }}</text>
+              </template>
+            </view>
+          </view>
+        </view>
+        <text class="item-time">提问时间:{{ item.create_time }}</text>
+      </view>
+    </view>
+    <!-- 弹窗 -->
+    <van-popup
+      :show="pupData.show"
+      round
+      @close="pupData.show = false"
+      closeable
+      :close-on-click-overlay="false"
+    >
+      <view class="global-pup">
+        <view class="content">
+          <rich-text
+            style="flex: none; margin-bottom: 20rpx"
+            :nodes="pupData.content"
+          ></rich-text>
+          <view class="contact" v-if="pupData.type === 'contact'">
+            {{ pupData.saleName || "梁娜" }}:<text
+              @click="handleCallPhone(pupData.mobile)"
+              >{{ pupData.mobile || "123456" }}</text
+            >
+          </view>
+          <view class="apply" v-else-if="pupData.type === 'apply'">
+            <view @click="handleApply">立即申请</view>
+          </view>
+        </view>
+      </view>
+    </van-popup>
+  </view>
+</template>
+<script>
+import mixin from "../mixin/questionMixin";
+import { apiBarTotal ,apiSetRead} from "@/api/question.js";
+export default {
+  mixins: [mixin],
+  data() {
+    return {
+      questionList: [],
+      barList: [],
+      selectKey: "Wait",
+      pauseImgSrc: "../static/question/recordplay.png",
+      playImgSrc: "../static/question/recordpause.png",
+      /* userInfo:{
+				is_inner:1,//0:外部客户;1内部员工
+				status:'试用',
+				is_suspend:0,
+				is_researcher:0
+			},//mock用户信息 */
+    };
+  },
+  onLoad() {
+    /* this.getVisitor();
+    this.getBarList();
+    this.getQuestionData(); */
+  },
+  onShow() {
+    this.getSelectKey();
+    this.getBarList();
+    this.getQuestionData();
+  },
+  onReachBottom() {
+    if (this.finished) return;
+    this.page++;
+    this.getQuestionData();
+  },
+  methods: {
+    toDetail(item) {
+      //reply_status:1-待分配 2-待回答 3-已回答
+      if (this.isUserResearcher&& item.reply_status === 2) {
+        uni.navigateTo({ url: "/pages-question/answerDetail?id=" + item.id });
+      }
+    },
+    //点击bar
+    changeBar({ key }) {
+      if (key === this.selectKey) return;
+      this.selectKey = key;
+      this.questionList = [];
+      this.page = 1;
+      this.resetAudio();
+      this.getQuestionData();
+    },
+    getSelectKey() {
+      if(this.userInfo.is_inner === 1){
+        this.selectKey = 'Wait'
+      }else{
+        this.selectKey = 'Replied'
+      }
+    },
+    async getBarList() {
+      const res = await apiBarTotal();
+      if (res.code !== 200) return;
+      const { replied, wait, total } = res.data;
+      //客户: 已回答 未回答 全部
+      const customBar = [
+        {
+          label: "已回答",
+          key: "Replied",
+          num: replied,
+        },
+        {
+          label: "未回答",
+          key: "Wait",
+          num: wait,
+        },
+        {
+          label: "全部",
+          key: "Total",
+          num: total,
+        },
+      ];
+      //研究员: 待回答 已回答 全部
+      const researBar = [
+        {
+          label: "待回答",
+          key: "Wait",
+          num: wait,
+        },
+        {
+          label: "已回答",
+          key: "Replied",
+          num: replied,
+        },
+        {
+          label: "全部",
+          key: "Total",
+          num: total,
+        },
+      ];
+      this.barList = this.isUserResearcher ? researBar : customBar;
+    },
+    async getQuestionData() {
+      const reply_status = { Wait: 2, Replied: 3, Total: 0 };
+      await this.getQuestionList(reply_status[this.selectKey],1);
+      this.setQuestionsRead()
+    },
+    async setQuestionsRead(){
+      //获取未读的数据,请求未读接口变为已读
+      let unReadArr = []
+      this.questionList.forEach(item=>{
+        let isReadKey = this.isUserResearcher?'replier_is_read':'is_read'
+        if(item[isReadKey]===0){
+          unReadArr.push(item.community_question_id)
+        }
+      })
+      if(unReadArr.length===0) return
+      await apiSetRead({
+        question_ids:unReadArr.join(',')
+      })
+    }
+  },
+};
+</script>
+<style lang="scss" scoped>
+.answer-page {
+  padding: 30rpx;
+  .answer-bar {
+    display: flex;
+    justify-content: space-between;
+    .bar-item {
+      width: 206rpx;
+      color: #666666;
+      background-color: #f5f5f5;
+      height: 70rpx;
+      line-height: 70rpx;
+      text-align: center;
+      &.active {
+        background-color: #fdf8f2;
+        color: #e3b377;
+      }
+    }
+  }
+  .answer-list {
+    margin-top: 20rpx;
+  }
+  .global-pup {
+    .content {
+      padding: 90rpx 34rpx;
+      flex-direction: column;
+      .contact {
+        text {
+          margin-left: 15rpx;
+          color: #e6b77d;
+        }
+      }
+      .apply {
+        margin-top: 40rpx;
+        width: 390rpx;
+        height: 80rpx;
+        background-color: #e6b77d;
+        color: #ffffff;
+        text-align: center;
+        line-height: 80rpx;
+        border-radius: 40rpx;
+      }
+    }
+  }
+}
+</style>

+ 125 - 0
pages-question/hasQuestion.vue

@@ -0,0 +1,125 @@
+<template>
+  <view class="hasquestion-wrap">
+    <view class="title">问题描述<van-icon @click="showHint" coloe="rgba(0, 0, 0, 0.2)" name="question" />
+      <view class="hint" v-if="hintShow">实际发布的问题会以提炼出的精简内容为准</view>
+    </view>
+    <textarea
+      v-model="text"
+      placeholder="点击输入提问"
+      placeholder-class="textarea-placeholder"
+      :maxlength="maxlength"
+      @input="calcWord"
+    />
+    <text style="float:right;color:grey;">剩余可输入字数:<text :style="{color:(maxlength-textlength<=10)?'red':'grey'}">{{maxlength-textlength}}</text>字</text>
+    <view class="btn-wrap">
+      <view class="btn" :class="{'active':textlength>0}" @click="handleClick">完成</view> 
+    </view>
+  </view>
+</template>
+
+<script>
+import {apiPubAsk} from '@/api/question.js'
+export default {
+  data() {
+    return {
+      text:'',
+      textlength:0,
+      maxlength:50,
+      hintShow:false
+    };
+  },
+  methods: {
+    async handleClick(){
+      if(!this.textlength){
+        uni.showToast({
+          title: '请输入问题',
+          icon: 'none',
+        })
+        return
+      }
+      const res = await apiPubAsk({
+        question_content:this.text
+      })
+      if(res.code===200){
+        uni.navigateBack({delta:1})
+      }
+      
+    },
+    //计算字数
+    calcWord(e){
+      console.log('text1',this.text)
+      this.textlength = e.detail.value.length
+      //真机,一次性输入字数大于maxlength时数据和显示错误
+      if(this.textlength>=this.maxlength){
+        this.textlength=this.maxlength
+        this.text = this.text.slice(0,this.maxlength)
+      }
+      console.log('text2',this.text)
+    },
+    //展示提示
+    showHint(){
+     this.hintShow = true
+     setTimeout(()=>{
+       this.hintShow = false
+     },1000)
+    }
+  },
+};
+</script>
+<style lang="scss">
+.hasquestion-wrap{
+  .title{
+    .van-icon{
+      color:rgba(0, 0, 0, 0.2);
+    }
+  }
+}
+</style>
+<style scoped lang="scss">
+.hasquestion-wrap{
+  padding:40rpx 34rpx;
+  .title{
+    margin-bottom: 20rpx;
+    font-size: 32rpx;
+    color: #333333;
+    position:relative;
+    .hint{
+      position: absolute;
+      padding:20rpx 30rpx;
+      border-radius: 4rpx;
+      font-size: 28rpx;
+      background-color: rgba(0, 0, 0, 0.5);
+      color:#FFFFFF;
+      z-index: 2;
+      left:30rpx;
+    }
+  }
+  textarea{
+    width:100%;
+    height:294rpx;
+    box-sizing: border-box;
+    padding:30rpx;
+    border:1rpx solid #BEBEBE;
+    border-radius: 8rpx;
+  }
+  .btn-wrap{
+    margin-top: 120rpx;
+    text-align: center;
+    .btn{
+      display: inline-block;
+      width:390rpx;
+      height:80rpx;
+      border-radius: 40rpx;
+      font-size: 32rpx;
+      text-align: center;
+      line-height: 80rpx;
+      background-color: #ADADAD99;
+      color: #FFFFFF;
+      &.active{
+        background-color: #E6B77D;
+      }
+    }
+  }
+  
+}
+</style>

BIN
pages-question/static/delerecord.png


BIN
pages-question/static/record-img.png


BIN
pages-question/static/record.png


+ 38 - 3
pages.json

@@ -3,8 +3,7 @@
 		{
 			"path": "pages/report/report",
 			"style": {
-				"navigationBarTitleText": "报告",
-				"navigationStyle": "custom",
+				"navigationBarTitleText": "FICC研报",
 				"enablePullDownRefresh": true
 			}
 		},
@@ -35,6 +34,12 @@
 				"navigationBarTitleText": "我的"
 			}
 		},
+		{
+			"path": "pages/question/question",
+			"style": {
+				"navigationBarTitleText": "问答社区"
+			}
+		},
 		{
 			"path":"pages/login",
 			"style":{
@@ -207,6 +212,30 @@
 					"path": "ficcService"
 				}
 			]
+		},
+		// 问答模块
+		{
+			"root": "pages-question",
+			"pages": [
+				{
+					"path": "answerList",
+					"style":{
+						"navigationBarTitleText": "我的问答"
+					}
+				},
+				{
+					"path": "answerDetail",
+					"style":{
+						"navigationBarTitleText": "问答详情"
+					}
+				},
+				{
+					"path": "hasQuestion",
+					"style":{
+						"navigationBarTitleText": "我要提问"
+					}
+				}
+			]
 		}
 	],
 		
@@ -239,11 +268,17 @@
 				"iconPath": "./static/tabbar/activity.png",
 				"selectedIconPath": "./static/tabbar/activity-s.png"
 			},
-			{
+			/* {
 				"pagePath": "pages/user/user",
 				"text": "我的",
 				"iconPath": "./static/tabbar/user.png",
 				"selectedIconPath": "./static/tabbar/user-s.png"
+			}, */
+			{
+				"pagePath": "pages/question/question",
+				"text": "问答",
+				"iconPath": "./static/tabbar/question.png",
+				"selectedIconPath": "./static/tabbar/question-s.png"
 			}
 		]
 	},

+ 364 - 0
pages/question/question.vue

@@ -0,0 +1,364 @@
+<template>
+	<view class="question-wrap">
+		<view class="question-top" :class="{'noAuth':!(isUserResearcher||userInfo.status&&userAuth)}">
+			<view @click="showPopup" v-if="isUserResearcher||userInfo.status&&userAuth"
+				  style="display:flex;align-items: center;margin-left:30rpx;">
+				<image src="../../static/question/select.png" mode="widthFix" class="menu-icon"/>
+				<text style="color:#E3B377;font-size:28rpx;">筛选</text>
+			</view>	
+			<van-popup :show="isPopupShow" position="left"  :close-on-click-overlay="true"
+				@close="isPopupShow = false"
+				custom-style="height: 100%;width:50%;"
+				@touchmove.stop.prevent>
+				<view class="pop-wrap">
+					<view class="pop-option-list">
+						<van-collapse :value="activeName" @change="changeSelecOption" accordion :border="false">
+							<van-collapse-item :border="false" :title="item.ClassifyName" :name="index" v-for="(item, index) in optionList"
+								:key="index">
+								<view class="option-btn-wrap">
+									<view class="option-btn" 
+										v-for=" i in item.Items" :key="i.PermissionId"
+										@click="handleOptionClick(i)" 
+										:class="{'active':selectName===i.PermissionName,'full':i.PermissionName.length>4}" 
+										>
+											{{ i.PermissionName }}
+									</view>
+								</view>
+							</van-collapse-item>
+						</van-collapse>
+					</view>
+				</view>	
+			</van-popup>		
+		</view>
+		<view class="report-empty-box" v-if="questionList.length==0">
+      		<image :src="globalImgUrls.activityNoAuth" mode="widthFix"  style="width:100%;"/>
+      		<view>暂无提问<text v-if="userInfo.is_inner!==1">,快试试提问功能吧</text></view>
+    	</view>
+		<view class="question-list" :class="{'last':finished}">
+			<view class="question-item" v-for="item in questionList" :key="item.community_question_id">
+				<view class="question-info">
+					<view style="flex:1;" class="question-title">
+						<text class="item-label">{{item.chart_permission_name}}</text>
+						<!-- <text class="item-title"> -->{{ item.question_content }}<!-- </text> -->
+					</view>	
+					<view class="item-answer">
+						<view class="answer" @click="handleAudio(item)">
+							<template v-if="!item.loading">
+								<image class="music-img" :src="item.answer.isplay?playImgSrc:pauseImgSrc" mode="widthFix"/>
+								<template v-if="item.answer.isplay || item.answer.ispause">
+									<text>{{
+										item.answer.audioTime - currentAudioMsg.audioCurrentTime>0?
+										moment(item.answer.audioTime - currentAudioMsg.audioCurrentTime).format('mm:ss')
+										:'00:00'}}
+									</text>
+								</template>
+								<template v-else>
+									<text>{{ moment(item.answer.audioTime).format('mm:ss') }}</text>
+								</template>
+							</template>
+							<template v-else>
+								<image class="load-img" src="../../static/loading.png" mode="aspectFill" />
+								<text>{{ moment(item.answer.audioTime).format('mm:ss') }}</text>
+							</template>
+						</view>
+					</view>
+				</view>
+				<text class="item-time">提问时间:{{ item.create_time }}</text>
+			</view>
+		</view>
+		<view class="topage-btn" @click="toPage" v-if="isUserResearcher||userInfo.status&&userAuth">
+			<image 
+				v-if=" userInfo.is_inner!==1"
+				src="../../static/question/askquestion.png"
+				mode="scaleToFill"
+				style="width:34rpx;height:34rpx;"
+			/>
+			{{ isUserResearcher ? '待回答' : '我要提问' }} <text v-if="isUserResearcher" style="margin-left:5rpx;">{{'('+waitNum+')'}}</text>
+		</view>
+		<!-- 弹窗 -->
+		<van-popup :show="pupData.show" round @close="pupData.show = false" closeable :close-on-click-overlay="false">
+			<view class="global-pup">
+				<view class="content">
+					<rich-text style="flex:none;margin-bottom:20rpx;" :nodes="pupData.content"></rich-text>
+					<view class="contact" v-if="pupData.type == 'contact'">
+						{{pupData.saleName||''}}:<text @click="handleCallPhone(pupData.mobile)">{{pupData.mobile||''}}</text>
+					</view>
+					<view class="apply" v-else-if="pupData.type == 'apply'">
+						<view @click="handleApply">立即申请</view>
+					</view>
+				</view>
+			</view>
+		</van-popup>
+	</view>
+</template>
+
+<script>
+import mixin from "../../mixin/questionMixin";
+import {apiOptionList,apiBarTotal} from '@/api/question'
+export default {
+	mixins: [mixin],
+	data() {
+		return {
+			questionList: [],
+			isPopupShow: false,//弹出层是否展示
+			optionList: [],
+			activeNames: [],//collapse
+			activeName:'',
+			selectName:'',
+			pauseImgSrc:'../../static/question/recordplay.png',
+			playImgSrc:'../../static/question/recordpause.png',
+			noAuth:['潜在','流失','冻结'],
+			waitNum:0,
+			/* userInfo:{
+				is_inner:1,//0:外部客户;1内部员工
+				status:'试用',
+				is_suspend:0,
+				is_researcher:0
+			},//mock用户信息 */
+		}
+	},
+	watch:{
+		selectName(){
+			this.getQuestionList(3)
+		}
+	},
+	computed:{
+		userAuth(){
+			//暂停试用
+			if(this.userInfo.status==='试用'&&this.userInfo.is_suspend===1){
+				return false
+			}
+			//潜在流失冻结
+			if(this.noAuth.includes(this.userInfo.status)){
+				return false
+			}
+			//有权限的
+			return true
+		}
+	},
+	onLoad() {
+		/* this.getVistor()
+		this.getOptionList()
+		this.getQuestionList(3) */
+	},
+	onShow() {
+		this.getWaitNum()
+		this.getOptionList()
+		this.getQuestionList(3)
+	},
+	onReachBottom() {
+		if(this.finished) return
+		this.page++
+		this.getQuestionList(3)
+ 	},
+	methods: {
+		//获取研究员问答列表数量统计
+		getWaitNum() {
+			if(this.userInfo.is_inner!==1) return
+			//如果是研究员,则请求问答列表数量统计
+			apiBarTotal().then(res=>{
+				if(res.code===200){
+					this.waitNum = res.data.wait
+				}
+			})
+		},
+		//获取筛选列表
+		async getOptionList(){
+			const res = await apiOptionList()
+			if(res.code===200){
+				this.optionList = res.data
+			}
+		},	
+		//点击筛选
+		showPopup() {
+			this.isPopupShow = true
+		},
+		//点击一级分类
+		changeSelecOption(e) {
+			this.activeName = e.detail
+		},
+		//点击二级分类
+		handleOptionClick(item){
+			//重复点击代表取消
+			if(this.selectId===item.PermissionId){
+				this.selectId=-1
+				this.selectName=''
+			}else{
+				this.selectId = item.PermissionId
+				this.selectName = item.PermissionName	
+			}
+			this.page = 1
+			this.isPopupShow = false
+		},
+		//点击'我要提问' or '待回答'
+		toPage() {
+			//const {is_inner} = this.userInfo
+			if (this.isUserResearcher) {
+				uni.navigateTo({ url: '/pages-question/answerList' })
+			} else {
+				uni.navigateTo({ url: '/pages-question/hasQuestion' })
+			}
+		}
+	}
+}
+</script>
+
+<style lang="scss">
+.question-wrap {
+	.van-popup--bottom{
+		padding-bottom: 0 !important;
+	}
+	/deep/ .van-cell__title,
+	.van-cell__value {
+		flex: none !important;
+	}
+	.van-collapse-item{
+		&.van-hairline--top{
+			&::after{
+				border: none !important;
+			}
+		}
+		.van-cell{
+			padding-left:0 !important;
+		}
+	}
+	.van-collapse-item__content{
+		padding:0;
+	}
+}
+
+page {
+	padding-bottom: env(safe-area-inset-bottom);
+}
+</style>
+<style lang="scss" scoped>
+.question-wrap {
+	padding: 0 30rpx 80rpx 30rpx;
+	background-color: #FFFFFF;
+	.question-top {
+		display: flex;
+		flex: auto;
+		align-items: center;
+		height: 100rpx;
+		background-color: white;
+		position: sticky;
+		top: 0;
+		left: 0;
+		margin:0 -30rpx;
+		z-index: 99;
+		&.noAuth{
+			height:20rpx;
+		}
+		/* border-top: 1rpx solid rgba(0, 0, 0, 0.1); */
+		.text{
+			width:240rpx;
+			height:70rpx;
+			line-height: 70rpx;
+			background-color: #F5F5F5;
+			border-radius: 4rpx;
+			color:#666666;
+			text-align: center;
+			&.active{
+				color:#E3B377;
+				background-color: #FDF8F2;
+			}
+		}
+		.auth-box {
+			flex: 1;
+		}
+
+		.menu-icon {
+			width: 34rpx;
+			height:34rpx;
+		}
+		.pop-wrap{
+			height: 100%;
+			padding: 50rpx 34rpx;
+			.pop-option-list {
+				.option-btn-wrap {
+					display: flex;
+					justify-content: space-between;
+					flex-wrap: wrap;
+					margin-bottom: 20rpx;
+					.option-btn {
+						min-width: 145rpx;
+						height:76rpx;
+						line-height: 76rpx;
+						text-align: center;
+						color: black;
+						background-color: #F6F6F6;
+						border-radius: 4rpx;
+						margin-top: 10rpx;
+						&.active{
+							background-color: #FAEEDE;
+						}
+						&.full{
+							width:100%;
+						}
+					}
+				}
+			}
+			.pop-btn{
+				height:80rpx;
+				margin:0 -34rpx;
+				text-align: center;
+				line-height: 80rpx;
+				color:#FFFFFF;
+				background-color: #E6B77D;
+			}
+		}
+		
+	}
+
+	.question-list {
+		padding-bottom: 34rpx;
+		&.last{
+			padding-bottom: 260rpx;
+		}
+	}
+
+	.topage-btn {
+		position: fixed;
+		left:50%;
+		margin-left: -257rpx;
+		bottom: 215rpx;
+		width:514rpx;
+		height: 80rpx;
+		text-align: center;
+		line-height: 80rpx;
+		border-radius: 40rpx;
+		background-color: #333333;
+		box-shadow: 0px 4px 20px 1px rgba(160, 126, 84, 0.25);
+		color: #E3B377;
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		image{
+			margin-right: 10rpx;
+			margin-top: -2rpx;
+		}
+	}
+	.global-pup{
+		.content{
+			padding:90rpx 34rpx;
+			flex-direction: column;
+			.contact{
+				text{
+					margin-left: 15rpx;
+					color:#E6B77D;
+				}
+			}
+			.apply{
+				margin-top: 40rpx;
+				width:390rpx;
+				height:80rpx;
+				background-color: #E6B77D;
+				color: #FFFFFF;
+				text-align: center;
+				line-height: 80rpx;
+				border-radius: 40rpx;
+			}
+		}
+	}
+}
+</style>

+ 58 - 5
pages/report/report.vue

@@ -2,14 +2,27 @@
   <view class="report-page">
     <view class="top-sticky" style="background: #fff">
     <!-- 导航 -->
-    <view class="nav-bar-wrap" :style="{height:navBarStyle.height,paddingTop:navBarStyle.paddingTop,paddingBottom:navBarStyle.paddingBottom}">
+    <!-- <view class="nav-bar-wrap" :style="{height:navBarStyle.height,paddingTop:navBarStyle.paddingTop,paddingBottom:navBarStyle.paddingBottom}">
       <view class="content">
         <van-icon custom-class="search-icon" name="search" size="24px" @click="goSearch" />
         <view class="text">FICC研报</view>
       </view>
-    </view>
+    </view> -->
     <!-- 分类 -->
     <view class="type-wrap">
+      <view style="display:flex;align-items: center;margin-bottom: 20rpx;">
+        <view class="avatar" @click="goUser">
+          <image style="width:100%;height:100%" :src="userInfo.head_img_url" mode="aspectFill"/>
+        </view>
+        <view style="flex:1" @click="goSearch">
+          <van-search
+                shape="round"
+                disabled
+                placeholder="请输入报告标题或关键字"
+            />
+        </view>
+        
+      </view>
       <view class="flex first-type-box">
         <view class="item" v-for="(item,index) in topFirstList" :key="item.classify_name" @click="handleClickTopFirst(item,index)">
           <image :src="selectTopFirstId==item.classify_name?item.select_icon_url:item.icon_url" mode="aspectFill"/>
@@ -67,7 +80,7 @@
             <view @click="handleCallPhone(authData.contactInfo.mobile)">拨号</view>
           </view>    
       </view>
-    </van-popup>
+    </van-popup>  
   </view>
 </template>
 
@@ -88,7 +101,6 @@ export default {
         paddingTop:40+'px',
         paddingBottom:'4px'
       },
-
       authData:{
         show:false,
         isBuy:false,//是否为已购客户
@@ -122,6 +134,8 @@ export default {
 			},
 		})
   },
+  onReady() {
+  },
   onPullDownRefresh() {
     this.getTopAuthList()
     setTimeout(() => {
@@ -147,7 +161,6 @@ export default {
         paddingBottom:'4px'
       }
     },
-
     //顶部权限数据
     async getTopAuthList(){
       const res=await apiReportIndexPageAuthList()
@@ -240,6 +253,18 @@ export default {
     goSearch(){
       uni.navigateTo({url:'/pages-report/search'})
     },
+    
+    //跳转我的
+    goUser(){
+       uni.navigateTo({
+        url: '/pages/user/user',
+        fail () {
+          uni.switchTab({
+            url:'/pages/user/user'
+          })
+        }
+      })
+    },
 
     //跳转报告详情
     goDetail(item){
@@ -293,6 +318,20 @@ export default {
 </style>
 
 <style lang="scss" scoped>
+movable-area{
+  position: fixed;
+  top: 50%;
+  left: 0;
+  width: 100%;
+  height: calc(50% - 100rpx);
+  pointer-events: none;
+  z-index: 3;
+  movable-view{
+    width:fit-content;
+    height:fit-content;
+    pointer-events: auto;
+  }
+}
 .top-sticky{
   position: sticky;
   top: 0;
@@ -319,6 +358,13 @@ export default {
   // border-bottom: 1px solid $global-border-color;
   padding: 20rpx 34rpx 0 34rpx;
   box-shadow: 0px 4rpx 4rpx 1px rgba(198, 198, 198, 0.25);
+  .avatar{
+    width:78rpx;
+    height:78rpx;
+    border-radius: 50%;
+    overflow: hidden;
+    margin-right: 15rpx;
+  }
   .first-type-box{
     justify-content: space-between;
     .item{
@@ -440,5 +486,12 @@ export default {
     }
   }
 }
+.quesion-btn{
+  width:100rpx;
+  height:100rpx;
+  background-color: red;
+  border-radius: 50%;
+  z-index: 50;
+}
 
 </style>

+ 50 - 4
pages/user/user.vue

@@ -61,11 +61,22 @@
 					<van-icon name="arrow"></van-icon>
 				</view>
 			</view>
+			
+			<view class="flex item-card">
+				<image src="../../static/question/question-icon.png" mode="widthFix" />
+				<text class="label">我的问答</text>
+				<view class="right-text look" @click="handleToQuestionPage">
+					<!-- <text>查看</text> -->
+					<text class="hint" v-if="questionUnread!==0">{{questionUnread}}</text>
+					<van-icon name="arrow"></van-icon>
+				</view>
+			</view>
 			<view class="flex item-card" v-if="userInfo.status!='试用'">
 				<image src="../../static/calendar.png" mode="widthFix" />
 				<text class="label">服务截止日期</text>
 				<text class="right-text" v-if="!(userInfo.status=='冻结'||(userInfo.status=='试用'&&userInfo.is_suspend==1))">{{lastTime}}</text>
 			</view>
+			
 		</view>
 
 
@@ -87,6 +98,7 @@
 <script>
 	const moment=require('@/utils/moment-with-locales.min')
 	import {apiLastApplyRecord,apiApplyPermission} from '@/api/user'
+	import {apiGetUnread} from '@/api/question'
 	export default {
 		computed: {
 			lastTime(){
@@ -112,11 +124,13 @@
 				pupData:{
 					show:false,
 					content:'',//弹窗html字符串
-				}
+				},
+				questionUnread:0
 			}
 		},
 		onShow() {
 			this.$store.dispatch('getUserInfo')
+			this.getQuestionUnread()
 		},
 		methods: {
 			async handleGoApplyPermission(){
@@ -167,6 +181,9 @@
 				uni.navigateTo({ url: '/pages-user/mysetting' })
 			},
 
+			handleToQuestionPage(){
+				uni.navigateTo({url:'/pages-question/answerList'})
+			},
 			handleContact(){
 				apiApplyPermission({
                     company_name:this.userInfo.company_name,
@@ -181,15 +198,30 @@
 				uni.makePhoneCall({
 					phoneNumber: this.userInfo.seal_mobile
 				});
+			},
+			getQuestionUnread(){
+				apiGetUnread().then(
+					res=>{
+						if(res.code===200){
+							this.questionUnread = res.data
+						}
+					}
+				)
+
 			}
 		}
 	}
 </script>
-
+<style>
+page{
+	padding-bottom: 0;
+}
+</style>
 <style lang="scss">
 	.user-page{
-		min-height: calc(100vh - calc(50px + constant(safe-area-inset-bottom)));
-    	min-height: calc(100vh - calc(50px + env(safe-area-inset-bottom)));
+		min-height: 100vh;
+		/* min-height: calc(100vh - calc(50px + constant(safe-area-inset-bottom)));
+    	min-height: calc(100vh - calc(50px + env(safe-area-inset-bottom))); */
 		background-color: #EDEDED;
 	}
 	.top-box{
@@ -270,6 +302,20 @@
 				height: 100rpx;
 				text-align: right;
 				line-height: 100rpx;
+				display: flex;
+    			align-items: center;
+    			justify-content: flex-end;
+				.hint{
+					width:30rpx;
+					height:30rpx;
+					background-color: #FF0000FF;
+					color:#fff;
+					font-size: 20rpx;
+					text-align: center;
+					line-height: 30rpx;
+					border-radius: 50%;
+					display: inline-block;
+				}
 			}
 			.unread-ico {
 				width: 30rpx;

BIN
static/question/askquestion.png


BIN
static/question/question-icon.png


BIN
static/question/recordpause.png


BIN
static/question/recordplay.png


BIN
static/question/select.png


BIN
static/tabbar/question-s.png


BIN
static/tabbar/question.png


+ 76 - 0
style/common.scss

@@ -97,4 +97,80 @@ view{
   overflow: hidden;
   text-overflow: ellipsis;
   white-space: nowrap;
+}
+/* 问答模块单个问答样式*/
+.question-item {
+    margin-bottom: 20rpx;
+    &::after{
+        content: '';
+        display: block;
+        height:10rpx;
+        margin:0 -30rpx;
+        background-color: #F9F9F9;
+    }
+    &:last-child{
+        &::after{
+            background-color: #FFFFFF;
+        }
+    }
+    .question-info{
+        display: flex;
+        padding: 10rpx 0;
+        .question-title{
+            font-size: 32rpx;
+            color:#333333;
+            .item-label{
+                padding:7rpx 12rpx;
+                font-size: 22rpx;
+                text-align: center;
+                background-color: #333333;
+                color: #E4B478;	
+                border-radius: 21rpx;
+                margin-right:15rpx ;		
+            }
+        }
+        .item-answer {
+            display: flex;
+            align-items: center;
+            .answer {
+                width:150rpx;
+                height: 97rpx;
+                box-sizing: border-box;
+                padding:30rpx 15rpx;
+                border-radius: 18rpx;
+                background:linear-gradient(253deg, #E3B377 0%, #FBCA8E 100%);
+                display: flex;
+                justify-content: space-around;
+                align-items: center;
+                color: #FFFFFF;
+                font-size: 24rpx;
+                .load-img{
+                    width: 34rpx;
+                    height: 34rpx;
+                    margin-right: 10rpx;
+                    animation: circle 1s linear infinite;
+                }
+                .music-img{
+                    width: 25rpx;
+                    height: 34rpx;
+                    margin-right: 10rpx;
+                }
+                @keyframes circle {
+                    0%{
+                        transform: rotateZ(0);
+                    }
+                    100%{
+                        transform: rotateZ(360deg);
+                    }
+                }
+            }
+        }
+    }
+    .item-time {
+        color: #999999;
+        font-size: 24rpx;
+        margin:20rpx 0;
+        display: block;
+    }
+    
 }

+ 8 - 1
utils/config.js

@@ -63,12 +63,19 @@ const defaultTabBarListConfig=[
         iconPath: "../static/tabbar/activity.png",
         selectedIconPath: "../static/tabbar/activity-s.png",
     },
-    {
+   /*  {
         key: "user",
         pagePath: "pages/user/user",
         text: "我的",
         iconPath: "../static/tabbar/user.png",
         selectedIconPath: "../static/tabbar/user-s.png",
+    }, */
+    {
+        key: "question",
+        pagePath: "pages/question/question",
+        text: "问答",
+        iconPath: "../static/tabbar/question.png",
+        selectedIconPath: "../static/tabbar/question-s.png"
     }
 ]
 

+ 1 - 1
utils/request.js

@@ -95,7 +95,7 @@ const http=(url,params,method)=>{
 			method:method,
 			header:{
 				Authorization:store.state.user.token,
-				version:'yb3.0'
+				version:'yb5.0'
 			},
 			success(e) {
 				// 接口404

+ 17 - 1
utils/upload.js

@@ -26,7 +26,23 @@ export const uploadToServer = async (tempFilePath) => {
   }
 };
 
-
+/**
+ * 
+ * 上传回复音频到服务器
+ */
+export const uploadAudioToServer = async(tempFilePath)=>{
+  const temres = await uniAsync.uploadFile({
+    url: baseApiUrl + "/community/question/reply/upload_audio",
+    filePath: tempFilePath,
+    name: "file",
+    header: {
+      Authorization: store.state.user.token,
+    },
+  });
+  const res = JSON.parse(temres.data);
+    return res;
+  
+}
 
 /**
  * 上传图片