walter 10 kuukautta sitten
vanhempi
commit
3faf0866b4

+ 214 - 0
src/components/AppealCreate.vue

@@ -0,0 +1,214 @@
+<template>
+  <el-dialog
+    :title="title"
+    :visible.sync="visible"
+    :modal="false"
+    :close-on-click-modal="false"
+    :close-on-press-escape="false"
+    :center="true"
+    :show-close="false"
+    :before-close="beforeCloseHandler"
+    @open="openHandler"
+    @close="closeHandler"
+    append-to-body
+    width="1000px"
+    top="5%"
+  >
+
+    <template>
+      <el-form v-model="rewriteData" v-loading="loading">
+        <el-form-item label="审批人" required>
+          <el-input class="w250" autocomplete="off" v-model="rewriteData.reviewerName" placeholder="请选择审批人" />
+          <div @click="showReviewerSelector = true" style="position: absolute; top: 0; right: 0; left: 0; bottom: 0; z-index: 9;"></div>
+          <EmployeeSelector
+            :multi="false"
+            :user_employee_list="true"
+            :isChecKedAll="false"
+            :is_filtration_creator="false"
+            :employee_list="superiorList"
+            :selected="rewriteData.employeeSelected"
+            :visible.sync="showReviewerSelector"
+            @confirm="handleRewriteReviewerSelector"
+          />
+        </el-form-item>
+        <el-form-item label="原因说明">
+          <el-input type="textarea" :rows="2" placeholder="填写原因说明" v-model="rewriteData.globalRemark" maxlength="100" show-word-limit />
+        </el-form-item>
+        <el-form-item label="积分事件" required>
+        </el-form-item>
+        <el-card>
+          <div slot="header">
+            <span>共{{rewriteData.events.length}}条积分事件</span>
+            <el-button @click="openEventSelected" type="primary" size="mini" >选择积分</el-button>
+          </div>
+          <IntegralEventSelector
+            :visible.sync="showEventSelector"
+            :title="userInfo.name + '的积分事件'"
+            :eid="userInfo.id"
+            :selected="eventSelected"
+            :max="100"
+            @confirm="onEventSelected"
+          />
+          <el-scrollbar
+            :native="false"
+            style="height: 300px"
+          >
+            <el-form-item v-for="(item,index) in rewriteData.events" :key="index">
+              <el-tag style="margin-right: 10px">{{item.remark}}</el-tag><i class="el-icon-delete pointer" @click="deleteEvent(item)" />
+              <el-input type="textarea" :rows="2" v-model="item.appeal_remark" placeholder="申诉原因" maxlength="100" show-word-limit />
+            </el-form-item>
+          </el-scrollbar>
+        </el-card>
+      </el-form>
+    </template>
+
+    <template slot="footer">
+      <div class="flex-box-end flex-v-ce">
+        <el-button @click="close" :loading="submitting || !hasOpen">关闭</el-button>
+        <el-button type="primary" @click="createAppeal" :loading="submitting || !hasOpen" :disabled="!canSubmit">确认</el-button>
+      </div>
+    </template>
+
+  </el-dialog>
+</template>
+
+<script>
+import IntegralEventSelector from './IntegralEventSelector.vue'
+import Template from "../examine/components/Template.vue";
+import EmployeeSelector from "./EmployeeSelector.vue";
+export default {
+  name: 'AppealCreate',
+  components:{EmployeeSelector, Template, IntegralEventSelector},
+  props:{
+    visible:{
+      type: Boolean,
+      default: false
+    },
+    title:{
+      type: String,
+      default: '发起复议'
+    },
+    events:{
+      type: Array,
+      default: () => {
+        return []
+      }
+    }
+  },
+  data(){
+    const pts =  this.$getTyps()
+    return {
+      userInfo: this.$userInfo(),
+      pts:pts,
+      rewriteData:{
+        events:[],
+        globalRemark:'',
+        reviewerId:'',
+        reviewerName:'',
+        employeeSelected: { dept: [], employee: [] },
+      },
+      showReviewerSelector:false,
+      showEventSelector:false,
+      superiorList:[],
+      loading:false,
+      eventSelected:[],
+      submitting:false,
+      hasOpen:false,
+    }
+  },
+  computed:{
+    canSubmit(){
+      return this.rewriteData.events.length > 0 && this.rewriteData.reviewerId
+    }
+  },
+  methods:{
+    initData(){
+      this.rewriteData = {
+        events:[],
+        globalRemark:'',
+        reviewerId:'',
+        reviewerName:'',
+        employeeSelected: { dept: [], employee: [] },
+      }
+      this.showReviewerSelector = false
+      this.loading = false
+      this.submitting = false
+      this.hasOpen = false
+    },
+    beforeCloseHandler(){
+      this.initData()
+      this.$emit('update:visible',false)
+    },
+    closeHandler(){
+      this.initData()
+    },
+    openHandler() {
+      if (this.events) this.rewriteData.events = this.events
+      this.hasOpen = true
+    },
+    handleRewriteReviewerSelector(val){
+      this.rewriteData.reviewerName = ''
+      this.rewriteData.employeeSelected = { dept: [], employee: [] }
+      this.rewriteData.reviewerId = 0
+      if (val.employee.length > 0){
+        this.rewriteData.reviewerName = val.employee[0].name
+        this.rewriteData.employeeSelected.employee = [{name:val.employee[0].name,id:val.employee[0].id,img_url:val.employee[0].img_url}]
+        this.rewriteData.reviewerId = val.employee[0].id
+      }
+    },
+    openEventSelected(){
+      this.eventSelected = !this.rewriteData.events ? [] : this.rewriteData.events.map(event => {
+        return {
+          id: event.id,
+          remark: event.remark,
+          appeal_remark:event.appeal_remark ? event.appeal_remark : ''
+        }
+      })
+      this.showEventSelector = true
+    },
+    onEventSelected(val){
+      this.rewriteData.events = val;
+    },
+    createAppeal(){
+      let self = this
+      if (!self.canSubmit) return
+      self.submitting = true
+      let params = {
+        reviewer_id:self.rewriteData.reviewerId,
+        remark:self.rewriteData.globalRemark,
+        events:self.rewriteData.events.map(item => {
+          return {id:item.id,remark:item.appeal_remark}
+        })
+      }
+      if (!params.reviewer_id || params.events.length <= 0) this.$message.error('非法操作')
+
+      self.$axiosUser('post','api/pro/integral/appeal/create',params)
+        .then(res => {
+          if (res.data.code === 1){
+            self.$message.success('复议已发起')
+            self.$emit('createFinish',res.data.data)
+            self.close()
+          }else {
+            self.$message.error(res.data.msg)
+          }
+        })
+        .finally(() => {
+          self.submitting = false
+        })
+    },
+    close(){
+      this.$emit('update:visible',false)
+    },
+    deleteEvent(event){
+      let index = this.rewriteData.events.indexOf(event)
+      if (index >= 0) this.rewriteData.events.splice(index,1)
+    }
+  },
+  mounted() {
+    this.superiorList = this.$store.getters.user_info.employee_detail.superior_list
+  }
+}
+</script>
+<style scoped lang="scss">
+
+</style>

+ 250 - 0
src/components/AppealRewrite.vue

@@ -0,0 +1,250 @@
+<template>
+  <el-dialog
+    :title="title"
+    :visible.sync="visible"
+    :modal="false"
+    :close-on-click-modal="false"
+    :close-on-press-escape="false"
+    :center="true"
+    :show-close="false"
+    :before-close="beforeCloseHandler"
+    @open="openHandler"
+    @close="closeHandler"
+    append-to-body
+    width="1000px"
+    top="5%"
+  >
+
+    <template>
+      <el-form v-model="rewriteData" v-loading="loading">
+        <el-form-item label="审批人" required>
+          <el-input class="w250" autocomplete="off" v-model="rewriteData.reviewerName" placeholder="请选择审批人" :disabled="!canRewrite" />
+          <div v-if="canRewrite" @click="showReviewerSelector = true" style="position: absolute; top: 0; right: 0; left: 0; bottom: 0; z-index: 9;"></div>
+          <EmployeeSelector
+            :multi="false"
+            :user_employee_list="true"
+            :isChecKedAll="false"
+            :is_filtration_creator="false"
+            :employee_list="superiorList"
+            :selected="rewriteData.employeeSelected"
+            :visible.sync="showReviewerSelector"
+            @confirm="handleRewriteReviewerSelector"
+          />
+        </el-form-item>
+        <el-form-item label="原因说明">
+          <el-input type="textarea" :rows="2" placeholder="填写原因说明" v-model="rewriteData.globalRemark" maxlength="100" show-word-limit :disabled="!canRewrite" />
+        </el-form-item>
+        <el-form-item label="积分事件" required>
+        </el-form-item>
+        <el-card>
+          <div slot="header">
+            <span>共{{rewriteData.events.length}}条积分事件</span>
+            <el-button @click="openEventSelected" type="primary" size="mini" :disabled="!canRewrite" >选择积分</el-button>
+          </div>
+          <IntegralEventSelector
+            :visible.sync="showEventSelector"
+            :title="userInfo.name + '的积分事件'"
+            :eid="userInfo.id"
+            :selected="eventSelected"
+            :max="100"
+            @confirm="onEventSelected"
+          />
+          <el-scrollbar
+            :native="false"
+            style="height: 300px"
+          >
+            <el-form-item v-for="(item,index) in rewriteData.events" :key="index">
+              <el-tag style="margin-right: 10px">{{item.remark}}</el-tag><i class="el-icon-delete pointer" @click="deleteEvent(item)" v-if="canRewrite" />
+              <el-input type="textarea" :rows="2" v-model="item.appeal_remark" placeholder="申诉原因" maxlength="100" show-word-limit :disabled="!canRewrite" />
+            </el-form-item>
+          </el-scrollbar>
+        </el-card>
+      </el-form>
+    </template>
+
+    <template slot="footer">
+      <div class="flex-box-end flex-v-ce">
+        <el-button @click="close" :loading="submitting || !hasOpen">关闭</el-button>
+        <el-button type="primary" @click="rewriteAppeal" :loading="submitting || !hasOpen" :disabled="!canSubmit">确认</el-button>
+      </div>
+    </template>
+
+  </el-dialog>
+</template>
+
+<script>
+import IntegralEventSelector from './IntegralEventSelector.vue'
+import Template from "../examine/components/Template.vue";
+import EmployeeSelector from "./EmployeeSelector.vue";
+export default {
+  name: 'AppealRewrite',
+  components:{EmployeeSelector, Template, IntegralEventSelector},
+  props:{
+    id:{
+      type: Number,
+      default:0
+    },
+    visible:{
+      type: Boolean,
+      default: false
+    },
+    title:{
+      type: String,
+      default: '复议记录'
+    }
+  },
+  data(){
+    const pts =  this.$getTyps()
+    return {
+      userInfo: this.$userInfo(),
+      appealInfo:null,
+      pts:pts,
+      rewriteData:{
+        events:[],
+        globalRemark:'',
+        reviewerId:'',
+        reviewerName:'',
+        employeeSelected: { dept: [], employee: [] },
+      },
+      showReviewerSelector:false,
+      showEventSelector:false,
+      superiorList:[],
+      loading:false,
+      eventSelected:[],
+      submitting:false,
+      hasOpen:false,
+    }
+  },
+  computed:{
+    canRewrite(){
+      return this.appealInfo && this.appealInfo.can_rewrite
+    },
+    canSubmit(){
+      return this.canRewrite && this.rewriteData.events.length > 0 && this.rewriteData.reviewerId
+    }
+  },
+  methods:{
+    initData(){
+      this.appealInfo = null
+      this.rewriteData = {
+        events:[],
+        globalRemark:'',
+        reviewerId:'',
+        reviewerName:'',
+        employeeSelected: { dept: [], employee: [] },
+      }
+      this.showReviewerSelector = false
+      this.loading = false
+      this.submitting = false
+      this.hasOpen = false
+    },
+    getAppealInfo(){
+      if (!this.id) return
+      let self = this
+      self.loading = true
+      self.$axiosUser('get','api/pro/integral/appeal/info',{appeal_id:this.id})
+        .then((res) => {
+          if (res.data.code === 1){
+            self.appealInfo = res.data.data
+            self.initRewriteData()
+          }
+        })
+        .finally(() => {
+          self.loading = false
+        })
+    },
+    initRewriteData(){
+      if (!this.appealInfo) return
+      this.rewriteData.events = this.appealInfo.events.map(event => {
+        let pt = this.pts.find(item => item.id === event.pt_id)
+        pt = pt ? pt.name : ''
+        return {
+          id:event.id,
+          remark:`${event.point} ${pt} ${event.event_time} ${event.event_remark}`,
+          appeal_remark:event.appeal_remark
+        }
+      })
+      this.rewriteData.globalRemark = this.appealInfo.global_remark
+      let node = this.appealInfo.process.find(item => item.step === 1)
+      if (node){
+        this.rewriteData.reviewerId = node.reviewer_id
+        this.rewriteData.reviewerName = node.reviewer_name
+      }
+    },
+    beforeCloseHandler(){
+      this.initData()
+      this.$emit('update:visible',false)
+    },
+    closeHandler(){
+      this.initData()
+    },
+    openHandler() {
+      this.hasOpen = true
+      this.getAppealInfo()
+    },
+    handleRewriteReviewerSelector(val){
+      this.rewriteData.reviewerName = ''
+      this.rewriteData.employeeSelected = { dept: [], employee: [] }
+      this.rewriteData.reviewerId = 0
+      if (val.employee.length > 0){
+        this.rewriteData.reviewerName = val.employee[0].name
+        this.rewriteData.employeeSelected.employee = [{name:val.employee[0].name,id:val.employee[0].id,img_url:val.employee[0].img_url}]
+        this.rewriteData.reviewerId = val.employee[0].id
+      }
+    },
+    openEventSelected(){
+      this.eventSelected = !this.rewriteData.events ? [] : this.rewriteData.events.map(event => {
+        return {
+          id: event.id,
+          remark: event.remark,
+          appeal_remark:event.appeal_remark ? event.appeal_remark : ''
+        }
+      })
+      this.showEventSelector = true
+    },
+    onEventSelected(val){
+      this.rewriteData.events = val;
+    },
+    rewriteAppeal(){
+      let self = this
+      if (!self.canSubmit) return
+      self.submitting = true
+      let params = {
+        appeal_id:self.appealInfo.id,
+        reviewer_id:self.rewriteData.reviewerId,
+        remark:self.rewriteData.globalRemark,
+        events:self.rewriteData.events.map(item => {
+          return {id:item.id,remark:item.appeal_remark}
+        })
+      }
+
+      self.$axiosUser('post','api/pro/integral/appeal/rewrite',params)
+        .then(res => {
+          if (res.data.code === 1){
+            self.$message.success(res.data.msg)
+            self.$emit('rewriteFinish')
+            self.close()
+          }else {
+            self.$message.error(res.data.msg)
+          }
+        })
+        .finally(() => {
+          self.submitting = false
+        })
+    },
+    close(){
+      this.$emit('update:visible',false)
+    },
+    deleteEvent(event){
+      let index = this.rewriteData.events.indexOf(event)
+      if (index >= 0) this.rewriteData.events.splice(index,1)
+    }
+  },
+  mounted() {
+    this.superiorList = this.$store.getters.user_info.employee_detail.superior_list
+  }
+}
+</script>
+<style scoped lang="scss">
+
+</style>

+ 362 - 0
src/components/IntegralEventSelector.vue

@@ -0,0 +1,362 @@
+<template>
+  <el-dialog
+    :title="title"
+    :visible.sync="visible_"
+    :close-on-click-modal="false"
+    :before-close="beforeClose"
+    @open="openHandler"
+    append-to-body
+    width="1000px"
+    top="5%"
+  >
+    <el-container>
+      <el-header height="100px">
+        <el-form :inline="true">
+          <el-form-item label="积分类型">
+            <el-select v-model="searchForm.ptId" placeholder="全部">
+              <el-option v-for="item in pts" :key="item.id" :label="item.name" :value="item.id"/>
+            </el-select>
+          </el-form-item>
+          <el-form-item label="时间区间">
+            <el-date-picker
+              v-model="timeScope"
+              type="daterange"
+              align="right"
+              unlink-panels
+              range-separator="至"
+              start-placeholder="开始日期"
+              end-placeholder="结束日期"
+              value-format="yyyy-MM-dd"
+              editable
+              :clearable="false"
+              :picker-options="{onPick:getList}"
+            />
+          </el-form-item>
+          <br/>
+          <el-form-item label="关键字">
+            <el-input v-model="searchForm.keyword" placeholder="输入关键字查找" maxlength="100" clearable/>
+          </el-form-item>
+        </el-form>
+      </el-header>
+      <el-main class="employee_selector_box">
+        <el-row :gutter="10">
+          <el-col :xs="11" :sm="11" :md="11" :lg="11" :xl="11" class="scroller-box">
+            <el-scrollbar
+              :native="false"
+              style="height: 100%;padding-top: 10px;"
+              v-loading="loading"
+            >
+              <div v-for="(item,index) in list" :key="index" @click.prevent.stop="selectEvent(item)" >
+                <el-row class="select_item" :gutter="10" >
+                  <el-col :span="2"><el-checkbox v-model="item.checked"/></el-col>
+                  <el-col :span="20" class="event_remark">{{item.remark}}</el-col>
+                </el-row>
+              </div>
+            </el-scrollbar>
+          </el-col>
+          <el-col :xs="2" :sm="2" :md="2" :lg="2" :xl="2" class="col-line"><span style="opacity: 0;">空</span></el-col>
+          <el-col :xs="11" :sm="11" :md="11" :lg="11" :xl="11" class="scroller-box">
+            <el-scrollbar
+              :native="false"
+              style="height: 100%;"
+            >
+              <el-row class="select_item" :gutter="10" v-for="(item,index) in eventSelected" :key="index">
+                <el-col :span="20" class="selected_remark">
+                  <el-tooltip placement="top" :content="item.remark" :disabled="item.remark.length <= 20">
+                    <span>{{item.remark}}</span>
+                  </el-tooltip>
+                </el-col>
+                <el-col :span="2" class="event_delete"><el-button type="default" size="mini" @click="cancelEvent(item.id)" icon="el-icon-close" circle/></el-col>
+              </el-row>
+            </el-scrollbar>
+          </el-col>
+        </el-row>
+      </el-main>
+      <el-footer height="100px">
+        <center style="padding: 20px 0;">
+          <el-pagination
+            background
+            @size-change="handleSizeChange"
+            @current-change="handlePageChange"
+            :current-page="searchForm.page"
+            layout="total, sizes, prev, pager, next"
+            :page-size="searchForm.page_size"
+            :page-sizes="[10,20,50,100]"
+            :total="searchForm.total"
+            :hide-on-single-page="true"
+            :key="pageKey"
+          />
+        </center>
+        <el-row :gutter="30" type="flex" align="middle" justify="end">
+          <el-col :span="2" style="text-align: center"><el-button @click="close">取消</el-button></el-col>
+          <el-col :span="2" style="text-align: center"><el-button type="primary" :disabled="!allowConfirm" @click="onConfirm">确定</el-button></el-col>
+        </el-row>
+      </el-footer>
+    </el-container>
+
+  </el-dialog>
+</template>
+
+
+<script>
+import Template from "../examine/components/Template.vue";
+
+export default {
+  name:'integralEventSelector',
+  components: {Template},
+  props:{
+    visible:{
+      type: Boolean,
+      default:false
+    },
+    eid:{
+      type: Number,
+      default:0
+    },
+    selected:{
+      type: Array,
+      default:()=>[]
+    },
+    title:{
+      type: String,
+      default: '积分选择'
+    },
+    max:{
+      type: Number,
+      default: 100
+    }
+  },
+  data(){
+    const today = new Date()
+    const startDate = this.$moment(today).subtract(8, 'months')
+    const endDate = this.$moment(today)
+
+    const sd = new Date(startDate.format('YYYY-MM-DD'))
+    const ed = new Date(endDate.format('YYYY-MM-DD'))
+
+    const pts =  this.$getTyps()
+    pts.push({id:0,name:'全部',code:'ALL'})
+    const ptb = pts.find(item => item.code === 'BF')
+    return {
+      list:[],
+      eventSelected:[],
+      pts:pts,
+      visible_:false,
+      startDate:startDate.format('YYYY-MM-DD'),
+      endDate:endDate.format('YYYY-MM-DD'),
+      timeScope:[
+        sd,
+        ed
+      ],
+      searchForm:{
+        startDate:startDate.format('YYYY-MM-DD'),
+        endDate:endDate.format('YYYY-MM-DD'),
+        ptId:ptb ? ptb.id : 0,
+        keyword:'',
+        page:1,
+        page_size:20,
+        total:0
+      },
+      pageKey:0,
+      loading:false,
+    }
+  },
+  computed:{
+    allowConfirm(){
+      // 积分数量是否可以提交
+      return this.eventSelected.length <= this.max
+    },
+    allowSelect(){
+      // 积分数量是否还可以添加
+      return this.eventSelected.length < this.max
+    }
+  },
+  watch:{
+    visible(val){
+      this.visible_ = val
+    },
+    timeScope(val){
+      if (typeof val[0] === 'string'){
+        this.searchForm.startDate = val[0]
+        this.searchForm.endDate = val[1]
+      }else {
+        this.searchForm.startDate = this.$moment(val[0]).format('YYYY-MM-DD')
+        this.searchForm.endDate = this.$moment(val[1]).format('YYYY-MM-DD')
+      }
+    },
+    'searchForm.ptId'(val){
+      this.getList()
+    },
+    'searchForm.keyword'(val){
+      this.getList()
+    }
+  },
+  methods:{
+    initData(){
+      const ptb = this.pts.find(item => item.code === 'BF')
+      this.list = []
+      this.searchForm.startDate = this.startDate
+      this.searchForm.endDate = this.endDate
+      this.searchForm.ptId = ptb ? ptb.id : 0
+      this.searchForm.page = 1
+      this.searchForm.page_size = 20
+      this.searchForm.total = 0
+      this.searchForm.keyword = ''
+      this.eventSelected = []
+      this.pageKey = 1
+      this.timeScope = [
+        new Date(this.startDate),
+        new Date(this.endDate)
+      ]
+    },
+    beforeClose(done){
+      this.initData()
+      this.$emit('update:visible',false)
+      done()
+    },
+    openHandler(){
+      this.eventSelected = this.selected
+      this.getList()
+    },
+    getList(){
+      if (!this.searchForm.startDate || !this.searchForm.endDate) return
+      let self = this
+      self.loading = true
+      let params = {
+        start_day:this.searchForm.startDate,
+        end_day:this.searchForm.endDate,
+        pt_id:this.searchForm.ptId,
+        simple:true,
+        page:this.searchForm.page,
+        page_size:this.searchForm.page_size,
+        keyword:this.searchForm.keyword
+      }
+      if (this.eid) params.employee_ids = this.eid
+      self.$axiosUser('get','api/pro/integral/statistics/integral',params,'v3')
+        .then(res => {
+          if (res.data.code === 1){
+            self.list = res.data.data.list.map(item => {
+              let pt = self.pts.find(i => i.id === item.pt_id)
+              pt = !pt ? '' : (pt.code === 'BF' ? 'B分' : (pt.code === 'AF' ? 'A分' : ''))
+
+              let obj = {
+                id:item.id,
+                remark:`${item.point} ${pt} ${item.date} ${item.remark}`,
+                checked:false
+              }
+
+              let checked = self.selected.find(i => i.id === item.id)
+              if (checked) obj.checked = true
+
+              return obj
+            })
+            self.searchForm.total = res.data.data.total
+          }else {
+            self.$message.error(res.data.data.msg)
+          }
+        })
+        .finally(() => {
+          self.loading = false
+        })
+    },
+    selectEvent(item){
+      if (item.checked){
+        //取消选择
+        this.cancelEvent(item.id)
+      }else {
+        //选择
+        if (!this.allowSelect){
+          this.$message.warning('最多选择'+this.max+'条积分')
+          return
+        }
+
+        item.checked = true
+        this.eventSelected.push({id:item.id,remark:item.remark,appeal_remark:''})
+      }
+    },
+    cancelEvent(eventId){
+      let selectedEvent = this.eventSelected.find(item => item.id === eventId)
+      if (!selectedEvent) return
+      this.eventSelected.splice(this.eventSelected.indexOf(selectedEvent),1);
+
+      let event = this.list.find(i => i.id === eventId)
+      if (event) event.checked = false
+    },
+    handleSizeChange(val){
+      this.searchForm.page = 1
+      this.searchForm.page_size = val
+      this.getList()
+    },
+    handlePageChange(val){
+      this.searchForm.page = val
+      this.pageKey = val
+      this.getList()
+    },
+    onConfirm(){
+      if (!this.allowConfirm){
+        this.$message.warning('最多选择'+this.max+'条积分')
+      }
+      this.$emit('confirm',this.eventSelected)
+      this.close()
+    },
+    close(){
+      this.initData()
+      this.$emit('update:visible',false)
+    }
+  }
+}
+</script>
+
+<style scoped lang="scss">
+.employee_selector_box {
+  width: 100%;
+  box-sizing: border-box;
+  margin: 0 auto;
+}
+.scroller-box {
+  height: 440px;
+  padding-right: 0px !important;
+  background-color: #fdfdfd;
+  border: 1px solid #eee;
+  border-radius: 4px;
+  overflow: hidden;
+}
+
+.select_item{
+  margin-bottom: 10px;
+}
+
+.select_item:hover{
+  border-radius: 4px;
+  background-color: #ecf5ff;
+  cursor: pointer;
+}
+
+.event_remark{
+  white-space: nowrap;
+  color: #606266;
+}
+
+.selected_remark{
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  color: #606266;
+}
+
+.event_delete{
+  display: table-cell;
+  text-align: right;
+  padding-right: 0px;
+}
+
+.event_delete .el-button /deep/.el-icon-close{
+  color: #ffffff;
+}
+
+.event_delete .el-button{
+  background: #c0c4cc;
+  transform: scale(0.5);
+}
+
+</style>

+ 699 - 0
src/point/views/common/AppealPopup.vue

@@ -0,0 +1,699 @@
+<template>
+  <div v-loading="loading">
+    <el-container v-if="appealInfo">
+      <el-main class="detail_popup">
+        <!--     基础信息     -->
+        <el-card shadow="always" class="appeal_content">
+          <template slot="header">
+            <userImage
+              :user_name="appealInfo.employee_name"
+              :img_url="appealInfo.employee_img_url"
+              class="fl"
+              width="50px"
+              height="50px"
+              fontSize="15"
+            />
+            <span style="line-height: 50px; margin-left:10px;margin-right:4px;">{{appealInfo.employee_name}}</span>
+            <el-tag :type="appealStatusType(appealInfo.status)">{{appealStatusMap[appealInfo.status] || '--'}}</el-tag>
+          </template>
+          <div>
+            <p><el-tag>{{appealInfo.create_time}}</el-tag>&nbsp;发起复议申请</p>
+            <p>共&nbsp;<el-tag>{{appealInfo.events.length}}</el-tag>&nbsp;条积分事件</p>
+            <p v-if="appealInfo.global_remark">申请原因&nbsp;:&nbsp;<el-tag>{{appealInfo.global_remark}}</el-tag></p>
+            <template v-if="appealInfo.complete_time">
+              <p><el-tag>{{appealInfo.complete_time}}</el-tag>&nbsp;审批结束</p>
+            </template>
+          </div>
+        </el-card>
+        <!--     审批流程     -->
+        <div class="process">
+          <h5>审批流程</h5>
+          <el-steps direction="vertical" :space="50" >
+            <el-step v-for="(item, index) in appealInfo.process" :key="index" style="margin-bottom: 5px;">
+              <template slot="icon">
+                <userImage width="36px" height="36px" :img_url="item.reviewer_img_url" :user_name="item.reviewer_name"></userImage>
+              </template>
+              <template slot="title">
+                阶段{{item.step}}
+                <el-tag :type="processStatusType(item.status)">{{processStatusMap[item.status] || '--'}}</el-tag>
+              </template>
+              <template slot="description">
+                <el-descriptions :column="1" size="small" :label-style="{width:'80px'}" border>
+                  <el-descriptions-item label="发起人">{{item.publisher_name}}</el-descriptions-item>
+                  <el-descriptions-item label="发起时间">{{item.create_time}}</el-descriptions-item>
+                  <el-descriptions-item label="审批人">{{item.reviewer_name}}</el-descriptions-item>
+                  <el-descriptions-item label="审批意见" v-if="item.remark" >{{item.remark}}</el-descriptions-item>
+                  <el-descriptions-item label="完结时间" v-if="item.complete_time" >{{item.complete_time}}</el-descriptions-item>
+                </el-descriptions>
+              </template>
+            </el-step>
+          </el-steps>
+        </div>
+        <!--     隐藏信息     -->
+        <el-collapse v-model="infoActiveNames">
+          <el-collapse-item :title="appealInfo.events.length + '条积分'" name="events" >
+            <el-descriptions class="detail_row" v-for="(item,index) in appealInfo.events" size="small" :key="index" :column="1" :label-style="{width : '80px'}"  border>
+              <el-descriptions-item label="积分">
+                {{item.point + ' ' + ptName(item.pt_id)}}
+                <el-tag :type="pointStatusType(item.status)" v-if="pointStatusMap[item.status]">{{pointStatusMap[item.status]}}</el-tag>
+              </el-descriptions-item>
+              <el-descriptions-item label="积分备注">{{item.event_remark}}</el-descriptions-item>
+              <el-descriptions-item label="时间">{{item.event_time}}</el-descriptions-item>
+              <el-descriptions-item v-if="item.appeal_remark" label="复议原因">{{item.appeal_remark}}</el-descriptions-item>
+              <el-descriptions-item v-if="item.delete_time" label="已删除">{{item.delete_time}}</el-descriptions-item>
+            </el-descriptions>
+          </el-collapse-item>
+          <el-collapse-item :title="appealInfo.logs.length + '条操作日志'" name="logs">
+            <el-row class="detail_row" v-for="(item,index) in appealInfo.logs" :key="index" >
+              <el-col>
+                <el-tag v-if="item.create_time">{{item.create_time}}</el-tag>
+                {{item.msg}}
+              </el-col>
+            </el-row>
+          </el-collapse-item>
+        </el-collapse>
+      </el-main>
+      <el-footer class="appeal_footer flex-box-end flex-v-ce" height="60" >
+        <el-popconfirm
+          v-if="appealInfo.can_delete"
+          title="删除复议后将不可恢复,确认提交吗"
+          confirm-button-text="提交"
+          cancel-button-text="取消"
+          icon="el-icon-question"
+          icon-color="red"
+          @confirm="removeAppeal"
+          style="margin: 0 10px"
+        >
+          <el-button type="danger" size="mini" slot="reference" :loading="submitting || !hasOpen" >删除复议</el-button>
+        </el-popconfirm>
+
+
+
+        <el-button type="danger" size="mini" v-if="appealInfo.can_refuse" @click="showReviewRefuse = true" :loading="submitting || !hasOpen">拒绝</el-button>
+        <el-button type="warning" size="mini" v-if="appealInfo.can_reject_rewrite" @click="showReviewRejectRewrite = true" :loading="submitting || !hasOpen">驳回重填</el-button>
+
+        <el-popconfirm
+          v-if="appealInfo.can_cancel"
+          title="审批撤回后可以重新编辑内容重新提交"
+          confirm-button-text="提交"
+          cancel-button-text="取消"
+          icon="el-icon-question"
+          icon-color="red"
+          @confirm="reviewCancel"
+          style="margin: 0 10px"
+        >
+          <el-button type="info" size="mini" slot="reference" :loading="submitting || !hasOpen">撤回审批</el-button>
+        </el-popconfirm>
+
+        <el-popconfirm
+          v-if="appealInfo.can_cancel_appeal"
+          title="复议撤回后可以重新编辑内容重新提交"
+          confirm-button-text="提交"
+          cancel-button-text="取消"
+          icon="el-icon-question"
+          icon-color="red"
+          @confirm="cancelAppeal"
+          style="margin: 0 10px"
+        >
+          <el-button type="info" size="mini" slot="reference" :loading="submitting || !hasOpen" >撤回复议</el-button>
+        </el-popconfirm>
+
+        <el-button type="success" size="mini" v-if="appealInfo.can_rewrite" :loading="submitting || !hasOpen" @click="openRewrite">重新填写</el-button>
+        <el-button type="success" size="mini" v-if="appealInfo.can_submit" @click="showReviewSubmit = true" :loading="submitting || !hasOpen">递交审批</el-button>
+
+        <el-button type="primary" size="mini" v-if="appealInfo.can_approval" @click="showReviewApproval = true" :loading="submitting || !hasOpen">通过</el-button>
+      </el-footer>
+    </el-container>
+    <noData class="center" v-else />
+
+<!--  审批拒绝  -->
+    <el-dialog
+      title="拒绝审批"
+      :visible.sync="showReviewRefuse"
+      :modal="false"
+      :close-on-click-modal="false"
+      :close-on-press-escape="false"
+      :center="true"
+      :show-close="false"
+      width="600px"
+    >
+      <template>
+        <el-form v-model="formData">
+          <el-form-item >
+            <el-input
+              type="textarea"
+              :rows="3"
+              :autosize="true"
+              :clearable="true"
+              :maxlength="100"
+              :show-word-limit="true"
+              placeholder="原因说明"
+              v-model="formData.remark"
+            />
+          </el-form-item>
+        </el-form>
+      </template>
+      <template slot="footer">
+        <div class="flex-box-end flex-v-ce">
+          <el-button type="danger" @click="formData.remark = '',showReviewRefuse = false">关闭</el-button>
+          <el-button type="primary" @click="reviewRefuse" :disabled="!canConfirmReviewRefuse" :loading="submitting || !showReviewRefuse || !hasOpen" >确认</el-button>
+        </div>
+      </template>
+    </el-dialog>
+
+<!--  驳回重填  -->
+    <el-dialog
+      title="驳回重填"
+      :visible.sync="showReviewRejectRewrite"
+      :modal="false"
+      :close-on-click-modal="false"
+      :close-on-press-escape="false"
+      :center="true"
+      :show-close="false"
+      width="600px"
+    >
+      <template>
+        <el-form v-model="formData">
+          <el-form-item >
+            <el-input
+              type="textarea"
+              :rows="3"
+              :autosize="true"
+              :clearable="true"
+              :maxlength="100"
+              :show-word-limit="true"
+              placeholder="原因说明"
+              v-model="formData.remark"
+            />
+          </el-form-item>
+        </el-form>
+      </template>
+      <template slot="footer">
+        <div class="flex-box-end flex-v-ce">
+          <el-button type="danger" @click="formData.remark = '',showReviewRejectRewrite = false">关闭</el-button>
+          <el-button type="primary" @click="reviewRejectRewrite" :disabled="!canConfirmReviewRejectRewrite" :loading="submitting || !showReviewRejectRewrite || !hasOpen">确认</el-button>
+        </div>
+      </template>
+    </el-dialog>
+
+<!--  重新填写  -->
+    <AppealRewrite
+      :visible.sync="showRewrite"
+      :id="id"
+      @rewriteFinish="onAppRewriteFinish"
+    />
+
+<!--  递交审批  -->
+    <el-dialog
+      title="递交审批"
+      width="600px"
+      :visible.sync="showReviewSubmit"
+      :modal="false"
+      :close-on-click-modal="false"
+      :close-on-press-escape="false"
+      :center="true"
+      :show-close="false"
+    >
+      <template>
+        <el-form v-model="formData">
+          <el-form-item>
+            <el-input class="w250" auto-complete="off" v-model="formData.reviewerName" placeholder="请选择审批人" />
+            <div @click="showReviewerSelector = true" style=" position: absolute; top: 0; right: 0; left: 0; bottom: 0; z-index: 9;"></div>
+            <EmployeeSelector
+              :multi="false"
+              :user_employee_list="true"
+              :isChecKedAll="false"
+              :is_filtration_creator="false"
+              :employee_list="superiorList"
+              :selected="formData.employeeSelected"
+              :visible.sync="showReviewerSelector"
+              @confirm="handleReviewerSelector"
+            />
+          </el-form-item>
+          <el-form-item>
+            <el-input
+              type="textarea"
+              :autosize="true"
+              :clearable="true"
+              :maxlength="100"
+              :show-word-limit="true"
+              placeholder="请填写原因说明"
+              v-model="formData.remark"
+            />
+          </el-form-item>
+        </el-form>
+      </template>
+      <template slot="footer">
+        <div class="flex-box-end flex-v-ce">
+          <el-button @click="formData.remark = '',formData.reviewerId = 0,formData.reviewerName = '',formData.employeeSelected = { dept: [], employee: [] },showReviewSubmit = false">关闭</el-button>
+          <el-button type="primary" @click="reviewSubmit" :disabled="!canConfirmReviewSubmit" :loading="submitting || !showReviewSubmit || !hasOpen" >确认</el-button>
+        </div>
+      </template>
+    </el-dialog>
+
+<!--  通过  -->
+    <el-dialog
+      title="通过"
+      :visible.sync="showReviewApproval"
+      :modal="false"
+      :close-on-click-modal="false"
+      :close-on-press-escape="false"
+      :center="true"
+      :show-close="false"
+      width="600px"
+    >
+      <template>
+        <el-form v-model="formData">
+          <el-form-item >
+            <el-input
+              type="textarea"
+              :rows="3"
+              :autosize="true"
+              :clearable="true"
+              :maxlength="100"
+              :show-word-limit="true"
+              placeholder="原因说明"
+              v-model="formData.remark"
+            />
+          </el-form-item>
+        </el-form>
+      </template>
+      <template slot="footer">
+        <div class="flex-box-end flex-v-ce">
+          <el-button type="danger" @click="formData.remark = '',showReviewApproval = false">关闭</el-button>
+          <el-button type="primary" @click="reviewApproval" :disabled="!canConfirmReviewApproval" :loading="submitting || !showReviewApproval || !hasOpen">确认</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+
+import Template from "../../../examine/components/Template.vue";
+import EmployeeSelector from "../../../components/EmployeeSelector.vue";
+import AppealRewrite from "../../../components/AppealRewrite.vue";
+
+export default {
+  name:'appealPopup',
+  components: {AppealRewrite, Template,EmployeeSelector},
+  props:{
+    id:{
+      type: Number,
+      default:0
+    }
+  },
+  data() {
+    return {
+      userInfo: this.$userInfo(),
+      hasOpen:false,
+      hasSubmit:false,    //标识复议记录是否有提交操作,用来通知父组件如果有需要的话
+      loading:false,
+      appealInfo:null,
+      superiorList:[],
+      infoActiveNames:[],
+      eventSelected:[],
+      appealStatusMap:{
+        0:'全部',
+        1:'审批中',
+        2:'审批通过',
+        3:'驳回重填',
+        4:'撤回重填',
+        5:'拒绝'
+      },
+      pointStatusMap:{
+        1:'正常',
+        2:'已删除',
+        3:'已通过申述并删除'
+      },
+      processStatusMap:{
+        1:'待处理',
+        2:'审批通过',
+        3:'递交审批',
+        4:'拒绝',
+        5:'驳回重填',
+        6:'撤回上个节点重填',
+        7:'复议撤回',
+      },
+      pts:[],
+      formData:{
+        reviewerId:0,
+        reviewerName:'',
+        remark:'',
+        employeeSelected: { dept: [], employee: [] },
+      },
+      rewriteData:{
+        events:[],
+        globalRemark:'',
+        reviewerId:'',
+        reviewerName:'',
+        employeeSelected: { dept: [], employee: [] },
+      },
+      showReviewRefuse:false,
+      showReviewRejectRewrite:false,
+      showReviewApproval:false,
+      showReviewerSelector:false,
+      showReviewSubmit:false,
+      submitting:false,
+      showEventSelector:false,
+      showRewrite:false,
+    }
+  },
+  watch:{
+  },
+  computed:{
+    canConfirmReviewSubmit(){
+      return this.appealInfo && this.appealInfo.can_submit && this.formData.reviewerId && this.showReviewSubmit
+    },
+    canConfirmReviewApproval(){
+      return this.appealInfo && this.appealInfo.can_approval && this.showReviewApproval
+    },
+    canConfirmReviewRejectRewrite(){
+      return this.appealInfo && this.appealInfo.can_reject_rewrite && this.showReviewRejectRewrite
+    },
+    canConfirmReviewRefuse(){
+      return this.appealInfo && this.appealInfo.can_refuse && this.showReviewRefuse
+    }
+  },
+  methods:{
+    getAppealInfo(){
+      if (!this.$props.id) return
+      let self = this
+      self.loading = true
+      self.$axiosUser('get','api/pro/integral/appeal/info',{appeal_id:this.$props.id})
+        .then((res) => {
+          if (res.data.code === 1){
+            self.appealInfo = res.data.data
+          }
+        })
+        .finally(() => {
+          self.loading = false
+        })
+    },
+    initData(){
+      this.formData.reviewerId = 0
+      this.formData.remark = ''
+      this.formData.reviewerName = ''
+      this.formData.employeeSelected = { dept: [], employee: [] }
+      this.hasSubmit = false
+      this.appealInfo = null
+      this.infoActiveNames = []
+      this.showReviewRefuse = false
+      this.showReviewRejectRewrite = false
+      this.showReviewApproval = false
+      this.showReviewerSelector = false
+      this.showReviewSubmit = false
+      this.showRewrite = false
+      this.showEventSelector = false
+      this.submitting = false
+    },
+    closedHandler(){
+      if(this.hasSubmit) this.$emit('update:appeal')
+      this.initData()
+    },
+    closeHandler(){
+      this.hasOpen = false
+    },
+    openedHandler(){
+      this.hasOpen = true
+      this.initData()
+      this.getAppealInfo()
+    },
+    ptName(ptId){
+      let item = this.pts.find(item => item.id === ptId)
+      return item ? item.name : ''
+    },
+    pointStatusType(status){
+      switch (status){
+        case 2:
+        case 3:
+          return 'warning'
+        default:
+          return "success"
+      }
+    },
+    appealStatusType(status){
+      const map = {
+        1:'info',
+        2:'success',
+        3:'warning',
+        4:'warning',
+        5:'danger'
+      }
+      return map[status] || 'info'
+    },
+    processStatusType(status){
+      const map = {
+        1:'info',
+        2:'success',
+        3:'success',
+        4:'danger',
+        5:'warning',
+        6:'warning',
+        7:'info',
+      }
+      return map[status] || 'info'
+    },
+    onAppRewriteFinish(){
+      this.hasSubmit = true
+      this.getAppealInfo()
+    },
+    removeAppeal(){
+      if (!this.appealInfo) return
+      let self = this
+      self.submitting = true
+      let params = {
+        appeal_id:self.appealInfo.id
+      }
+      self.$axiosUser('post','api/pro/integral/appeal/remove',params)
+        .then(res => {
+          if (res.data.code === 1){
+            self.hasSubmit = true
+            self.getAppealInfo()
+          }else {
+            self.$message.error(res.data.msg)
+          }
+        })
+        .finally(() => {
+          self.submitting = false
+        })
+
+    },
+    reviewRefuse(){
+      if (!this.appealInfo) return
+      let self = this
+      self.submitting = true
+      let params = {
+        appeal_id:self.appealInfo.id,
+        remark:self.formData.remark
+      }
+      self.$axiosUser('post','api/pro/integral/appeal/review/refuse',params)
+        .then(res => {
+          if (res.data.code === 1){
+            self.hasSubmit = true
+            self.getAppealInfo()
+            self.showReviewRefuse = false
+          }else {
+            self.$message.error(res.data.msg)
+          }
+        })
+        .finally(() => {
+          self.submitting = false
+        })
+    },
+    reviewRejectRewrite(){
+      if (!this.appealInfo) return
+      let self = this
+      self.submitting = true
+      let params = {
+        appeal_id:self.appealInfo.id,
+        remark:self.formData.remark
+      }
+      self.$axiosUser('post','api/pro/integral/appeal/review/reject',params)
+        .then(res => {
+          if (res.data.code === 1){
+            self.hasSubmit = true
+            self.getAppealInfo()
+            self.showReviewRejectRewrite = false
+          }else {
+            self.$message.error(res.data.msg)
+          }
+        })
+        .finally(() => {
+          self.submitting = false
+        })
+    },
+    reviewCancel(){
+      if (!this.appealInfo) return
+      this.submitting = true
+      let self = this
+      self.$axiosUser('post','api/pro/integral/appeal/review/cancel',{appeal_id:self.appealInfo.id})
+        .then(res => {
+          if (res.data.code === 1){
+            self.hasSubmit = true
+            self.getAppealInfo()
+          }else {
+            self.$message.error(res.data.msg)
+          }
+        })
+        .finally(() => {
+          self.submitting = false
+        })
+    },
+    cancelAppeal(){
+      if (!this.appealInfo) return
+      this.submitting = true
+      let self = this
+
+      self.$axiosUser('post','api/pro/integral/appeal/cancel',{appeal_id:self.appealInfo.id})
+        .then(res => {
+          if (res.data.code === 1){
+            self.hasSubmit = true
+            self.getAppealInfo()
+          }else {
+            this.$message.error(res.data.msg)
+          }
+        })
+        .finally(() => {
+          self.submitting = false
+        })
+    },
+    reviewSubmit(){
+      if (!this.appealInfo) return
+      let self = this
+      self.submitting = true
+      let params = {
+        appeal_id:self.appealInfo.id,
+        reviewer_id:self.formData.reviewerId,
+        remark:self.formData.remark
+      }
+      self.$axiosUser('post','api/pro/integral/appeal/review/submit',params)
+        .then(res => {
+          if (res.data.code === 1){
+            self.hasSubmit = true
+            self.getAppealInfo()
+            self.showReviewSubmit = false
+          }else {
+            self.$message.error(res.data.msg)
+          }
+        })
+        .finally(() => {
+          self.submitting = false
+        })
+    },
+    reviewApproval(){
+      if (!this.appealInfo) return
+      let self = this
+      self.submitting = true
+      let params = {
+        appeal_id:self.appealInfo.id,
+        remark:self.formData.remark
+      }
+      self.$axiosUser('post','api/pro/integral/appeal/review/approval',params)
+        .then(res => {
+          if (res.data.code === 1){
+            self.hasSubmit = true
+            self.getAppealInfo()
+            self.showReviewApproval = false
+          }else {
+            self.$message.error(res.data.msg)
+          }
+        })
+        .finally(() => {
+          self.submitting = false
+        })
+
+    },
+    handleReviewerSelector(val){
+      this.formData.reviewerName = ''
+      this.formData.employeeSelected = { dept: [], employee: [] }
+      this.formData.reviewerId = 0
+      if (val.employee.length > 0){
+        this.formData.reviewerName = val.employee[0].name
+        this.formData.employeeSelected.employee = [{name:val.employee[0].name,id:val.employee[0].id,img_url:val.employee[0].img_url}]
+        this.formData.reviewerId = val.employee[0].id
+      }
+    },
+    openRewrite(){
+      if (!this.appealInfo) return
+      this.rewriteData.events = this.appealInfo.events.map(event => {
+        let pt = this.pts.find(item => item.id === event.pt_id)
+        pt = pt ? pt.name : ''
+        return {
+          id:event.id,
+          remark:`${event.point} ${pt} ${event.event_remark}`,
+          appeal_remark:event.appeal_remark
+        }
+      })
+      this.rewriteData.globalRemark = this.appealInfo.global_remark
+      let node = this.appealInfo.process.find(item => item.step === 1)
+      if (node){
+        this.rewriteData.reviewerId = node.reviewer_id
+        this.rewriteData.reviewerName = node.reviewer_name
+      }
+      this.showRewrite = true
+    },
+  },
+  mounted() {
+    this.pts = this.$getTyps()
+    this.superiorList = this.$store.getters.user_info.employee_detail.superior_list
+  }
+}
+
+</script>
+
+<style scoped lang="scss">
+.detail_popup {
+  padding: 20px;
+  overflow-y: auto;
+  overflow-x: hidden !important;
+  height: calc(100vh - 60px);
+}
+
+.detail_row {
+  padding-bottom: 10px;
+  line-height: 50px;
+}
+
+.appeal_content {
+  font-size: 18px;
+}
+
+.appeal_content p{
+  margin-bottom: 10px;
+  font-size: 12px;
+  color: rgb(144, 147, 153);
+}
+
+.process{
+  position: relative;
+  margin: 0 0 20px 0;
+  padding-top: 12px;
+  font-size: 16px;
+  color: #303133;
+  line-height: 22px;
+}
+
+.process:before{
+  position: absolute;
+  top: 0;
+  content: ' ';
+  width: 100%;
+  border-top: 1px #f8f8f8 solid;
+}
+
+.appeal_footer{
+  border-top: 1px solid #ebebeb;
+  padding: 10px 20px;
+  font-size: 14px;
+}
+
+.appeal_action {
+  text-align: center;
+}
+
+.center{
+  margin-top: calc(100vh * 0.3);
+}
+
+</style>

+ 21 - 8
src/point/views/setting/set_basics.vue

@@ -122,6 +122,16 @@
               </template>
               <el-checkbox v-model="basics_info.specified_rule_item" >提交积分时,必须选择积分规则</el-checkbox>
             </el-form-item>
+            <el-form-item prop="appeal">
+              <template slot="label">
+                <span>积分复议</span>
+                <el-tooltip placement="top">
+                  <div slot="content">开启可对个人积分提起复议审批,通过后自动撤销积分事件</div>
+                  <span class="tips">?</span>
+                </el-tooltip>
+              </template>
+              <el-checkbox v-model="basics_info.appeal">开启</el-checkbox>
+            </el-form-item>
           </template>
           <el-form-item>
             <el-button type="primary" :loading="save_loading" @click="save('basics_info')">保存</el-button>
@@ -133,8 +143,9 @@
 </template>
 <script>
   import EmployeeSelector from '@/components/EmployeeSelector';
+  import Template from "../../../examine/components/Template.vue";
 export default {
-  components:{EmployeeSelector},
+  components:{Template, EmployeeSelector},
   data() {
     return {
       loading: false,
@@ -176,13 +187,14 @@ export default {
     get_basics() {
       this.loading = true;
       this.$axiosUser('get', '/api/pro/integral/site/config').then(res => {
-          if (res.data.code == 1) {
+          if (res.data.code === 1) {
             var resData = res.data.data;
-            resData.rule_limit_check = resData.rule_limit_check == 1 ? false : true;
-            resData.task_review = resData.task_review == 1 ? true : false;
-            resData.look_dept_ranking =resData.look_dept_ranking == 1 ? true : false;
-            resData.change_reviewer =resData.change_reviewer == 1 ? true : false;
-            resData.specified_rule_item =resData.specified_rule_item == 1 ? true : false;
+            resData.rule_limit_check = resData.rule_limit_check === 1;
+            resData.task_review = resData.task_review === 1;
+            resData.look_dept_ranking =resData.look_dept_ranking === 1;
+            resData.change_reviewer =resData.change_reviewer === 1;
+            resData.specified_rule_item =resData.specified_rule_item === 1;
+            resData.appeal = resData.appeal === 1;
             this.basics_info = resData;
             if(resData.not_in_rank){
               this.employee_selected.employee = resData.not_in_rank;
@@ -209,9 +221,10 @@ export default {
           this.basics_info.look_dept_ranking ? (data.look_dept_ranking = 1) : (data.look_dept_ranking = 0);
           this.basics_info.change_reviewer ? (data.change_reviewer = 1) : (data.change_reviewer = 0);
           this.basics_info.specified_rule_item ? (data.specified_rule_item = 1) : (data.specified_rule_item = 0);
+          this.basics_info.appeal ? (data.appeal = 1) : (data.appeal = 0);
           data.not_in_rank=JSON.stringify(this.employee_selected.employee);
           this.$axiosUser('post', '/api/pro/integral/site/config', data, 'v2').then(res => {
-              if (res.data.code == 1) {
+              if (res.data.code === 1) {
                 this.$message.success(res.data.msg);
               } else {
                 this.$message.error(res.data.msg);

+ 420 - 0
src/point/views/statistics/appeal.vue

@@ -0,0 +1,420 @@
+<template>
+  <div class="appeal_box boxMinHeight">
+    <el-row>
+      <el-button type="primary" :disabled="!appealEnable" @click="showAppealCreate = true">发起申诉</el-button>
+    </el-row>
+    <el-tabs v-model="currentTag">
+      <el-tab-pane
+        :name="tabs[2].name"
+        :disabled="loading"
+      >
+        <template slot="label">
+          <el-tooltip content="等待您处理的复议" placement="left">
+            <span>{{tabs[2].label}}</span>
+          </el-tooltip>
+        </template>
+      </el-tab-pane>
+      <el-tab-pane
+        :name="tabs[1].name"
+        :disabled="loading"
+      >
+        <template slot="label">
+          <el-tooltip content="自己发起的复议" placement="top">
+            <span>{{tabs[1].label}}</span>
+          </el-tooltip>
+        </template>
+      </el-tab-pane>
+      <el-tab-pane
+        :name="tabs[0].name"
+        :disabled="loading"
+      >
+        <template slot="label">
+          <el-tooltip content="参与审批的复议" placement="right">
+            <span>{{tabs[0].label}}</span>
+          </el-tooltip>
+        </template>
+      </el-tab-pane>
+
+
+    </el-tabs>
+
+    <el-container>
+      <el-header>
+        <el-form :inline="true">
+          <el-form-item label="状态" v-if="currentTag === 'publishList'">
+            <el-select v-model="searchForm.status">
+              <el-option v-for="item in appealStatus" :key="item.id" :label="item.value" :value="item.id" />
+            </el-select>
+          </el-form-item>
+        </el-form>
+      </el-header>
+      <el-main>
+        <el-table
+          :data="list"
+          v-loading="loading"
+          :row-class-name="statusClassName"
+          @row-click="openAppealInfo"
+        >
+          <el-table-column label="审批人">
+            <template slot-scope="scope">
+              <userImage
+                style="float: left"
+                :id="scope.row.last_reviewer_id"
+                :user_name="scope.row.reviewer_name"
+                :img_url="scope.row.reviewer_img_url"
+                width="40px"
+                height="40px"
+              />
+              <span style="margin-left: 10px; line-height: 50px; display: inline-block">{{scope.row.reviewer_name}}</span>
+            </template>
+          </el-table-column>
+          <el-table-column label="积分记录" prop="event_count"></el-table-column>
+          <el-table-column label="状态">
+            <template slot-scope="scope">
+              {{appealStatusMap[scope.row.status] || "--"}}
+            </template>
+          </el-table-column>
+          <el-table-column label="日期" prop="create_time"></el-table-column>
+          <template slot="empty">
+            <noData></noData>
+          </template>
+        </el-table>
+      </el-main>
+      <el-footer>
+        <center style="padding: 20px 0;">
+          <el-pagination
+            background
+            @size-change="handleSizeChange"
+            @current-change="handlePageChange"
+            :current-page="searchForm.page"
+            layout="total, sizes, prev, pager, next"
+            :page-size="searchForm.page_size"
+            :page-sizes="[10,20,50,100]"
+            :total="searchForm.total"
+            :hide-on-single-page="true"
+            :key="pageKey"
+          />
+        </center>
+      </el-footer>
+    </el-container>
+
+<!--  申诉详情  -->
+    <el-drawer
+      :with-header="false"
+      :visible.sync="showDetail"
+      size="500px"
+      direction="rtl"
+      :show-close="false"
+      :wrapper-closable="true"
+      @close="handleAppealClose"
+      @closed="handleAppealClosed"
+      @opened="handleAppealOpen"
+    >
+      <AppealPopup ref="appealInfo" :id="appealId" @update:appeal="init"/>
+    </el-drawer>
+
+    <AppealCreate
+      :visible.sync="showAppealCreate"
+      @createFinish="onAppealCreateFinish"
+    />
+
+  </div>
+</template>
+
+<script>
+import Template from "../../../examine/components/Template.vue";
+import AppealPopup from "../common/AppealPopup.vue";
+import AppealCreate from "../../../components/AppealCreate.vue";
+
+export default {
+  name:'appeal',
+  components: {AppealCreate, Template,AppealPopup},
+  data(){
+    return {
+      userInfo: this.$userInfo(),
+      tabs:[
+        {label:'参与列表',name:'joinList'},
+        {label:'申诉列表',name:'publishList'},
+        {label:'待处理列表',name: 'waitingList'}
+      ],
+      currentTag:'publishList',
+      pageKey:0,
+      loading:false,
+      searchForm:{
+        status:0,
+        page:1,
+        page_size:10,
+        total:0
+      },
+      appealStatusMap:{
+        0:'全部',
+        1:'审批中',
+        2:'审批通过',
+        3:'驳回重填',
+        4:'撤回重填',
+        5:'拒绝'
+      },
+      appealStatus:[
+        {
+          id:0,
+          value:'全部'
+        },
+        {
+          id:1,
+          value:'审批中'
+        },
+        {
+          id:2,
+          value:'审批通过'
+        },
+        {
+          id:3,
+          value:'驳回重填'
+        },
+        {
+          id:4,
+          value:'撤回重填'
+        },
+        {
+          id:5,
+          value:'拒绝'
+        },
+      ],
+      appealEnable:false,
+      list:[],
+      showDetail:false,
+      pts:[],
+      pointStatusMap:{
+        1:'正常',
+        2:'已删除',
+        3:'已通过申述并删除'
+      },
+      infoActiveNames:[],
+      processStatusMap:{
+        1:'待处理',
+        2:'审批通过',
+        3:'递交审批',
+        4:'拒绝',
+        5:'驳回重填',
+        6:'撤回上个节点重填',
+        7:'复议撤回',
+      },
+      appealId:0,
+      showAppealCreate:false,
+    }
+  },
+  methods:{
+    statusClassName({row}){
+      switch (row.status){
+        case 2:
+          return "success"
+        case 3:
+        case 4:
+        case 5:
+          return "warning"
+        default:
+          return ""
+      }
+    },
+    getList(){
+      if (this.currentTag === 'publishList'){
+        this.getPublishList()
+      }else if (this.currentTag === 'joinList') {
+        this.getJoinList()
+      }else if (this.currentTag === 'waitingList'){
+        this.getListForWaiting()
+      }
+    },
+    getPublishList(){
+      let self = this
+      let params = {
+        status:self.searchForm.status,
+        page:self.searchForm.page,
+        page_size:self.searchForm.page_size
+      }
+      self.loading = true
+      self.list = []
+      self.searchForm.total = 0
+      self.$axiosUser('get','/api/pro/integral/appeal/list/publisher',params)
+        .then(res => {
+          if (res.data.code === 1){
+            self.list = res.data.data.list
+            self.searchForm.total = res.data.data.total
+          }
+        })
+        .finally(() => {
+          self.loading = false
+        })
+    },
+    getJoinList(){
+      let self = this
+      let params = {
+        page:self.searchForm.page,
+        page_size:self.searchForm.page_size
+      }
+      self.loading = true
+      self.list = []
+      self.searchForm.total = 0
+      self.$axiosUser('get','/api/pro/integral/appeal/list/reviewer',params)
+        .then((res) => {
+          if (res.data.code === 1){
+            self.list = res.data.data.list
+            self.total = res.data.data.total
+          }
+        })
+        .finally(() => {
+          self.loading= false
+        })
+    },
+    getListForWaiting(){
+      let self = this
+      let params = {
+        last_reviewer_id:self.userInfo.id,
+        page:self.searchForm.page,
+        page_size:self.searchForm.page_size
+      }
+      self.loading = true
+      self.list = []
+      self.searchForm.total = 0
+      self.$axiosUser('get','/api/pro/integral/appeal/list',params)
+        .then((res) => {
+          if (res.data.code === 1){
+            self.list = res.data.data.list
+            self.total = res.data.data.total
+          }
+        })
+        .finally(() => {
+          self.loading= false
+        })
+    },
+    getConfig(){
+      let self = this
+      self.$axiosUser('get','/api/pro/integral/site/config')
+        .then(res => {
+          if (res.data.code === 1){
+            self.appealEnable = res.data.data.appeal === 1
+          }
+        })
+    },
+    handleSizeChange(val){
+      this.searchForm.page = 1
+      this.searchForm.page_size = val
+      this.getList()
+    },
+    handlePageChange(val){
+      this.searchForm.page = val
+      this.pageKey = val          //预防实际页数变动但是分页组件中没变的问题
+      this.getList()
+    },
+    openAppealInfo(appeal){
+      this.appealId = appeal.id
+      this.showDetail = true
+    },
+    handleAppealClose(){
+      this.$refs['appealInfo'].closeHandler()
+    },
+    handleAppealClosed(){
+      this.$refs['appealInfo'].closedHandler()
+    },
+    handleAppealOpen(){
+      this.$refs['appealInfo'].openedHandler()
+    },
+    init(){
+      //el-pagination组件换页有问题,每次有数据更新需要重置为第一页暂时处理下
+      this.searchForm.page = 1
+      this.searchForm.page_size = 10
+      this.getList()
+    },
+    onAppealCreateFinish(){
+      this.getList()
+    }
+  },
+  watch:{
+    currentTag(){
+      this.searchForm.page = 1
+      this.searchForm.page_size = 10
+      this.getList()
+    },
+    'searchForm.status'(v,o){
+      this.getList()
+    },
+    'searchForm.page'(v){
+      this.pageKey = v
+    }
+  },
+  mounted() {
+    this.pts = this.$getTyps()
+    this.getConfig()
+    this.getList()
+  },
+}
+</script>
+
+<style scoped lang="scss">
+.appeal_box {
+  background-color: white;
+  padding: 20px;
+}
+
+/deep/ .el-table tr:hover{
+  cursor:pointer
+}
+
+/deep/ .el-table .success{
+  background: #f0f9eb;
+}
+
+/deep/ .el-table .warning{
+  background: oldlace;
+}
+
+//.detail_popup {
+//  padding: 20px;
+//  overflow-y: auto;
+//  overflow-x: hidden !important;
+//  height: calc(100vh - 60px);
+//}
+//
+//.detail_row {
+//  padding-bottom: 10px;
+//  line-height: 50px;
+//}
+//
+//.appeal_content {
+//  font-size: 18px;
+//}
+//
+//.appeal_content p{
+//  margin-bottom: 10px;
+//  font-size: 12px;
+//  color: rgb(144, 147, 153);
+//}
+//
+//.process{
+//  position: relative;
+//  margin: 0 0 20px 0;
+//  padding-top: 12px;
+//  font-size: 16px;
+//  color: #303133;
+//  line-height: 22px;
+//}
+//
+//.process:before{
+//  position: absolute;
+//  top: 0;
+//  content: ' ';
+//  width: 100%;
+//  border-top: 1px #f8f8f8 solid;
+//}
+//
+//.appeal_footer{
+//  border-top: 1px solid #ebebeb;
+//  padding: 10px 20px;
+//  font-size: 14px;
+//}
+//
+//.appeal_action {
+//  text-align: center;
+//}
+
+</style>

+ 81 - 5
src/point/views/statistics/integral_event.vue

@@ -82,6 +82,7 @@
           <el-button type="primary" v-if="!$supremeAuthority('employee')" @click="excelImportShow = true">导入数据</el-button>
           <el-button type="primary" @click="exportExcel">导出当前数据</el-button>
           <el-button class="first-element-btn" v-if="noticeRole" :disabled="selectionID.length == 0" @click="deleteInBatches" type="danger">批量删除</el-button>
+          <el-button type="warning" :disabled="!appealEnable || appealSelectionEvents.length === 0" @click="showAppealCreate = true" >发起申诉</el-button>
         </div>
       </div>
       <div class="diy-tip1" style="margin-bottom: 10px;">
@@ -308,14 +309,39 @@
       </span>
     </el-dialog>
 
+<!--  发起申诉  -->
+    <AppealCreate
+      :visible.sync="showAppealCreate"
+      :events="appealSelectionEvents"
+      @createFinish="onAppealCreateFinish"
+    />
+
+    <!--  申诉详情  -->
+    <el-drawer
+      :with-header="false"
+      :visible.sync="showAppealDetail"
+      size="500px"
+      direction="rtl"
+      :show-close="false"
+      :wrapper-closable="true"
+      @close="handleAppealClose"
+      @closed="handleAppealClosed"
+      @opened="handleAppealOpen"
+    >
+      <AppealPopup ref="appealInfo" :id="appeal.id" @update:appeal="onAppealUpdate"/>
+    </el-drawer>
   </div>
 </template>
 <script>
 import moment from 'moment';
 import Steps from '@/components/Steps.vue';
+import AppealCreate from "../../../components/AppealCreate.vue";
+import AppealPopup from "../common/AppealPopup.vue";
 export default {
   data() {
     return {
+      userInfo: this.$userInfo(),
+      pts:this.$getTyps(),
       instantPickerOptions: {
         shortcuts: [
           {
@@ -411,10 +437,9 @@ export default {
       update_btn: false,
       error_list: [],
       importErrorInfoShow: false,
-
       deriveNum: 0,
-
       selectionID: [] ,//删除的事件ID
+      appealSelectionEvents:[], //批量发起复议的事件ID
       source_type: [
       	{id: 0,name: '全部'},
       	{id: 1,name: '积分奖扣'},
@@ -439,10 +464,14 @@ export default {
           value: '扣分',
           id: 2
         },
-      ]
+      ],
+      showAppealCreate:false,
+      appeal:{id:0},
+      showAppealDetail:false,
+      appealEnable:false
     };
   },
-  components: {Steps},
+  components: {AppealPopup, AppealCreate, Steps},
   created() {
       this.process=process.env.NODE_ENV
       this.deriveRestrict()
@@ -454,6 +483,7 @@ export default {
     this.get_rule_trees();
     this.get_integral_list(this.formData);
     this.voluntarilyRule_trees(); //自动积分列表
+    this.getConfig()
   },
   watch: {
     newTaskFormType(val) {
@@ -536,10 +566,21 @@ export default {
     //删除事件
     deleteEvents(selection) {
       let listId = [];
+      let appealEvents = [];
       selection.forEach(item => {
         listId.push(item.id);
+        if (item.employee_id === this.userInfo.id) {
+          let pt = this.pts.find(p => p.id === item.pt_id)
+          pt = pt ? pt.name : ''
+          appealEvents.push({
+            id:item.id,
+            remark:`${item.point} ${pt} ${item.event_time} ${item.remark}`,
+            appeal_remark: ''
+          })
+        }
       });
       this.selectionID = listId;
+      this.appealSelectionEvents = appealEvents
     },
     //批量删除
     deleteInBatches() {
@@ -803,7 +844,42 @@ export default {
     },
     close_integral_event() {
       this.detail_popup = false;
-    }
+    },
+    onAppealCreateFinish(appeal){
+      this.appeal = {id:0}
+      this.$confirm('申诉已发起,积分申诉模块可以查看详细记录','发起申诉',{
+        confirmButtonText:'直接查看',
+        cancelButtonText:'知道了',
+        type:'success'
+      })
+        .then(() => {
+          this.appeal = appeal
+          this.showAppealDetail = true
+        })
+
+      this.get_integral_list(this.formData)
+    },
+    onAppealUpdate(){
+      this.get_integral_list(this.formData)
+    },
+    handleAppealClose(){
+      this.$refs['appealInfo'].closeHandler()
+    },
+    handleAppealClosed(){
+      this.$refs['appealInfo'].closedHandler()
+    },
+    handleAppealOpen(){
+      this.$refs['appealInfo'].openedHandler()
+    },
+    getConfig(){
+      let self = this
+      self.$axiosUser('get','/api/pro/integral/site/config')
+        .then(res => {
+          if (res.data.code === 1){
+            self.appealEnable = res.data.data.appeal === 1
+          }
+        })
+    },
   }
 };
 </script>

+ 1 - 1
src/point/views/workbench/task/my_issue.vue

@@ -523,7 +523,7 @@ $red: #f56c6c;
     color: #a6a8aa;
   }
 
- /deep/ .el-table tr:hover{
+/deep/ .el-table tr:hover{
   cursor:pointer
 }
 

+ 8 - 0
src/router/index.js

@@ -159,6 +159,14 @@ const constantRouterMap = [
               jurisdiction: ['creator', 'admin', 'point_manager', 'dept_manager']
             }
           },
+          {
+            path: '/appeal',
+            name: '积分申诉',
+            component: () => import( /* webpackChunkName: "integral_event" */ '@/point/views/statistics/appeal'),
+            meta: {
+              groupCode: 'integral'
+            }
+          },
 
           // 任务管理
           {