Browse Source

修复复核显示不出来的问题

manywhy 1 tuần trước cách đây
mục cha
commit
7f19b6ff63

+ 3 - 0
mini.project.json

@@ -0,0 +1,3 @@
+{
+  "format": 2
+}

+ 9 - 0
public/index.html

@@ -17,5 +17,14 @@
     </noscript>
     <div id="app"></div>
     <!-- built files will be auto injected -->
+    <!-- <script src="https://g.alicdn.com/dingtalk/open-develop/1.9.0/dingtalk.js" defer></script>
+    <script src="https://o.alicdn.com/dingding/bird/index.js" defer></script>
+    <script src="https://g.alicdn.com/code/npm/@ali/dingtalk-h5-remote-debug/0.1.3/index.js"></script> -->
+    <!-- <script src="https://g.alicdn.com/dingtalk/open-develop/1.9.0/dingtalk.js" defer></script> -->
+    <script src="https://o.alicdn.com/dingding/bird/index.js" defer></script>
+    <!-- <script>
+      window.__BIRD_CONFIG = window.__BIRD_CONFIG || {};
+        __BIRD_CONFIG.disableReport = true;
+    </script> -->
   </body>
 </html>

+ 229 - 0
src/api/wsService.js

@@ -0,0 +1,229 @@
+
+import { getToken, generateUUID } from "@/api/auth";
+
+const url = 'wss://' + process.env.VUE_APP_WEBSCOKET + '/ws2/'
+class WsService {
+    constructor(url) {
+        this.url = url
+        this.authStatus = false
+        this.businessCallback = null
+        this.wsClient = null
+        this.initInterval()
+        this.initWebSocket()
+    }
+
+    initInterval() {
+        this.interval = 3000
+    }
+
+    initWebSocket() {
+        this.wsClient = new WebSocket(this.url)
+        this.initInterval()
+        this.wsClient.onmessage = (e) => {
+            let msg = JSON.parse(e.data)
+            if (msg.type === 'ping') this.wsClient.send("wsService check")
+            if (msg.type === 'auth' && msg.code === 1) this.authStatus = true
+
+            if (typeof this.businessCallback === 'function') {
+                this.businessCallback(msg)
+                this.businessCallback = null
+            }
+        }
+        this.wsClient.onopen = () => {
+            this.log('websocket open')
+            this.userAuth()
+        }
+        this.wsClient.onerror = (event) => {
+            this.log('websocket error', event)
+        }
+        this.wsClient.onclose = (e) => {
+            this.log('websocket close', e)
+            this.authStatus = false
+            if (this.interval > 0) {
+                let intervalId = setInterval(function () {
+                    this.log("interval start")
+                    if (this.wsClient.readyState === WebSocket.OPEN) {
+                        this.log("interval close")
+                        clearInterval(intervalId)
+                    } else {
+                        this.log("interval initWebSocket")
+                        this.initWebSocket()
+                    }
+                }.bind(this), this.interval)
+            }
+        }
+    }
+
+    getAuthStatus() {
+        return this.authStatus
+    }
+
+    getUrl() {
+        return this.url
+    }
+
+    setCallback(fun) {
+        if (typeof fun === 'function') this.businessCallback = fun
+    }
+
+    getReadyState() {
+        return this.wsClient.readyState
+    }
+
+    userAuth() {
+        let token = getToken()
+        let uuid = generateUUID()
+        if (!token || !uuid || !this.wsClient) return
+        this.wsClient.send(JSON.stringify({ type: 'auth', token: token, machine: uuid }))
+    }
+
+
+
+    send(data, fun) {
+        if (this.wsClient.readyState !== WebSocket.OPEN) return
+        this.businessCallback = typeof fun === 'function' ? fun : null
+
+        this.wsClient.send(typeof data === 'string' ? data : JSON.stringify(data))
+    }
+
+
+    close() {
+        this.wsClient.close()
+    }
+
+    closeForce() {
+        this.interval = 0
+        this.wsClient.close()
+    }
+
+    log(message, ...args) {
+        if (process.env.NODE_ENV === 'development') console.log(message, ...args)
+    }
+
+}
+
+
+class WsClient {
+    constructor(url) {
+        this.url = url
+        this.authStatus = false
+        this.businessCallback = null
+        this.errorCallback = null
+        this.closeCallback = null
+        this.userAuthCallback = null
+        this.wsClient = null
+    }
+
+    initWebSocket(userAuthCallback, businessCallback, errorCallback, closeCallback) {
+        if (this.wsClient) {
+            this.wsClient.close()
+            this.authStatus = false
+            this.businessCallback = null
+            this.businessFun = null
+            this.errorCallback = null
+            this.closeCallback = null
+            this.userAuthCallback = null
+            this.wsClient = null
+        }
+
+        this.wsClient = new WebSocket(this.url)
+        if (typeof userAuthCallback === 'function') this.userAuthCallback = userAuthCallback
+        if (typeof businessCallback === 'function') this.businessCallback = businessCallback
+        if (typeof errorCallback === 'function') this.errorCallback = errorCallback
+        if (typeof closeCallback === 'function') this.closeCallback = closeCallback
+        this.wsClient.onmessage = (e) => {
+            let msg = JSON.parse(e.data)
+            if (msg.type === 'ping') this.wsClient.send("WsClient check")
+            if (msg.type === 'auth' && msg.code === 1) {
+                this.authStatus = true
+                if (typeof this.userAuthCallback === 'function') {
+                    this.userAuthCallback(msg)
+                    this.userAuthCallback = null
+                }
+            }
+            /*一次性回调*/
+            if (typeof this.businessCallback === 'function') {
+
+                this.businessCallback(msg)
+                this.businessCallback = null
+            }
+            /*长期回调*/
+            if (typeof this.businessFun === 'function') this.businessFun(msg)
+        }
+        this.wsClient.onopen = () => {
+            this.log('WsClient open')
+            this.userAuth()
+        }
+        this.wsClient.onerror = (event) => {
+            this.log('WsClient error', event)
+            if (typeof this.errorCallback === 'function') {
+                this.errorCallback(event)
+                this.errorCallback = null
+            }
+        }
+        this.wsClient.onclose = (e) => {
+            this.log('WsClient close', e)
+            this.authStatus = false
+            if (typeof this.closeCallback === 'function') {
+                this.closeCallback(e)
+                this.closeCallback = null
+            }
+        }
+    }
+
+    setErrorCallback(fun) {
+        this.errorCallback = typeof fun === 'function' ? fun : null
+        return this
+    }
+
+    setCloseCallback(fun) {
+        this.closeCallback = typeof fun === 'function' ? fun : null
+        return this
+    }
+
+    setUserAuthCallback(fun) {
+        this.userAuthCallback = typeof fun === 'function' ? fun : null
+        return this
+    }
+
+    setBusinessFun(fun) {
+        this.businessFun = typeof fun === 'function' ? fun : null
+        return this
+    }
+
+    getAuthStatus() {
+        return this.authStatus
+    }
+
+    userAuth() {
+        let token = getToken()
+        let uuid = generateUUID()
+        if (!token || !uuid || !this.wsClient) return
+
+        this.send(JSON.stringify({ type: 'auth', token: token, machine: uuid }))
+    }
+
+
+
+    send(data, businessCallback) {
+        if (this.wsClient.readyState !== WebSocket.OPEN) return
+        this.businessCallback = typeof businessCallback === 'function' ? businessCallback : null
+
+        this.wsClient.send(typeof data === 'string' ? data : JSON.stringify(data))
+    }
+
+    close() {
+        if (this.wsClient) this.wsClient.close()
+    }
+
+
+    log(message, ...args) {
+        if (process.env.NODE_ENV === 'development') console.log(message, ...args)
+    }
+
+}
+
+export const wsClient = new WsClient(url)
+
+
+

+ 5 - 1
src/home.vue

@@ -128,7 +128,7 @@
 							<el-radio-group v-model="tabPosition" style="margin-bottom: 30px;display:flex">
 								<div v-for="(item, index) in rankingList" :key="index" style="">
 									<el-radio-button :label="item.val" @click.native="tabPositions($event, item)">
-										<span style="width:84px;coloe:#606266;display: block;text-overflow: ellipsis;white-space: nowrap;overflow: hidden;">
+										<span style="width:84px;color:#606266;display: block;text-overflow: ellipsis;white-space: nowrap;overflow: hidden;">
 											{{ item.group_name }}
 										</span>
 									</el-radio-button>
@@ -502,6 +502,10 @@ import QRCode from 'qrcodejs2';
 import examinePopup from '@/components/examinePopup';
 import { openSubscribeApp } from "dingtalk-service-window-libs"
 import { contactAdminToUseApp } from 'dingtalk-design-libs';
+const { send } = window.__BIRD || {};
+
+typeof send === 'function' && send();
+window.performance && window.performance.mark("FMP");
 export default {
 	components: { examinePopup },
 	data() {

+ 6 - 0
src/index.vue

@@ -240,6 +240,10 @@
 <script>
 import { createSocket,sendWSPush,sendPing } from './api/websocket'
 import { removeCache } from './api/auth'
+const { send } = window.__BIRD || {};
+
+typeof send === 'function' && send();
+window.performance && window.performance.mark("FMP");
 export default {
 	data() {
 		return {
@@ -466,11 +470,13 @@ export default {
 			// 在需要的时候卸载监听事件,比如离开页面
 			window.removeEventListener('onmessageWS', getsocketData)
 		},
+
 		getInform(){
 			let params = {
 				page: 1,
 				page_size: 1
 			};
+			
 			this.$axios('get', '/api/information/index', params).then(res => {
 				if (res.data.code == 1) {
 					let data= res.data.data;

+ 4 - 0
src/main.js

@@ -144,6 +144,10 @@ router.afterEach(() => {
   NProgress.done();
 });
 
+
+window.__BIRD_CONFIG = window.__BIRD_CONFIG || {};
+__BIRD_CONFIG.disableReport = true;
+
 Vue.config.productionTip = false
 new Vue({
   router,

+ 2 - 2
src/views/award/lotteryTicket_statistics.vue

@@ -23,12 +23,12 @@
 				></el-cascader>
 			</div>
 		</div>
-		<div class="form-item">
+		<!-- <div class="form-item">
 			<div class="form-label">姓名搜索</div>
 			<div class="form-search">
 				<el-input size="medium" v-model="formData.keyword" clearable placeholder="请输入"  maxlength="20"></el-input>
 			</div>
-		</div>
+		</div> -->
 	</FormBox>  
 	
     <div class="all">

+ 2 - 2
src/views/ranking/balanceA.vue

@@ -28,12 +28,12 @@
 							></el-cascader>
 					</div>
 				</div>
-				<div class="form-item">
+				<!-- <div class="form-item">
 					<div class="form-label">姓名搜索</div>
 					<div class="form-search">
 						<el-input size="medium" clearable v-model="formData.keyword" placeholder="请输入" max="200" />
 					</div>
-				</div>
+				</div> -->
 			</FormBox>
 			<div style="margin: 10px 0;" v-if="active == 'balanceA'">
 				<el-button size="medium" plain type="primary" @click="clearAPointShow = true">清空A分余额</el-button>

+ 2 - 2
src/views/set/framework.vue

@@ -50,12 +50,12 @@
 								</el-select>
 							</div>
 						</div>
-						<div class="form-item">
+						<!-- <div class="form-item">
 							<div class="form-label">姓名搜索</div>
 							<div class="form-search">
 								<el-input placeholder="请输入" size="medium" v-model="keywords" clearable></el-input>
 							</div>
-						</div>
+						</div> -->
 					</FormBox>
 					<div style="margin: 10px 0;" class="flex-box-ce">
 						<el-button @click="participation()" :loading="enable_loading" size="medium" type="primary">批量启用</el-button>

+ 3 - 2
src/views/set/jurisdiction.vue

@@ -38,11 +38,12 @@
 							<el-button size="small" v-show="item_info.code == 'dept_manager'" @click="toleadShw = true">导出/修改管理列表</el-button>
 							<el-button size="small" v-show="item_info.code == 'dept_manager'" @click="synchronization" type="primary">同步管理范围</el-button>
 						</div>
-						<div class="gap-right-8 fr" style="display:inline-block; width:180px;" v-show="item_info.code == 'dept_manager'">
+						<!-- <div class="gap-right-8 fr" style="display:inline-block; width:180px;" v-show="item_info.code == 'dept_manager'">
 							<el-input placeholder="搜索员工姓名" ref="search-bar" v-model="keyword" size="small" class="input-with-select" @keyup.enter.native="onFilterChanged">
 								<el-button slot="append" size="small" icon="el-icon-search" @click="onFilterChanged"></el-button>
 							</el-input>
-						</div>
+						</div> -->
+						
 					</div>
 					<div v-show="item_info.code == 'creator'">
 						<el-table :data="table_list" v-loading="table_loading">

+ 869 - 0
src/views/workbench/integral_review copy.vue

@@ -0,0 +1,869 @@
+<template>
+  <div class="all padding-20">
+    <el-card style="margin-bottom: 20px;">
+      <el-form inline>
+        <el-form-item>
+          <el-select class="date-picker-width" size="medium" v-model="formData.dc_status"  placeholder="复核状态" :disabled="loading">
+            <el-option v-for="item in dc_status_options" :key="item.id" :label="item.name" :value="item.id"/>
+          </el-select>
+        </el-form-item>
+        <el-form-item>
+          <el-select size="medium" v-model="formData.source_type" placeholder="全部来源" :disabled="loading">
+            <el-option v-for="item in source_types" :key="item.id" :label="item.name" :value="item.id"/>
+          </el-select>
+        </el-form-item>
+        <el-form-item>
+          <el-select size="medium" v-model="formData.is_enable" style="margin-right: 5px" :disabled="loading">
+            <el-option  label="已启用用户" :value="1"></el-option>
+            <el-option  label="未授权&未启用用户" :value="0"></el-option>
+          </el-select>
+          <el-tooltip effect="dark" placement="top">
+            <template slot="content">
+              人员状态过滤。默认仅显示组织架构中“已启用”人员的积分事件,未启用、已离职等人员的积分事件可以通过“未启用&未授权”筛选查看
+            </template>
+            <span><i class="el-icon-warning"></i></span>
+          </el-tooltip>
+        </el-form-item>
+        <el-form-item>
+          <el-select size="medium" v-model="formData.employee_ids" filterable clearable placeholder="请输入或选择人员" :disabled="loading">
+            <el-option v-for="item in employee_list" :key="item.id" :label="item.name" :value="item.id"></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item>
+          <el-select  size="medium" v-model="formData.pt_id" clearable placeholder="A/B分" :disabled="loading">
+            <el-option v-for="item in pts" :key="item.name" :label="item.name" :value="item.id"/>
+          </el-select>
+        </el-form-item>
+
+        <el-form-item>
+          <el-cascader
+              v-model="formData.rules"
+              :options="rule_trees"
+              :props="{ checkStrictly: true, value: 'id', label: 'name', children: 'child' }"
+              @change="onRuleChange"
+              size="medium"
+              ref="rule"
+              clearable
+              placeholder="规则分类"
+              :disabled="loading"
+          />
+        </el-form-item>
+        <el-form-item>
+          <el-date-picker
+              v-model="formData.time_scope"
+              type="daterange"
+              size="medium"
+              value-format="yyyy-MM-dd"
+              range-separator="至"
+              start-placeholder="开始日期"
+              end-placeholder="结束日期"
+              :disabled="loading"
+          />
+        </el-form-item>
+
+        <el-form-item>
+          <el-input
+              size="medium"
+              clearable
+              max="20"
+              v-model="formData.keyword"
+              placeholder="搜索内容"
+              :disabled="loading"
+          />
+        </el-form-item>
+<!--        <el-form-item>-->
+<!--          <el-input-->
+<!--              size="medium"-->
+<!--              clearable-->
+<!--              v-model="formData.point"-->
+<!--              placeholder="分值"-->
+<!--              @input="onPointInput"-->
+<!--              :disabled="loading"-->
+<!--          />-->
+<!--        </el-form-item>-->
+
+        <el-form-item>
+          <el-cascader
+              size="medium"
+              v-model="dept_names"
+              :options="dept_tree"
+              :props="{ checkStrictly: true, multiple: true, value: 'id', label: 'name', children: '_child' }"
+              ref="dept"
+              filterable
+              collapse-tags
+              clearable
+              placeholder="全公司"
+              style="min-width: 300px"
+              :disabled="loading"
+          />
+        </el-form-item>
+        <el-form-item>
+          <NumberRangeSelector
+              :min.sync="formData.minPoint"
+              :max.sync="formData.maxPoint"
+              :disabled="loading"
+              title="分值区间"
+              @scopeSubmit="handlePointScopeSubmit"
+          />
+        </el-form-item>
+      </el-form>
+      <el-row :gutter="5" justify="center" align="center">
+        <el-col :span="4">
+          <el-button-group>
+            <el-button type="primary" size="medium" :disabled="!canSubmitBatch" @click="openBatch">批处理</el-button>
+          </el-button-group>
+          <span style="padding-left: 20px;" :class="websocketAuth ? 'green' : 'orange'">
+            <i :class="websocketAuth ? 'el-icon-connection' : 'el-icon-warning-outline'">
+              {{websocketAuth ? '服务已启动' : '服务启动中.....'}}
+            </i>
+          </span>
+        </el-col>
+      </el-row>
+    </el-card>
+    <div>
+      <el-table
+          :data="integralList"
+          style="width: 100%;"
+          v-loading="loading"
+          @row-click="openDetail"
+          @selection-change="onSelectionChange"
+      >
+        <el-table-column
+            v-if="formData.dc_status === 0"
+            type="selection"
+            width="55"
+            :key="1"
+        />
+        <el-table-column prop="employee_name" label="姓名" align="center" width="200px" :key="2" >
+          <template slot-scope="scope">
+            <div class="flex-box">
+              <userImage :user_name="scope.row.employee_name" :img_url="scope.row.employee_img_url" width="50px" height="50px"></userImage>
+              <span style="line-height: 50px; padding-left: 10px;">{{ scope.row.employee_name }}</span>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column
+            prop="dept"
+            show-overflow-tooltip
+            label="部门"
+            align="center"
+            :key="3"
+        />
+        <el-table-column prop="point" label="积分" align="center" :key="4">
+          <template slot-scope="scope">
+            <span :class="scope.row.point < 0 ? 'green' : 'red'">{{ scope.row.point }} {{ $getTypsName(scope.row.pt_id) }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="remark" label="事件内容" align="center" show-overflow-tooltip :key="5" />
+        <el-table-column prop="event_time" label="时间" align="center" :key="6"/>
+        <el-table-column prop="source_type" label="事件来源" align="center" :key="7">
+          <template slot-scope="scope">
+            <span v-show="scope.row.source_type == 1">
+              积分奖扣
+              <span v-if="scope.row.recorder_name">({{ scope.row.recorder_name }})</span>
+            </span>
+            <span v-show="scope.row.source_type == 2">任务</span>
+            <span v-show="scope.row.source_type == 3">积分系统分配</span>
+            <span v-show="scope.row.source_type == 4">考勤系统分配</span>
+            <span v-show="scope.row.source_type == 5">
+								积分申请
+								<span v-if="scope.row.applyor_name">({{ scope.row.applyor_name }})</span>
+							</span>
+            <span v-show="scope.row.source_type == 6">绩效任务包</span>
+            <span v-show="scope.row.source_type == 8">积分导入</span>
+            <span v-show="scope.row.source_type == 9">A分转B分</span>
+            <span v-show="scope.row.source_type == 10">钉钉汇报(日志)奖扣分</span>
+            <span v-show="scope.row.source_type > 10">其他</span>
+          </template>
+        </el-table-column>
+<!--        <el-table-column prop="create_time" label="操作" v-if="formData.dc_status === 0" :key="8">-->
+<!--          <template slot-scope="scope">-->
+<!--            <el-link :underline="false" type="primary" :disabled="loading" @click.stop="openPass(scope.row)">通过</el-link>-->
+<!--            <el-link :underline="false" type="primary" :disabled="loading" style="padding: 0 10px;" @click.stop="setGrade(scope.row)">调整分数</el-link>-->
+<!--            <el-link :underline="false" type="danger" :disabled="loading" style="padding: 0 10px;" @click.stop="reject(scope.row)">驳回</el-link>-->
+<!--          </template>-->
+<!--        </el-table-column>-->
+        <template slot="empty">
+          <noData></noData>
+        </template>
+      </el-table>
+      <center class="pagination">
+        <el-pagination
+            background
+            @size-change="v => formData.page_size = v"
+            @current-change="v => formData.page = v"
+            :current-page="formData.page"
+            :page-sizes="[10, 20, 50,100]"
+            layout="total, sizes, prev, pager, next"
+            :page-size="formData.page_size"
+            :total="formData.total"
+        />
+      </center>
+    </div>
+    <el-dialog
+        :visible.sync="showDetail"
+        custom-class="text-center"
+        title="积分信息"
+        :close-on-click-modal="!loading || !currentItem"
+        :close-on-press-escape="!loading || !currentItem"
+        :show-close="false"
+        :before-close="closeDetail"
+    >
+      <div v-if="currentItem">
+        <el-row :gutter="5" v-if="this.currentItem.dc_status === 0" style="margin-bottom: 10px;">
+          <el-col :span="6" v-if="event_review_point">
+            <el-input size="small" v-model="currentItem.point" type="number">
+              <template #prepend>积分</template>
+            </el-input>
+          </el-col>
+          <el-col :span="event_review_point > 0 ? 18 : 24" >
+            <el-input :size=" event_review_point > 0 ? 'small' : 'medium'" v-model="currentItem.comment" maxlength="50" show-word-limit  clearable >
+              <template #prepend>备注</template>
+            </el-input>
+          </el-col>
+        </el-row>
+        <el-row type="flex" justify="end" style="margin-bottom: 10px;">
+          <el-col :span="6" style="text-align: end">
+            <el-button-group>
+              <el-button :size=" event_review_point > 0 ? 'small' : 'medium'" type="warning" @click="disagree" :disabled="loading">驳回</el-button>
+              <el-button :size=" event_review_point > 0 ? 'small' : 'medium'" type="primary" :disabled="!websocketAuth" :loading="loading" @click="agree">通过</el-button>
+            </el-button-group>
+          </el-col>
+        </el-row>
+        <el-descriptions
+            :column="1"
+            border
+            :label-style="{textAlign:'center',width:'100px'}"
+            :content-style="{textAlign: 'center'}"
+        >
+          <el-descriptions-item label="来源" v-if="currentItem.source_type_mark">{{currentItem.source_type_mark}}</el-descriptions-item>
+          <el-descriptions-item label="姓名">{{currentItem.employee_name}}&nbsp;{{currentItem.dept}}</el-descriptions-item>
+          <el-descriptions-item label="记录人" v-if="currentItem.recorder_name">{{currentItem.recorder_name}}</el-descriptions-item>
+          <el-descriptions-item label="积分">{{currentItem.point_mark}}&nbsp;{{$getTypsName(currentItem.pt_id)}}</el-descriptions-item>
+          <el-descriptions-item label="内容">{{currentItem.remark.customize}}</el-descriptions-item>
+          <el-descriptions-item label="事件时间">{{currentItem.event_time}}</el-descriptions-item>
+          <el-descriptions-item label="创建时间">{{currentItem.create_time}}</el-descriptions-item>
+          <el-descriptions-item label="附件" v-if="currentItem.files && currentItem.files.length > 0">
+            <el-image
+                v-for="(img,index) in currentItem.files"
+                style="width: 80px; height: 80px;margin: 0 5px;"
+                :key="index"
+                :src="img"
+                :preview-src-list="currentItem.files"
+            />
+          </el-descriptions-item>
+          <el-descriptions-item label="完成时间" v-if="currentItem.complete_task.time">{{currentItem.complete_task.time || ''}}</el-descriptions-item>
+          <el-descriptions-item label="完成备注" v-if="currentItem.complete_task.remark">{{currentItem.complete_task.remark || ''}}</el-descriptions-item>
+          <el-descriptions-item label="任务附件" v-if="currentItem.complete_task && currentItem.complete_task.files && currentItem.complete_task.files.length > 0">
+            <el-image
+                v-for="(img,index) in currentItem.complete_task.files"
+                style="width: 80px; height: 80px;margin: 0 5px;"
+                :key="index"
+                :src="img"
+                :preview-src-list="currentItem.complete_task.files"
+            />
+          </el-descriptions-item>
+          <el-descriptions-item label="细则分类" v-if="currentItem.rule_list">{{currentItem.rule_list}}</el-descriptions-item>
+          <el-descriptions-item label="细则" v-if="currentItem.rule_item.remark">{{currentItem.rule_item.remark}}</el-descriptions-item>
+        </el-descriptions>
+<!--        <template v-if="currentItem?.dc_remark?.flow?.length > 0">-->
+        <template v-if="currentItem.dc_remark && currentItem.dc_remark.flow">
+          <h3 style="margin: 10px 0;">复核流程</h3>
+          <el-steps
+              :active="currentItem.dc_remark.flow.length"
+              align-center
+          >
+            <el-step
+                v-for="(item,index) in currentItem.dc_remark.flow"
+                :key="index"
+                :title="`${item.employee_name} ${item.action === 'agree' ? '复核通过' : (item.action === 'disagree' ? '复核驳回' : '')}`"
+            >
+              <template #description>
+                {{$moment(item.time * 1000).format('YYYY-MM-DD')}}&nbsp;{{item.comment || ''}}
+              </template>
+            </el-step>
+          </el-steps>
+        </template>
+      </div>
+      <el-empty v-else/>
+    </el-dialog>
+    <el-dialog
+        :visible.sync="showBatch"
+        custom-class="text-center"
+        title="批处理"
+        :close-on-click-modal="!loading || !selectedItems || selectedItems.length === 0"
+        :close-on-press-escape="!loading || !selectedItems || selectedItems.length === 0"
+        :show-close="false"
+        :before-close="closeBatch"
+    >
+      <el-row :gutter="5">
+        <el-col :span="18">
+          <el-input
+              v-model="batchComment"
+              maxlength="50"
+              show-word-limit
+              :disabled="loading"
+          >
+            <template #prepend>备注</template>
+          </el-input>
+        </el-col>
+        <el-col :span="6">
+          <el-button-group>
+            <el-button  type="warning" @click="disagreeBatch" :disabled="loading">驳回</el-button>
+            <el-button  type="primary" :disabled="!websocketAuth" :loading="loading" @click="agreeBatch">通过</el-button>
+          </el-button-group>
+        </el-col>
+      </el-row>
+      <el-progress text-inside :stroke-width="20" :percentage="batchProgress" style="margin: 10px 0;" v-show="loading" />
+      <el-table :data="selectedItems">
+        <el-table-column
+            prop="employee_name"
+            show-overflow-tooltip
+            label="姓名"
+            align="center"
+        />
+        <el-table-column
+            prop="point_mark"
+            show-overflow-tooltip
+            label="积分"
+            align="center"
+        />
+        <el-table-column
+            prop="remark"
+            show-overflow-tooltip
+            label="内容"
+            align="center"
+        />
+        <el-table-column align="center">
+          <template #header>
+            结果&nbsp;
+            <el-dropdown trigger="click" @command="copyResult" v-if="canClipboard">
+              <span><i class="el-icon-more"/></span>
+              <el-dropdown-menu slot="dropdown">
+                <el-dropdown-item command="normal">复制结果</el-dropdown-item>
+                <el-dropdown-item command="origin">复制结果源</el-dropdown-item>
+              </el-dropdown-menu>
+            </el-dropdown>
+          </template>
+          <template slot-scope="scope">
+            <el-link :type="scope.row.code === 1 ? 'primary' : (scope.row.code === 1 ? 'primary' : 'warning')">
+              <template v-if="loading && scope.row.code === -1">
+                <i class="el-icon-loading"></i>
+              </template>
+              <template v-else>
+                {{ batchResultMark(scope.row) }}
+              </template>
+            </el-link>
+          </template>
+        </el-table-column>
+      </el-table>
+    </el-dialog>
+  </div>
+</template>
+
+
+<script>
+import {onlyNumberFilter} from "@/utils";
+import {isNumeric} from "echarts/lib/util/number";
+import {_debounce, getTypsName} from "@/api/auth";
+import {wsClient} from "@/api/wsService";
+import NumberRangeSelector from '@/components/numberRangeSelector'
+
+export default {
+  name: "integral_review",
+  components:{NumberRangeSelector},
+  data(){
+    let pts = this.$getTyps().filter(item => item.code !== 'JX');
+    return {
+      pts,
+      rule_trees:[],
+      employee_list:[],
+      event_review_point:0,
+      source_types:[
+        { id: 0, name: '全部来源' },
+        { id: 1, name: '积分奖扣' },
+        { id: 2, name: '任务' },
+        { id: 3, name: '积分系统分配' },
+        { id: 4, name: '考勤系统分配' },
+        { id: 5, name: '积分申请' },
+        // {id: 6,name: '绩效任务包'},
+        { id: 8, name: '积分导入' },
+        { id: 9, name: 'A分转B分' },
+        { id: 10, name: '钉钉汇报(日志)分' }
+      ],
+      dc_status_options:[
+        { id: 0, name: '待复核' },
+        { id: 1, name: '通过' },
+        { id: 2, name: '驳回' },
+      ],
+      loading: false,
+      dept_names:[],
+      dept_tree: [],
+      formData: {
+        page: 1,
+        page_size: 10,
+        employee_ids:'',
+        pt_id:'',
+        source_type:0,
+        rules:[],
+        rule_id:0,
+        time_scope:[],
+        start_day:'',
+        end_day:'',
+        dc_status:0,
+        keyword:'',
+        point:'',
+        is_enable:1,
+        dept_ids:'',
+        minPoint:null,
+        maxPoint:null,
+      },
+      integralList:[],
+      selectedItems:[],
+      wsClient: wsClient,
+      showDetail:false,
+      showBatch:false,
+      currentItem:null,
+      batchComment:'',
+      batchPoint:0,
+      batchResult:[],
+      eventTypeRemark:{
+        1:'任务奖分',
+        2:'任务扣分',
+        3:'奖扣奖分',
+        4:'奖扣扣分',
+        5:'申请奖分',
+        6:'申请扣分',
+        7:'绩效任务包奖分',
+        8:'绩效任务包扣分',
+        9:'考勤系统奖分',
+        10:'考勤系统扣分',
+        11:'系统奖分',
+        12:'系统扣分',
+        13:'奖分任务扣分',
+        14:'扣分任务扣分',
+        15:'奖扣分任务比例扣分',
+        16:'奖扣分任务次数扣分',
+        17:'自动积分',
+        18:'导入积分奖分',
+        19:'导入积分扣分',
+        20:'A分转B分奖分',
+        21:'A分转B分扣分',
+        22:'钉钉报告(日志)奖分',
+        23:'钉钉报告(日志)扣分',
+        24:'任务提前完成奖分',
+        25:'任务逾期扣分',
+      }
+    }
+  },
+  methods:{
+    getTypsName,
+    getRuleTrees(){
+      this.$axios('get', '/api/integral/rule/trees').then(res => {
+        this.rule_trees = this.getRuleTreeData(res.data.data.rule_tree);
+      });
+    },
+    // 规则递归 children
+    getRuleTreeData(data) {
+      for (var i = 0; i < data.length; i++) {
+        if (data[i].child.length < 1) {
+          // children若为空数组,则将children设为undefined
+          data[i].child = undefined;
+        } else {
+          // children若不为空数组,则继续 递归调用 本方法
+          this.getRuleTreeData(data[i].child);
+        }
+      }
+      return data;
+    },
+    getEmployee(){
+      this.$axios('get', '/api/employee/index', { page: 0, page_size: 3000, is_official: 1 }).then(res => {
+        this.employee_list = res.data.data.list;
+      });
+    },
+    onRuleChange(val){
+      this.formData.rule_id = val.length === 0 ? 0 : this.formData.rules[this.formData.rules.length - 1]
+    },
+    onPointInput(v){
+      this.formData.point = onlyNumberFilter(v)
+    },
+    getDepartment(){
+      this.$axios('get', '/api/department/tree').then(res => {
+        this.dept_tree = this.getTreeData(res.data.data.list);
+      });
+    },
+    // 递归判断列表,把最后的children设为undefined
+    getTreeData(data) {
+      for (var i = 0; i < data.length; i++) {
+        if (data[i]._child.length < 1) {
+          // children若为空数组,则将children设为undefined
+          data[i]._child = undefined;
+        } else {
+          // children若不为空数组,则继续 递归调用 本方法
+          this.getTreeData(data[i]._child);
+        }
+      }
+      return data;
+    },
+    handlePointScopeSubmit(){
+      this.formData.page = 1
+      this.getIntegralList()
+    },
+    getIntegralList(){
+      let self = this
+      if (self.loading) return;
+      self.loading = true;
+      let data = {
+        page:self.formData.page,
+        page_size:self.formData.page_size,
+        is_enable:self.formData.is_enable,
+        dc_status:[self.formData.dc_status],
+      }
+      if ([1,2].includes(self.formData.dc_status)) data.order_key = 'dc_time'
+
+      if (self.formData.source_type) data.source_type = self.formData.source_type;
+      if (self.formData.employee_ids) data.employee_ids = self.formData.employee_ids;
+      if (self.formData.pt_id) data.pt_id = self.formData.pt_id;
+      if (self.formData.rule_id) data.rule_id = self.formData.rule_id;
+      if (self.formData.start_day && self.formData.end_day) {
+        data.start_day = self.formData.start_day
+        data.end_day = self.formData.end_day
+      }
+      if (self.formData.keyword) data.keyword = self.formData.keyword
+      if (isNumeric(self.formData.point)) data.point = self.formData.point;
+      if (self.formData.dept_ids) data.dept_ids = self.formData.dept_ids;
+      if (self.formData.minPoint !== null && self.formData.maxPoint !== null) {
+        data.min_point = self.formData.minPoint
+        data.max_point = self.formData.maxPoint
+      }
+
+      self.$axios('get', '/api/integral/statistics/integral', data).then(res => {
+        self.integralList = res.data.data.list;
+        if(data.page === 1){
+          self.formData.total = res.data.data.total;
+        }
+      }).finally(() => {
+        self.loading = false;
+      });
+    },
+    onSelectionChange(selection){
+      this.selectedItems = selection.map(item => {
+        return {
+          event_id:item.id,
+          employee_name:item.employee_name,
+          point:item.point,
+          point_mark:item.point_mark + " " + this.$getTypsName(item.pt_id),
+          remark:item.remark,
+          hasFinish:false,
+          code:-1,
+          msg:'',
+        }
+      })
+    },
+    openDetail(item){
+      this.currentItem = item
+
+      let data = {
+        event_id: item.id
+      }
+      this.loading = true
+      this.$axios('get', '/api/integral/statistics/integral/info', data)
+          .then(res => {
+            if (res.data.code !== 1) return
+            this.currentItem.dc_remark = res.data.data.dc_remark
+            this.currentItem.files = res.data.data.files
+            this.currentItem.rule_id = res.data.data.rule_id
+            this.currentItem.rule_item = res.data.data.rule_item
+            this.currentItem.rule_item_id = res.data.data.rule_item_id
+            this.currentItem.rule_list = res.data.data.rule_list
+            this.currentItem.remark = res.data.data.remark
+            this.currentItem.source_type_mark = res.data.data.source_type_mark
+            this.currentItem.recorder_name = res.data.data.recorder_name
+            this.currentItem.complete_task = res.data.data.complete_task
+          })
+          .finally(() => {
+            this.loading = false
+            this.showDetail = true
+          })
+    },
+    closeDetail(){
+      this.showDetail = false
+      this.currentItem = null
+      this.getIntegralList()
+    },
+    agree(){
+      if (!this.currentItem || this.loading) return
+      let data = {
+        type:'event_check',
+        action:'agree',
+        event_id:this.currentItem.id,
+        point:this.currentItem.point,
+        comment:this.currentItem?.comment || ''
+      }
+      this.showDetail = false
+      this.currentItem = null
+      this.loading = true
+      if (!this.wsClient.getAuthStatus()){
+        this.wsClient.initWebSocket(() => {
+          this.wsClient.send(data)
+        },(msg) => {
+          if (msg.type !== 'event_check') return
+          this.loading = false
+          this.getIntegralList()
+        })
+      }else {
+        this.wsClient.send(data,(msg) => {
+          if (msg.type !== 'event_check') return
+          this.loading = false
+          this.getIntegralList()
+        })
+      }
+    },
+    disagree(){
+      if (!this.currentItem || this.loading) return
+      let data = {
+        type:'event_check',
+        action:'disagree',
+        event_id:this.currentItem.id,
+        comment:this.currentItem?.comment || ''
+      }
+      this.showDetail = false
+      this.currentItem = null
+      this.loading = true
+      if (!this.wsClient.getAuthStatus()){
+        this.wsClient.initWebSocket(() => {
+          this.wsClient.send(data)
+        },(msg) => {
+          if (msg.type !== 'event_check') return
+          this.loading = false
+          this.getIntegralList()
+        })
+      }else {
+        this.wsClient.send(data,(msg) => {
+          if (msg.type !== 'event_check') return
+          this.loading = false
+          this.getIntegralList()
+        })
+      }
+
+    },
+    openBatch(){
+      console.log(this.selectedItems.map(item => item.event_id).toString())
+      this.showBatch = true
+      this.batchComment = ''
+    },
+    closeBatch(){
+      this.showBatch = false
+      this.batchComment = ''
+      this.batchResult = []
+      this.getIntegralList()
+    },
+    agreeBatch(){
+      this.loading = true
+      this.batchResult = []
+      this.wsClient.setBusinessFun((res) => {
+        if (res.type !== 'ping') this.batchResult.push(res)
+        if (res.type !== 'event_check') return
+        this.selectedItems.filter(item => item.event_id === res?.result?.source_msg?.event_id).forEach(item => {
+          item.hasFinish = true
+          item.code = res.code
+          item.msg = res.code === 1 ? '已处理' : (res?.msg ? res.msg : '处理失败')
+        })
+        if (this.selectedItems.filter(item => item.hasFinish).length >= this.selectedItems.length) this.loading = false
+      })
+      this.selectedItems.map(item => {
+        return {
+          type:'event_check',
+          action:'agree',
+          event_id:item.event_id,
+          point:item.point,
+          comment:this.batchComment || ''
+        }
+      }).forEach(data => {
+        this.wsClient.send(data)
+      })
+    },
+    disagreeBatch(){
+      this.loading = true
+      this.batchResult = []
+      this.wsClient.setBusinessFun(res => {
+        if (res.type !== 'ping') this.batchResult.push(res)
+        if (res.type !== 'event_check') return
+        this.selectedItems.filter(item => item.event_id === res?.result?.source_msg?.event_id).forEach(item => {
+          item.hasFinish = true
+          item.code = res.code
+          item.msg = res.code === 1 ? '已处理' : (res?.msg ? res.msg : '处理失败')
+        })
+        if (this.selectedItems.filter(item => item.hasFinish).length >= this.selectedItems.length) this.loading = false
+      })
+      this.selectedItems.map(item => {
+        return {
+          type:'event_check',
+          action:'disagree',
+          event_id:item.event_id,
+          comment:this.batchComment || ''
+        }
+      }).forEach(data => {
+        this.wsClient.send(data)
+      })
+    },
+    copyResult(origin){
+      switch (origin){
+        case 'normal':
+          let result = this.batchResult.filter(res => res.type === 'event_check').map(res => {
+            return {
+              type:res?.result?.source_msg?.type === 'event_check' ? '复核' : res.type,
+              action: res?.result?.source_msg?.action === 'agree' ? '同意' : (res?.result?.source_msg?.action === 'disagree' ? '驳回' : '未知'),
+              msg: res?.msg,
+              code: res?.code,
+              origin: {
+                action:res?.result?.source_msg?.action,
+                comment:res?.result?.source_msg?.comment,
+                no:res?.result?.source_msg?.event_id,
+                p:res?.result?.source_msg?.point,
+                t:res?.result?.source_msg?.type,
+              }
+            }
+          })
+          navigator.clipboard.writeText(JSON.stringify(result)).then(() => {
+            this.$message.success("已复制")
+          },(reason) => {
+            console.error('integral review clipboard',reason)
+            this.$message.info("没有任何结果")
+          })
+          break;
+        case 'origin':
+          navigator.clipboard.writeText(JSON.stringify(this.batchResult)).then(() => {
+            this.$message.success("已复制")
+          },(reason) => {
+            console.error('integral review clipboard',reason)
+            this.$message.info("没有任何结果")
+          })
+          break;
+      }
+    },
+    batchResultMark(item){
+      return !!item.msg ? item.msg : '待处理'
+    },
+
+  },
+  watch:{
+    'formData.time_scope'(v){
+      if (v){
+        this.formData.start_day = v[0]
+        this.formData.end_day = v[1]
+      }else {
+        this.formData.start_day = ''
+        this.formData.end_day = ''
+      }
+      this.formData.page = 1
+      this.getIntegralList()
+    },
+    'formData.employee_ids'(v){
+      this.formData.page = 1
+      this.getIntegralList()
+    },
+    'formData.pt_id'(v){
+      this.formData.page = 1
+      this.getIntegralList()
+    },
+    'formData.source_type'(v){
+      this.formData.page = 1
+      this.getIntegralList()
+    },
+    'formData.rule_id'(v){
+      this.formData.page = 1
+      this.getIntegralList()
+    },
+    'formData.dc_status'(v){
+      this.formData.page = 1
+      if (v !== 0) this.selectedItems = []
+      this.getIntegralList()
+    },
+    'formData.dept_ids'(v){
+      this.formData.page = 1
+      this.getIntegralList()
+    },
+    'formData.keyword' : {
+      deep: true,
+      handler: _debounce(function (v){
+        this.formData.page = 1
+        this.getIntegralList()
+      })
+    },
+    'formData.point': {
+      deep: true,
+      handler: _debounce(function (v,o){
+        if (v === o) return
+        this.formData.page = 1
+        this.getIntegralList()
+      })
+    },
+    'formData.is_enable'(v){
+      this.formData.page = 1
+      this.getIntegralList()
+    },
+    'formData.page'(v){
+      this.getIntegralList()
+    },
+    'formData.page_size'(v){
+      this.formData.page = 1
+      this.getIntegralList()
+    },
+    dept_names(v){
+      if (v.length !== 0){
+        let dept_ids = v.map(item => item[item.length - 1])
+        let set = new Set(dept_ids)
+        this.formData.dept_ids = [...set].toString()
+      } else {
+        this.formData.dept_ids = []
+      }
+      this.formData.page = 1
+      this.getIntegralList()
+    }
+  },
+  computed:{
+    websocketAuth(){
+      return this.wsClient.getAuthStatus()
+    },
+    canSubmitBatch(){
+      return !this.loading && this.websocketAuth && this.selectedItems && this.selectedItems.length > 0
+    },
+    batchProgress(){
+      return this.selectedItems && this.selectedItems.length > 0 ? (this.selectedItems.filter(item => item.hasFinish).length / this.selectedItems.length) * 100 : 0
+    },
+    canClipboard(){
+      return !!navigator?.clipboard && this.batchResult && this.batchResult.length > 0
+    }
+  },
+  mounted(){
+    this.getEmployee();
+    this.getDepartment();
+    this.getRuleTrees();
+    this.getIntegralList();
+    let siteConfig = this.$getCache('siteConfig');
+    this.event_review_point = siteConfig.event_review_point;
+
+    //初始化长连接
+    this.wsClient.initWebSocket()
+
+  },
+  beforeDestroy() {
+    if (this.wsClient) this.wsClient.close()
+  },
+
+  destroyed() {
+  }
+
+
+}
+</script>
+
+<style scoped lang="scss">
+
+.el-table tr{
+  cursor: pointer !important;
+}
+
+
+</style>

+ 0 - 6
src/views/workbench/integral_review.vue

@@ -847,12 +847,6 @@ export default {
     //初始化长连接
     this.wsClient.initWebSocket()
 
-
-
-
-
-
-
   },
   beforeDestroy() {
     if (this.wsClient) this.wsClient.close()