Explorar el Código

Merge branch 'performanceConfig' into dev

huoyi hace 4 semanas
padre
commit
6dbe9603b5

+ 26 - 0
src/api/performance/performance.js

@@ -0,0 +1,26 @@
1
+import request from '@/utils/request'
2
+//绩效查询
3
+export function getPerformanceList(query) {
4
+  return request({
5
+    url: '/item/indicators/query',
6
+    method: 'get',
7
+    params: query
8
+  })
9
+}
10
+//获取绩效矩阵参数配置
11
+export function getMatrixConfig(query) {
12
+  return request({
13
+    url: '/item/indicators/matrix/config',
14
+    method: 'get',
15
+    params: query
16
+  })
17
+}
18
+//保存绩效矩阵
19
+export function saveMatrixConfig(query) {
20
+  return request({
21
+    url: '/item/indicators/matrix/add',
22
+    method: 'get',
23
+    params: query
24
+  })
25
+}
26
+

+ 35 - 0
src/utils/request.js

@@ -150,4 +150,39 @@ export function download(url, params, filename, config) {
150 150
   })
151 151
 }
152 152
 
153
+// GET方法下载方法
154
+export function getDownload(url, params, filename, config) {
155
+  downloadLoadingInstance = ElLoading.service({ text: "正在下载数据,请稍候", background: "rgba(0, 0, 0, 0.7)", })
156
+  
157
+  // 处理GET请求参数
158
+  let requestUrl = url
159
+  if (params) {
160
+    const queryString = tansParams(params)
161
+    if (queryString) {
162
+      requestUrl += '?' + queryString
163
+    }
164
+  }
165
+  
166
+  return service.get(requestUrl, {
167
+    responseType: 'blob',
168
+    ...config
169
+  }).then(async (data) => {
170
+    const isBlob = blobValidate(data)
171
+    if (isBlob) {
172
+      const blob = new Blob([data])
173
+      saveAs(blob, filename)
174
+    } else {
175
+      const resText = await data.text()
176
+      const rspObj = JSON.parse(resText)
177
+      const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default']
178
+      ElMessage.error(errMsg)
179
+    }
180
+    downloadLoadingInstance.close()
181
+  }).catch((r) => {
182
+    console.error(r)
183
+    ElMessage.error('下载文件出现错误,请联系管理员!')
184
+    downloadLoadingInstance.close()
185
+  })
186
+}
187
+
153 188
 export default service

+ 1 - 0
src/utils/ruoyi.js

@@ -226,3 +226,4 @@ export function getNormalPath(p) {
226 226
 export function blobValidate(data) {
227 227
   return data.type !== 'application/json'
228 228
 }
229
+

+ 272 - 0
src/views/performanceManage/performanceConfig/index.vue

@@ -0,0 +1,272 @@
1
+<template>
2
+  <div class="app-container">
3
+    <el-card>
4
+      <!-- 第一行:标题 -->
5
+      <div class="header-section">
6
+        <h2 class="section-title">绩效指标</h2>
7
+      </div>
8
+
9
+      <!-- 第二行:多选按钮组 -->
10
+      <div class="button-group-section">
11
+        <el-checkbox-group v-model="selectedIndicators" class="indicator-checkbox-group" :min="3">
12
+          <el-checkbox-button v-for="indicator in indicatorOptions" :key="indicator.value" :label="indicator.value" disabled>
13
+            {{ indicator.label }}
14
+          </el-checkbox-button>
15
+        </el-checkbox-group>
16
+      </div>
17
+
18
+      <!-- 第三行:绩效矩阵标题 -->
19
+      <div class="header-section" style="margin-top: 30px;">
20
+        <h2 class="section-title">绩效矩阵</h2>
21
+      </div>
22
+
23
+      <!-- 第四行:默认值说明 -->
24
+      <!-- <div class="default-value-section">
25
+        <p class="default-value-text">
26
+          默认值a=3,b=5,c=2;abc可编辑,编辑后,计算逻辑按照下图取倒数
27
+        </p>
28
+      </div> -->
29
+
30
+      <!-- 第五行:绩效矩阵表格 -->
31
+      <div class="matrix-section">
32
+
33
+        <el-table :data="matrixData" border style="width:600px; margin-top: 20px;" class="performance-matrix">
34
+
35
+          <!-- 表头 -->
36
+          <el-table-column label="" prop="rowName" align="center" width="120" fixed />
37
+
38
+          <el-table-column v-for="header in matrixHeaders" :key="header" :label="header" align="center" min-width="120">
39
+            <template #default="scope">
40
+              <div v-if="scope.row.rowName === '查获效率' && header === '查获效率'" class="fixed-cell">
41
+                1
42
+              </div>
43
+              <div v-else-if="scope.row.rowName === '查获效率' && header === '巡检合格率'" class="editable-cell">
44
+                <el-input-number v-model="aValue" :min="1" :step="1" controls-position="right" size="small"
45
+                  style="width: 100%;" />
46
+              </div>
47
+              <div v-else-if="scope.row.rowName === '查获效率' && header === '抽问抽答正确率'" class="editable-cell">
48
+                <el-input-number v-model="bValue" :min="1" :step="1" controls-position="right" size="small"
49
+                  style="width: 100%;" />
50
+              </div>
51
+              <div v-else-if="scope.row.rowName === '巡检合格率' && header === '查获效率'" class="calculated-cell">
52
+                {{ reciprocalA }}
53
+              </div>
54
+              <div v-else-if="scope.row.rowName === '巡检合格率' && header === '巡检合格率'" class="fixed-cell">
55
+                1
56
+              </div>
57
+              <div v-else-if="scope.row.rowName === '巡检合格率' && header === '抽问抽答正确率'" class="editable-cell">
58
+                <el-input-number v-model="cValue" :min="1" :step="1" controls-position="right" size="small"
59
+                  style="width: 100%;" />
60
+              </div>
61
+              <div v-else-if="scope.row.rowName === '抽问抽答正确率' && header === '查获效率'" class="calculated-cell">
62
+                {{ reciprocalB }}
63
+              </div>
64
+              <div v-else-if="scope.row.rowName === '抽问抽答正确率' && header === '巡检合格率'" class="calculated-cell">
65
+                {{ reciprocalC }}
66
+              </div>
67
+              <div v-else-if="scope.row.rowName === '抽问抽答正确率' && header === '抽问抽答正确率'" class="fixed-cell">
68
+                1
69
+              </div>
70
+              <div v-else>
71
+                -
72
+              </div>
73
+            </template>
74
+          </el-table-column>
75
+        </el-table>
76
+
77
+        <div class="matrix-actions" style="margin-bottom: 20px;">
78
+          <el-button type="primary" @click="handleSaveMatrixConfig">保存配置</el-button>
79
+        </div>
80
+      </div>
81
+    </el-card>
82
+  </div>
83
+</template>
84
+
85
+<script setup>
86
+import { ref, reactive, onMounted, computed } from 'vue'
87
+import { ElMessage } from 'element-plus'
88
+import { getMatrixConfig, saveMatrixConfig } from '@/api/performance/performance.js'
89
+
90
+// 绩效指标选项
91
+const indicatorOptions = [
92
+  { label: '查获效率', value: 'detectionEfficiency' },
93
+  { label: '巡检合格率', value: 'inspectionPassRate' },
94
+  { label: '抽问抽答正确率', value: 'trainingScore' }
95
+]
96
+
97
+// 选中的指标(默认全选)
98
+const selectedIndicators = ref(['detectionEfficiency', 'inspectionPassRate', 'trainingScore'])
99
+
100
+// 矩阵表头
101
+const matrixHeaders = ['查获效率', '巡检合格率', '抽问抽答正确率']
102
+
103
+// 矩阵数据(简化版,主要用于显示行名)
104
+const matrixData = reactive([
105
+  { rowName: '查获效率' },
106
+  { rowName: '巡检合格率' },
107
+  { rowName: '抽问抽答正确率' }
108
+])
109
+
110
+// 可编辑的a、b、c值
111
+const aValue = ref(0)
112
+const bValue = ref(0)
113
+const cValue = ref(0)
114
+
115
+// 计算值(使用computed实现自动更新)
116
+const reciprocalA = computed(() => `1/${aValue.value}`)
117
+const reciprocalB = computed(() => `1/${bValue.value}`)
118
+const reciprocalC = computed(() => `1/${cValue.value}`)
119
+
120
+// 保存绩效矩阵配置
121
+const handleSaveMatrixConfig = async () => {
122
+  try {
123
+    const params = {
124
+      a: aValue.value,
125
+      b: bValue.value,
126
+      c: cValue.value
127
+    }
128
+
129
+    const response = await saveMatrixConfig(params)
130
+
131
+    if (response.code === 200) {
132
+      ElMessage.success('保存成功')
133
+    } else {
134
+      ElMessage.error('保存失败:' + response.msg)
135
+    }
136
+  } catch (error) {
137
+    console.error('保存配置异常:', error)
138
+    ElMessage.error('保存失败,请重试')
139
+  }
140
+}
141
+
142
+// 获取绩效矩阵配置
143
+const fetchMatrixConfig = async () => {
144
+  try {
145
+    const response = await getMatrixConfig({})
146
+
147
+    if (response.code === 200 && response.data) {
148
+      const { paramA, paramB, paramC } = response.data
149
+      // 设置从接口返回的a、b、c值
150
+      aValue.value = paramA || 0
151
+      bValue.value = paramB || 0
152
+      cValue.value = paramC || 0
153
+    }
154
+  } catch (error) {
155
+    console.error('获取配置异常:', error)
156
+    // 使用默认值
157
+    aValue.value = 3
158
+    bValue.value = 5
159
+    cValue.value = 2
160
+  }
161
+}
162
+
163
+onMounted(() => {
164
+  // 页面加载时获取配置
165
+  fetchMatrixConfig()
166
+})
167
+</script>
168
+
169
+<style lang="less" scoped>
170
+.app-container {
171
+  padding: 20px;
172
+}
173
+
174
+.header-section {
175
+  margin-bottom: 20px;
176
+
177
+  .section-title {
178
+    margin: 0;
179
+    color: #303133;
180
+    font-size: 18px;
181
+    font-weight: 600;
182
+  }
183
+}
184
+
185
+.button-group-section {
186
+  margin-bottom: 30px;
187
+
188
+  .indicator-checkbox-group {
189
+    display: flex;
190
+    gap: 10px;
191
+
192
+    :deep(.el-checkbox-button) {
193
+      .el-checkbox-button__inner {
194
+        padding: 12px 20px;
195
+        font-size: 14px;
196
+        border-radius: 4px;
197
+
198
+        &:hover {
199
+          color: #409EFF;
200
+          border-color: #409EFF;
201
+        }
202
+      }
203
+
204
+      &.is-checked {
205
+        .el-checkbox-button__inner {
206
+          background-color: #409EFF;
207
+          border-color: #409EFF;
208
+          color: #fff;
209
+
210
+          &:hover {
211
+            background-color: #66b1ff;
212
+            border-color: #66b1ff;
213
+          }
214
+        }
215
+      }
216
+    }
217
+  }
218
+}
219
+
220
+.default-value-section {
221
+  margin: 20px 0;
222
+  padding: 15px;
223
+  background-color: #f5f7fa;
224
+  border-radius: 4px;
225
+  border-left: 4px solid #409EFF;
226
+
227
+  .default-value-text {
228
+    margin: 0;
229
+    color: #606266;
230
+    font-size: 14px;
231
+    line-height: 1.5;
232
+  }
233
+}
234
+
235
+.matrix-section {
236
+  .matrix-actions {
237
+    margin-top: 20px;
238
+  }
239
+
240
+  .performance-matrix {
241
+    :deep(.el-table__header-wrapper) {
242
+      th {
243
+        background-color: #f5f7fa;
244
+        font-weight: 600;
245
+        color: #303133;
246
+      }
247
+    }
248
+
249
+    :deep(.el-table__body-wrapper) {
250
+      td {
251
+        .fixed-cell {
252
+          color: #303133;
253
+          font-weight: 600;
254
+        }
255
+
256
+        .editable-cell {
257
+          padding: 4px;
258
+        }
259
+
260
+        .calculated-cell {
261
+          color: #409EFF;
262
+          font-weight: 500;
263
+        }
264
+
265
+        &:not(:first-child) {
266
+          font-weight: 500;
267
+        }
268
+      }
269
+    }
270
+  }
271
+}
272
+</style>

+ 429 - 0
src/views/performanceManage/performanceSearch/index.vue

@@ -0,0 +1,429 @@
1
+<template>
2
+  <div class="app-container">
3
+    <el-card>
4
+      <!-- 第一行:科室班组个人按钮组 -->
5
+      <div class="filter-container">
6
+        <div class="button-group">
7
+          <span class="button-group-label">查询范围:</span>
8
+          <el-button v-for="item in queryTypeOptions" :key="item.value"
9
+            :type="queryType === item.value ? 'primary' : 'default'"
10
+            :class="queryType === item.value ? 'active' : 'inactive'" @click="handleQueryTypeChange(item.value)">
11
+            {{ item.label }}
12
+          </el-button>
13
+        </div>
14
+
15
+        <div class="button-group" style="margin-left: 20px;">
16
+          <span class="button-group-label">时间范围:</span>
17
+          <el-button v-for="item in timeRangeOptions" :key="item.value"
18
+            :type="timeRange === item.value ? 'primary' : 'default'"
19
+            :class="timeRange === item.value ? 'active' : 'inactive'" @click="handleTimeRangeChange(item.value)">
20
+            {{ item.label }}
21
+          </el-button>
22
+
23
+          <!-- 自定义时间范围选择器 -->
24
+          <el-date-picker v-if="timeRange === 'custom'" v-model="customDateRange" type="daterange" range-separator="至"
25
+            start-placeholder="开始日期" end-placeholder="结束日期" value-format="YYYY-MM-DD"
26
+            style="margin-left: 10px; width: 240px;" @change="handleCustomDateChange" />
27
+        </div>
28
+      </div>
29
+
30
+      <!-- 第二行:查询条件 -->
31
+      <el-form :model="queryParams" ref="queryFormRef" :inline="true" class="search-form">
32
+        <el-form-item label="主管" prop="deptId" v-if="queryType === '2' || queryType === '1'">
33
+          <el-select v-model="queryParams.deptId" placeholder="请选择主管" clearable style="width: 200px"
34
+            @change="handleQuery">
35
+            <el-option v-for="item in departmentOptions" :key="item.value" :label="item.text" :value="item.value" />
36
+          </el-select>
37
+        </el-form-item>
38
+        <el-form-item label="班组" prop="teamId" v-if="queryType === '2' || queryType === '1'">
39
+          <el-select v-model="queryParams.teamId" placeholder="请选择班组" clearable style="width: 200px"
40
+            @change="handleQuery">
41
+            <el-option v-for="item in teamOptions" :key="item.value" :label="item.text" :value="item.value" />
42
+          </el-select>
43
+        </el-form-item>
44
+        <el-form-item label="姓名" prop="userName" v-if="queryType === '1'">
45
+          <el-input v-model="queryParams.userName" placeholder="请输入姓名" clearable style="width: 200px"
46
+            @input="handleQuery" />
47
+        </el-form-item>
48
+        <!-- <el-form-item>
49
+          <el-button type="primary" icon="Search" @click="handleQuery">查询</el-button>
50
+          <el-button icon="Refresh" @click="resetQuery">重置</el-button>
51
+        </el-form-item> -->
52
+      </el-form>
53
+
54
+      <!-- 导出按钮 -->
55
+      <div class="export-container">
56
+        <el-button type="warning" icon="Download" @click="handleExport">导出</el-button>
57
+      </div>
58
+
59
+      <!-- 表格 -->
60
+      <el-table v-loading="loading" :data="performanceList" border fit highlight-current-row
61
+        style="width: 100%; margin-top: 20px;">
62
+
63
+        <!-- 动态列 -->
64
+        <el-table-column v-if="queryType === '3' || queryType === '2' || queryType === '1'" label="主管"
65
+          :prop="queryType === '3' ? 'name' : 'deptName'" align="center" min-width="120" />
66
+
67
+        <el-table-column v-if="queryType === '2' || queryType === '1'" label="班组"
68
+          :prop="queryType === '2' ? 'name' : 'className'" align="center" min-width="120" />
69
+
70
+        <el-table-column v-if="queryType === '1'" label="姓名" prop="name" align="center" min-width="100" />
71
+
72
+        <!-- 固定列 -->
73
+        <el-table-column label="总分" prop="totalScore" align="center" min-width="100" sortable
74
+          :sort-orders="['ascending', 'descending']" @sort-change="handleSortChange('totalScore', $event)" />
75
+
76
+        <el-table-column label="查获效率" prop="seizureEfficiency" align="center" min-width="120" sortable
77
+          :sort-orders="['ascending', 'descending']" @sort-change="handleSortChange('seizureEfficiency', $event)" />
78
+
79
+        <el-table-column label="抽问抽答正确率" prop="trainingScore" align="center" min-width="100" sortable
80
+          :sort-orders="['ascending', 'descending']" @sort-change="handleSortChange('trainingScore', $event)" />
81
+
82
+        <el-table-column label="巡检合格率" prop="inspectionPassRate" align="center" min-width="120" sortable
83
+          :sort-orders="['ascending', 'descending']" @sort-change="handleSortChange('inspectionPassRate', $event)" />
84
+      </el-table>
85
+
86
+      <!-- 分页 -->
87
+      <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
88
+        v-model:limit="queryParams.pageSize" @pagination="getList" />
89
+    </el-card>
90
+  </div>
91
+</template>
92
+
93
+<script setup>
94
+import { ref, reactive, onMounted, getCurrentInstance, watch } from 'vue'
95
+import { getPerformanceList } from '@/api/performance/performance.js'
96
+import { listDept } from '@/api/system/dept.js'
97
+
98
+import { getDownload } from '@/utils/request.js'
99
+
100
+// 查询类型选项
101
+const queryTypeOptions = [
102
+  { label: '主管', value: '3' },
103
+  { label: '班组', value: '2' },
104
+  { label: '个人', value: '1' }
105
+]
106
+
107
+// 时间范围选项
108
+const timeRangeOptions = [
109
+  { label: '近三个月', value: 'threeMonths' },
110
+  { label: '近六个月', value: 'sixMonths' },
111
+  { label: '自定义', value: 'custom' }
112
+]
113
+
114
+// 响应式数据
115
+const queryType = ref('3') // 默认查询科室
116
+const timeRange = ref('threeMonths') // 默认近三个月
117
+const customDateRange = ref([])
118
+const loading = ref(false)
119
+const total = ref(0)
120
+const queryFormRef = ref()
121
+
122
+// 查询参数
123
+const queryParams = reactive({
124
+  pageNum: 1,
125
+  pageSize: 10,
126
+  deptId: '',
127
+  teamId: '',
128
+  userName: '',
129
+  sortField: '',
130
+  sortOrder: ''
131
+})
132
+
133
+// 绩效数据列表
134
+const performanceList = ref([])
135
+
136
+// 部门数据
137
+const deptList = ref([])
138
+const departmentOptions = ref([])
139
+const teamOptions = ref([])
140
+
141
+// 监听科室变化,获取对应班组
142
+watch(() => queryParams.deptId, async (newDeptId) => {
143
+  if (newDeptId) {
144
+    await fetchTeamByDept(newDeptId)
145
+  } else {
146
+    // 如果清空科室选择,重置班组选项为所有班组
147
+    teamOptions.value = deptList.value.filter(item => item.deptType === 'TEAMS').map(item => ({
148
+      value: item.deptId,
149
+      text: item.deptName
150
+    }))
151
+    queryParams.teamId = '' // 清空班组选择
152
+  }
153
+})
154
+
155
+// 获取部门数据
156
+const fetchDeptData = async () => {
157
+  try {
158
+    const response = await listDept({})
159
+    if (response.code === 200) {
160
+
161
+      deptList.value = response.data || []
162
+      // 生成科室选项
163
+      departmentOptions.value = deptList.value.filter(item => item.deptType === 'MANAGER').map(item => ({
164
+        value: item.deptId,
165
+        text: item.deptName
166
+      }))
167
+
168
+
169
+      // 生成班组选项
170
+      teamOptions.value = deptList.value.filter(item => item.deptType === 'TEAMS').map(item => ({
171
+        value: item.deptId,
172
+        text: item.deptName
173
+      }))
174
+    }
175
+  } catch (error) {
176
+    console.error('获取部门数据失败:', error)
177
+  }
178
+}
179
+
180
+// 根据科室ID获取班组列表
181
+const fetchTeamByDept = async (deptId) => {
182
+  try {
183
+    const response = await listDept({ parentId: deptId })
184
+    if (response.code === 200 && response.data) {
185
+      teamOptions.value = response.data.map(item => ({
186
+        value: item.deptId,
187
+        text: item.deptName
188
+      }))
189
+      // 清空班组选择,让用户重新选择
190
+      queryParams.teamId = ''
191
+    }
192
+  } catch (error) {
193
+    console.error('获取班组列表失败:', error)
194
+    // 如果获取失败,重置为所有班组
195
+    teamOptions.value = deptList.value.filter(item => item.deptType === 'TEAMS').map(item => ({
196
+      value: item.deptId,
197
+      text: item.deptName
198
+    }))
199
+  }
200
+}
201
+
202
+// 查询类型变化
203
+const handleQueryTypeChange = (type) => {
204
+  queryType.value = type
205
+  getList()
206
+}
207
+
208
+// 时间范围变化
209
+const handleTimeRangeChange = (range) => {
210
+  timeRange.value = range
211
+  if (range !== 'custom') {
212
+    getList()
213
+  }
214
+}
215
+
216
+// 格式化日期为 YYYY-MM-DD
217
+const formatDate = (date) => {
218
+  const year = date.getFullYear()
219
+  const month = String(date.getMonth() + 1).padStart(2, '0')
220
+  const day = String(date.getDate()).padStart(2, '0')
221
+  return `${year}-${month}-${day}`
222
+}
223
+
224
+// 计算时间范围
225
+const calculateTimeRange = (range) => {
226
+  const now = new Date()
227
+  const startTime = new Date()
228
+
229
+  switch (range) {
230
+    case 'threeMonths':
231
+      startTime.setMonth(now.getMonth() - 3)
232
+      break
233
+    case 'sixMonths':
234
+      startTime.setMonth(now.getMonth() - 6)
235
+      break
236
+    case 'custom':
237
+      if (customDateRange.value && customDateRange.value.length === 2) {
238
+        // 结束时间加一天
239
+        const endDate = new Date(customDateRange.value[1])
240
+        endDate.setDate(endDate.getDate() + 1)
241
+        return {
242
+          startTime: customDateRange.value[0],
243
+          endTime: formatDate(endDate)
244
+        }
245
+      }
246
+      return { startTime: '', endTime: '' }
247
+    default:
248
+      return { startTime: '', endTime: '' }
249
+  }
250
+
251
+  return {
252
+    startTime: formatDate(startTime),
253
+    endTime: formatDate(now)
254
+  }
255
+}
256
+
257
+// 自定义时间范围变化
258
+const handleCustomDateChange = () => {
259
+  if (customDateRange.value && customDateRange.value.length === 2) {
260
+    getList()
261
+  }
262
+}
263
+
264
+// 排序处理
265
+const handleSortChange = (field, { order }) => {
266
+  queryParams.sortField = field
267
+  queryParams.sortOrder = order === 'ascending' ? 'asc' : 'desc'
268
+  getList()
269
+}
270
+
271
+// 查询
272
+const handleQuery = () => {
273
+  queryParams.pageNum = 1
274
+  getList()
275
+}
276
+
277
+// 重置查询
278
+const resetQuery = () => {
279
+  queryFormRef.value.resetFields()
280
+  queryParams.pageNum = 1
281
+  queryParams.sortField = ''
282
+  queryParams.sortOrder = ''
283
+  getList()
284
+}
285
+
286
+// 获取绩效列表
287
+const getList = async () => {
288
+  loading.value = true
289
+
290
+  try {
291
+    // 计算时间范围
292
+    const timeRangeParams = calculateTimeRange(timeRange.value)
293
+
294
+    // 构建请求参数
295
+    const params = {
296
+      ...queryParams,
297
+      dimension: queryType.value,
298
+      ...timeRangeParams
299
+    }
300
+
301
+    // 根据查询类型清理不必要的参数
302
+    if (queryType.value === '3') {
303
+      // 科室查询:不需要班组参数
304
+      params.teamId = ''
305
+      params.deptId = ''
306
+    } else if (queryType.value === '2') {
307
+      // 班组查询:需要科室参数
308
+      // params.deptId = ''
309
+      // 班组查询:不需要个人参数
310
+    } else if (queryType.value === '1') {
311
+      // 个人查询:需要所有参数
312
+    }
313
+
314
+    // 调用API
315
+    const response = await getPerformanceList(params)
316
+
317
+    if (response.code === 200) {
318
+
319
+      performanceList.value = response.data || []
320
+      total.value = response.total || 0
321
+    } else {
322
+      console.error('获取绩效列表失败:', response.msg)
323
+      performanceList.value = []
324
+      total.value = 0
325
+    }
326
+  } catch (error) {
327
+    console.error('获取绩效列表异常:', error)
328
+    performanceList.value = []
329
+    total.value = 0
330
+  } finally {
331
+    loading.value = false
332
+  }
333
+}
334
+
335
+// 获取当前实例
336
+const { proxy } = getCurrentInstance()
337
+
338
+// 导出
339
+const handleExport = () => {
340
+  try {
341
+    // 计算时间范围
342
+    const timeRangeParams = calculateTimeRange(timeRange.value)
343
+
344
+    // 构建请求参数
345
+    const params = {
346
+      ...queryParams,
347
+      dimension: queryType.value,
348
+      ...timeRangeParams
349
+    }
350
+
351
+    // 根据查询类型清理不必要的参数
352
+    if (queryType.value === '3') {
353
+      // 科室查询:不需要班组参数
354
+      params.teamId = ''
355
+    } else if (queryType.value === '2') {
356
+      // 班组查询:不需要个人参数
357
+    } else if (queryType.value === '1') {
358
+      // 个人查询:需要所有参数
359
+    }
360
+
361
+    // 使用proxy.getDownload下载文件(GET请求)
362
+    getDownload('/item/indicators/export', params, `绩效查询数据_${new Date().getTime()}.xlsx`)
363
+  } catch (error) {
364
+    console.error('导出异常:', error)
365
+    proxy.$modal.msgError('导出异常,请重试')
366
+  }
367
+}
368
+
369
+onMounted(() => {
370
+  fetchDeptData()
371
+  getList()
372
+})
373
+</script>
374
+
375
+<style lang="less" scoped>
376
+.app-container {
377
+  padding: 20px;
378
+}
379
+
380
+.filter-container {
381
+  display: flex;
382
+  align-items: center;
383
+  margin-bottom: 20px;
384
+}
385
+
386
+.button-group {
387
+  display: flex;
388
+  align-items: center;
389
+
390
+  .button-group-label {
391
+    margin-right: 10px;
392
+    font-weight: 600;
393
+    color: #606266;
394
+  }
395
+
396
+  .el-button {
397
+    margin-right: 10px;
398
+
399
+    &.active {
400
+      background-color: #409EFF;
401
+      border-color: #409EFF;
402
+      color: #fff;
403
+    }
404
+
405
+    &.inactive {
406
+      background-color: #fff;
407
+      border-color: #409EFF;
408
+      color: #409EFF;
409
+    }
410
+  }
411
+}
412
+
413
+.search-form {
414
+  margin-bottom: 20px;
415
+}
416
+
417
+.export-container {
418
+  margin-bottom: 20px;
419
+}
420
+
421
+:deep(.el-table) {
422
+  .el-table__header-wrapper {
423
+    th {
424
+      background-color: #f5f7fa;
425
+      font-weight: 600;
426
+    }
427
+  }
428
+}
429
+</style>