Jelajahi Sumber

个贷修改

wucl 6 bulan lalu
induk
melakukan
197274653d

+ 84 - 6
src/components/TaskReminder/index.vue

@@ -1,17 +1,17 @@
 <template>
-<div class="card" v-if="show">
-    <div style="display: flex; width: 330px; height: 30px;">
+<div class="card shadow-drop-center slide-in-left" v-if="show">
+    <div style="display: flex; width: 320px; height: 30px;">
         <div class="red_point">
         </div>
-        <div class="card__title">待办提醒
+        <div class="card__title">待办提醒(1/{{ count }})
         </div>
         <div class="card__close">
-          <el-button type="text" @click="closeReminder()">关闭</el-button>
+          <el-button type="text" @click="closeReminder">标记已读</el-button>
         </div>
     </div>
-    <p class="card__content">内业一提交大中型2025010003的新的流程待办,请及时处理。</p>
+    <p class="card__content">{{user}}提交{{businessType}}{{no}}的新的流程待办,请及时处理。</p>
     <div class="card__date">
-        2025-01-11 12:23:23
+        {{ created }}
     </div>
     <div class="card__arrow">
         <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" height="15" width="15">
@@ -34,11 +34,37 @@ export default {
     },
     props: {
 
+      count:{
+        type: Number,
+        Required:true
+      },
+      user:{
+        type: String,
+        Required:true
+      },
+      businessType:{
+        type: String,
+        Required:true
+      },
+      no:{
+        type: String,
+        Required:true
+      },
+      created:{
+        type: String,
+        Required:true
+      },
+      id:{
+        type : Number,
+        Required:true
+      }
+
     },
 
 
     methods: {
           closeReminder(){
+            this.$emit('doRead',this.id);
             this.show = false;
           }
     },
@@ -134,5 +160,57 @@ export default {
     margin-top: 10px;
     margin-right: 5px;
 }
+.shadow-drop-center {
+	-webkit-animation: shadow-drop-center 0.4s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
+	        animation: shadow-drop-center 0.4s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
+}
+
+.slide-in-left {
+	-webkit-animation: slide-in-left 0.5s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
+	        animation: slide-in-left 0.5s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
+}
+
+/* ----------------------------------------------
+ * Generated by Animista on 2025-1-7 10:11:57
+ * Licensed under FreeBSD License.
+ * See http://animista.net/license for more info. 
+ * w: http://animista.net, t: @cssanimista
+ * ---------------------------------------------- */
+
+/**
+ * ----------------------------------------
+ * animation slide-in-left
+ * ----------------------------------------
+ */
+ @-webkit-keyframes slide-in-left {
+  0% {
+    -webkit-transform: translateX(-1000px);
+            transform: translateX(-1000px);
+            -webkit-box-shadow: 0 0 0 0 rgba(0, 0, 0, 0);
+            box-shadow: 0 0 0 0 rgba(0, 0, 0, 0);
+  }
+  100% {
+    -webkit-transform: translateX(0);
+            transform: translateX(0);
+            -webkit-box-shadow: 0 0 20px 0px rgba(0, 0, 0, 0.35);
+            box-shadow: 0 0 20px 0px rgba(0, 0, 0, 0.35);
+  }
+}
+@keyframes slide-in-left {
+  0% {
+    -webkit-transform: translateX(-1000px);
+            transform: translateX(-1000px);
+            -webkit-box-shadow: 0 0 0 0 rgba(0, 0, 0, 0);
+            box-shadow: 0 0 0 0 rgba(0, 0, 0, 0);
+  }
+  100% {
+    -webkit-transform: translateX(0);
+            transform: translateX(0);
+            -webkit-box-shadow: 0 0 20px 0px rgba(0, 0, 0, 0.35);
+            box-shadow: 0 0 20px 0px rgba(0, 0, 0, 0.35);
+  }
+}
+
+
 
 </style>

+ 45 - 4
src/layout/components/Navbar.vue

@@ -39,8 +39,6 @@
           </el-dropdown-item>
         </el-dropdown-menu>
       </el-dropdown>
-
-
       <el-dropdown class="avatar-container right-menu-item hover-effect" trigger="hover" slot="reference">
         <div class="avatar-wrapper-nb">
           <img :src="avatar" class="user-avatar">
@@ -59,7 +57,10 @@
       <img src="../../assets/icon/logout.png" alt=""
         style="width: 16px;position: absolute;top: 22px;right: 20px;cursor: pointer;" @click="logout">
     </div>
-    <!-- <taskReminder></taskReminder> -->
+    <taskReminder v-for="(r,index) in reminders" 
+    :count="reminders.length" :user="r.user" 
+    :businessType="r.businessType" :id="r.id"
+    :no="r.no" :created="r.created" @doRead="doRead"></taskReminder>
   </div>
 </template>
 
@@ -69,6 +70,7 @@ import avatar from '../../assets/icon/avatar.png'
 import eventBus from '../../utils/eventBus.js'
 import taskReminder from '../../components/TaskReminder/index.vue'
 // import settlePswDig from "./SettlePswDig.vue";
+import WebSocketClient from '../../utils/websocketClient.js'
 
 export default {
 
@@ -95,7 +97,14 @@ export default {
       showLevel1Status: false,
       sysCfg: '',
       avatar,
-      visible: false
+      visible: false,
+      reminders:[],
+      client: {
+                messages: [
+                    { data: "*" },
+                    { userSize: 0 }
+                ]
+            },
     }
   },
   computed: {
@@ -104,6 +113,10 @@ export default {
     ]),
   },
 
+  mounted() {
+        //this.init();
+    },
+
   methods: {
     async logout() {
       await this.$store.dispatch('user/logout');
@@ -177,6 +190,34 @@ export default {
         }
       })
     },
+    getReminders(){
+     
+    },
+    doRead(value){
+      this.reminders = this.reminders.filter(item =>item.id != value);
+      console.log(this.reminders.length)
+    },
+
+    init() {
+        if (typeof (WebSocket) === "undefined") {
+            console.log("您的浏览器不支持socket")
+        } else {
+            // 创建 WebSocketClient 实例并连接到服务端 API 接口
+            // this.client = new WebSocketClient('wss://kps.scdayou.com/ws/apo/ws')
+            this.client = new WebSocketClient('ws://127.0.0.1:8088/api/ws')
+            this.client.connect()
+        }
+    },
+    // destoryed() {
+    //     // 在组件销毁前断开 WebSocket 连接
+    //     this.client.disconnect()
+    //     console.log("websocket 断开")
+    // },
+    // beforeDestory() {
+    //     this.client.disconnect()
+    //     console.log("websocket 断开")
+    // }
+
   }
 }
 </script>

+ 1 - 1
src/utils/utils.js

@@ -204,7 +204,7 @@ const utils = {
   // 四舍五入到指定的小数位数
   roundToDecimalPlace(number, decimalPlaces) {
       const factor = Math.pow(10, decimalPlaces);
-      return Math.round(number * factor) / factor;
+      return Math.round(+number+'e'+ decimalPlaces) / factor;
     }
 
 };

+ 62 - 0
src/utils/websocketClient.js

@@ -0,0 +1,62 @@
+import axios from 'axios'
+ 
+export default class WebSocketClient {
+  constructor(url, apiEndpoint) {
+    this.url = url
+    this.apiEndpoint = apiEndpoint
+    this.socket = null
+    this.connected = false
+    this.messages = []
+    this.error = null
+  }
+ 
+  connect() {
+    if (this.socket && this.socket.readyState === WebSocket.OPEN) {
+      return
+    }
+    this.socket = new WebSocket(this.url)
+ 
+    this.socket.addEventListener('open', () => {
+      console.log('WebSocket connected')
+      this.connected = true
+      // 连接成功后发送 API 请求
+      //this.sendApiRequest()
+    })
+    this.socket.addEventListener('message', (event) => {
+      const message = JSON.parse(event.data)
+      //const message = event.data;
+      this.messages.push(message)
+    })
+    this.socket.addEventListener('error', (event) => {
+      console.error('WebSocket error:', event)
+      this.error = event
+    })
+    this.socket.addEventListener('close', () => {
+      console.log('WebSocket closed')
+      this.connected = false
+    })
+  }
+ 
+  disconnect() {
+    if (this.socket) {
+      this.socket.close()
+    }
+  }
+ 
+  sendMessage(message) {
+    if (this.socket.readyState !== WebSocket.OPEN) {
+      return
+    }
+    this.socket.send(JSON.stringify(message))
+  }
+ 
+  sendApiRequest() {
+    axios.get(this.apiEndpoint).then(response => {
+      // 处理 API 返回数据并发送给服务端
+      const data = response.data
+      this.sendMessage(data)
+    }).catch(error => {
+      console.error('API request error:', error)
+    })
+  }
+}

+ 10 - 2
src/views/personal/readonlyDetail.vue

@@ -958,8 +958,16 @@ export default {
     },
     target: {
       handler(newVal, oldVal) {
-        if (newVal.acreage != null && newVal.price != null) {
-          newVal.amount = (newVal.acreage * newVal.price/10000).toFixed(2)
+        if (newVal.acreage != null && newVal.price != null && newVal.price !=null && newVal.price != 0) {
+          let acrege = Decimal(newVal.acreage);
+          let price = Decimal(newVal.price);
+          let wan = Decimal(10000);
+          let amount = acrege.mul(price).div(wan);
+          // console.log(acrege.toString())
+          // console.log(price.toString())
+          // console.log(amount.toString())
+          newVal.amount = utils.roundToDecimalPlace(amount,2)
+          // console.log(newVal.amount)
         }
         if (newVal.id) {
           this.getProductions();

+ 140 - 94
src/views/personal/todoDetail.vue

@@ -442,7 +442,7 @@
           </el-row>
         </el-form>
       </el-tab-pane>
-      <el-tab-pane name="comparable" class="pane-class" :lazy=true :disabled="!target.isOnline">
+      <el-tab-pane name="comparable" class="pane-class" :lazy=true >
         <span slot="label"><i class="el-icon-office-building"></i> 可比实例</span>
         <div class="createMajor-main-container">
           <div class="postInfo-container">
@@ -739,7 +739,7 @@
           </el-table>
         </el-form>
       </el-tab-pane>
-      <el-tab-pane name="certificateInfo" class="pane-class" :lazy=true :disabled="!target.isOnline">
+      <el-tab-pane name="certificateInfo" class="pane-class" :lazy=true >
         <span slot="label"><i class="el-icon-postcard"></i> 证件信息</span>
         <el-tabs v-model="certificateInfo">
           <el-tab-pane name="CHANGE" class="pane-class" :lazy=true :disabled="!certificateCouldEdit">
@@ -777,7 +777,7 @@
           </el-tab-pane>
         </el-tabs>
       </el-tab-pane>
-      <el-tab-pane name="otherInfo" class="pane-class" :lazy=true :disabled="!target.isOnline">
+      <el-tab-pane name="otherInfo" class="pane-class" :lazy=true >
         <span slot="label"><i class="el-icon-chat-line-square"></i> 附加信息</span>
         <el-tabs v-model="otherInfo">
           <el-tab-pane name="entityInfo" class="pane-class" :lazy=true>
@@ -802,7 +802,8 @@
       <el-tab-pane name="producution" class="pane-class" :lazy=true>
         <span slot="label"><i class="el-icon-document-checked"></i> 产品信息</span>
         <div class="createMajor-main-container"
-          v-if="currentNode.nodeCode === 'QUOTATION_FEEDBACK' || currentNode.nodeCode === 'STATEMENT_FEEDBACK'">
+          v-if="currentNode.nodeCode === 'QUOTATION_FEEDBACK' || currentNode.nodeCode === 'STATEMENT_FEEDBACK' ||
+          currentNode.nodeCode === 'REPORT_FEEDBACK' || currentNode.nodeCode === 'LETTER_FEEDBACK'">
           <div class="postInfo-container">
             <div>
               <el-divider content-position="left">
@@ -814,16 +815,17 @@
             </div>
             <el-form ref="feedbackFrom" class="form-container" style="margin-top:30px" :rules="rules">
               <el-row>
-                <el-col :xs="24" :sm="12" :lg="8" :span="6">
-                  <el-form-item label="出具产品类型:" prop="production" label-width="160px" class="postInfo-container-item">
+                <el-col :xs="24" :sm="12" :lg="12" :span="6">
+                  <el-form-item label="选择反馈信息:" prop="production" label-width="160px" class="postInfo-container-item">
                     <el-checkbox-group v-model="feedback" @change="saveFeedback()">
-                      <el-checkbox-button label="STATEMENT" name="production">价值意见书</el-checkbox-button>
-                      <el-checkbox-button label="REPORT" name="production">报告</el-checkbox-button>
-                      <el-checkbox-button label="LETTER" name="production">复评函</el-checkbox-button>
-                      <el-tooltip class="item" effect="dark" content="只出具价值意见书,不再出具其他产品。" placement="top-start">
-                        <el-checkbox-button v-if="currentNode.nodeCode === 'STATEMENT_FEEDBACK'" label="NONE"
-                          name="production">不再出具</el-checkbox-button>
-                      </el-tooltip>
+                      <el-checkbox-button label="STATEMENT" name="production"  :disabled="!showStatement">出价值意见书</el-checkbox-button>
+                      <el-checkbox-button label="CHANGE_STATEMENT" name="production" :disabled="!showChangeStatement">修改意见书</el-checkbox-button>
+                      <el-checkbox-button label="REPORT" name="production"  :disabled="!showReport">出报告</el-checkbox-button>
+                      <el-checkbox-button label="CHANGE_REPORT" name="production" :disabled="!showChangeReport">修改报告</el-checkbox-button>
+                      <el-checkbox-button label="LETTER" name="production"  :disabled="!showLetter">出复评函</el-checkbox-button>
+                      <el-checkbox-button label="CHANGE_LETTER" name="production"  :disabled="!showChangeLetter">修改复评函</el-checkbox-button>
+                      <el-checkbox-button  label="NONE"
+                        name="production">结束</el-checkbox-button>
                     </el-checkbox-group>
                   </el-form-item>
                 </el-col>
@@ -1191,7 +1193,11 @@ export default {
           let price = Decimal(newVal.price);
           let wan = Decimal(10000);
           let amount = acrege.mul(price).div(wan);
+          // console.log(acrege.toString())
+          // console.log(price.toString())
+          // console.log(amount.toString())
           newVal.amount = utils.roundToDecimalPlace(amount,2)
+          // console.log(newVal.amount)
         }
       
         if (newVal.id) {
@@ -1294,6 +1300,37 @@ export default {
       }
 
     },
+    showStatement(){
+      if (this.$route.query.currentNodeCode=== 'QUOTATION_FEEDBACK'){
+          return true;
+      }
+      
+    },
+    showChangeStatement(){
+      if (this.$route.query.currentNodeCode=== 'STATEMENT_FEEDBACK'){
+        return true;
+      }
+    },
+    showReport(){
+      if (this.$route.query.currentNodeCode=== 'QUOTATION_FEEDBACK' || this.$route.query.currentNodeCode=== 'STATEMENT_FEEDBACK'){
+        return true;
+      }
+    },
+    showChangeReport(){
+      if (this.$route.query.currentNodeCode=== 'REPORT_FEEDBACK' ){
+        return true;
+      }   
+    },
+    showLetter(){
+      if (this.$route.query.currentNodeCode=== 'QUOTATION_FEEDBACK' ||this.$route.query.currentNodeCode=== 'REPORT_FEEDBACK' || this.$route.query.currentNodeCode=== 'STATEMENT_FEEDBACK'){
+        return true;
+      }
+    },
+    showChangeLetter(){
+      if (this.$route.query.currentNodeCode=== 'LETTER_FEEDBACK' ){
+        return true;
+      }   
+    }
   },
 
   data() {
@@ -1760,91 +1797,91 @@ export default {
           case "GENERATE_STATEMENT":
           case "WRITE_REPORT":
           case "WRITE_LETTER":
-            if (this.isOnline) {
-              let ok = true;
-              this.personal.credentials.forEach(item => {
-                if (item === 'HOUSE_CERTIFICATE' && !this.target.houseCertificate) {
-                  this.$notify({
-                    title: '提示',
-                    message: '请先完成房产证信息录入再提交,否则请切换为线下流程。',
-                    type: 'error',
-                    duration: 2000
-                  });
-                  ok = false;
+            // if (this.isOnline) {
+            //   let ok = true;
+            //   this.personal.credentials.forEach(item => {
+            //     if (item === 'HOUSE_CERTIFICATE' && !this.target.houseCertificate) {
+            //       this.$notify({
+            //         title: '提示',
+            //         message: '请先完成房产证信息录入再提交,否则请切换为线下流程。',
+            //         type: 'error',
+            //         duration: 2000
+            //       });
+            //       ok = false;
                 
-                }
-                if (item === 'LAND_CERTIFICATE' && !this.target.landCertificate) {
-                  this.$notify({
-                    title: '提示',
-                    message: '请先完成国土证信息录入再提交,否则请切换为线下流程。',
-                    type: 'error',
-                    duration: 2000
-                  });
-                  ok = false;
+            //     }
+            //     if (item === 'LAND_CERTIFICATE' && !this.target.landCertificate) {
+            //       this.$notify({
+            //         title: '提示',
+            //         message: '请先完成国土证信息录入再提交,否则请切换为线下流程。',
+            //         type: 'error',
+            //         duration: 2000
+            //       });
+            //       ok = false;
                 
-                }
-                if (item === 'IMMOVABLE_CERTIFICATE' && !this.target.immovableCertificate) {
-                  this.$notify({
-                    title: '提示',
-                    message: '请先完成不动产权证信息录入再提交,否则请切换为线下流程。',
-                    type: 'error',
-                    duration: 2000
-                  });
-                  ok = false;
+            //     }
+            //     if (item === 'IMMOVABLE_CERTIFICATE' && !this.target.immovableCertificate) {
+            //       this.$notify({
+            //         title: '提示',
+            //         message: '请先完成不动产权证信息录入再提交,否则请切换为线下流程。',
+            //         type: 'error',
+            //         duration: 2000
+            //       });
+            //       ok = false;
                 
-                }
-              })
-              if (!ok){
-                verify.state = false;
-                callback(verify);
-                return;
-              }
-              if (this.compareList.calculateId==null || this.compareList.targetId==null){
-                this.$notify({
-                  title: '提示',
-                  message: '请先完成可比实例表。',
-                  type: 'error',
-                  duration: 2000
-                });
-                verify.state = false;
-                callback(verify);
-                return;
-              }
-              if (ok && nodeCode === 'GENERATE_STATEMENT' && (this.statementProd == null || this.statementProd.files.length === 0)) {
-                this.$notify({
-                  title: '提示',
-                  message: '请生成意见书,再提交流程',
-                  type: 'error',
-                  duration: 2000
-                });
-                verify.state = false;
-                callback(verify);
-                return;
-              }
-              if (ok && nodeCode === 'WRITE_REPORT' && (this.technicReportProd == null || this.technicReportProd.files.length === 0)) {
-                this.$notify({
-                  title: '提示',
-                  message: '请生成报告,再提交流程',
-                  type: 'error',
-                  duration: 2000
-                });
-                verify.state = false;
-                callback(verify);
-                return;
-              }
-              if (ok && nodeCode === 'WRITE_LETTER' && (this.letterReprotProd == null || this.letterReprotProd.files.length === 0)) {
-                this.$notify({
-                  title: '提示',
-                  message: '请生成复评函,再提交流程',
-                  type: 'error',
-                  duration: 2000
-                });
-                verify.state = false;
-                callback(verify);
-                return;
-              }
+            //     }
+            //   })
+            //   if (!ok){
+            //     verify.state = false;
+            //     callback(verify);
+            //     return;
+            //   }
+            //   if (this.compareList.calculateId==null || this.compareList.targetId==null){
+            //     this.$notify({
+            //       title: '提示',
+            //       message: '请先完成可比实例表。',
+            //       type: 'error',
+            //       duration: 2000
+            //     });
+            //     verify.state = false;
+            //     callback(verify);
+            //     return;
+            //   }
+            //   if (ok && nodeCode === 'GENERATE_STATEMENT' && (this.statementProd == null || this.statementProd.files.length === 0)) {
+            //     this.$notify({
+            //       title: '提示',
+            //       message: '请生成意见书,再提交流程',
+            //       type: 'error',
+            //       duration: 2000
+            //     });
+            //     verify.state = false;
+            //     callback(verify);
+            //     return;
+            //   }
+            //   if (ok && nodeCode === 'WRITE_REPORT' && (this.technicReportProd == null || this.technicReportProd.files.length === 0)) {
+            //     this.$notify({
+            //       title: '提示',
+            //       message: '请生成报告,再提交流程',
+            //       type: 'error',
+            //       duration: 2000
+            //     });
+            //     verify.state = false;
+            //     callback(verify);
+            //     return;
+            //   }
+            //   if (ok && nodeCode === 'WRITE_LETTER' && (this.letterReprotProd == null || this.letterReprotProd.files.length === 0)) {
+            //     this.$notify({
+            //       title: '提示',
+            //       message: '请生成复评函,再提交流程',
+            //       type: 'error',
+            //       duration: 2000
+            //     });
+            //     verify.state = false;
+            //     callback(verify);
+            //     return;
+            //   }
               
-            }
+            // }
             // else{
             //   if (nodeCode === 'GENERATE_STATEMENT' && (this.statementProd == null || this.statementProd.files.length === 0)) {
             //     this.$notify({
@@ -2504,6 +2541,7 @@ export default {
     saveFeedback() {
       let flag = true;
       const oldFeedback = JSON.parse(this.target.feedback);
+      console.log(oldFeedback)
       if (oldFeedback) {
         oldFeedback.forEach(element => {
           if (!this.feedback.includes(element)) {
@@ -2525,6 +2563,14 @@ export default {
           this.$message("只能选择一种产品类型");
           flag = false;
         }
+        if (this.currentNode.nodeCode === 'REPORT_FEEDBACK' && this.feedback.length > 3) {
+          this.$message("只能选择一种产品类型");
+          flag = false;
+        }
+        if (this.currentNode.nodeCode === 'LETTER_FEEDBACK' && this.feedback.length > 4) {
+          this.$message("只能选择一种产品类型");
+          flag = false;
+        }
       }
       if (flag) {
         const feedbackDTO = new Object();

+ 6 - 0
src/views/personal/todoList.vue

@@ -214,6 +214,12 @@
               <span>{{ row.agent == null ? '-' : row.agent }}</span>
             </template>
           </el-table-column>
+          <el-table-column label="最新备注" align="center" width="150"
+          prop="comments">
+          <template slot-scope="{row}">
+            <span>{{ row.comments ? row.comments : '-' }}</span>
+          </template>
+        </el-table-column>
         </parentTable>
       </y-page-list-layout>
     </el-dialog>

+ 8 - 6
vue.config.js

@@ -48,12 +48,14 @@ module.exports = {
         changeOrigin: true,
         pathRewrite: {}
       },
-      // '/api': {
-      //   // test (docker)
-      //   target: 'http://127.0.0.1:8089',
-      //   changeOrigin: true,
-      //   pathRewrite: {}
-      // },
+      '/ws': {
+        target: 'http://127.0.0.1:8088',
+        ws: true,
+        changeOrigin: true,
+        pathRewrite: {
+          '^/ws' : ''
+        }
+      },
       '/dfs': {
         target: 'http://127.0.0.1:80',
         changeOrigin: true,