Commit 070faa1291afe4587a0b217b6e904dacee5f1f06

Authored by “wangming”
1 parent 86e6cb70

Implement validation logic for order creation and enhance performance statistics…

… tracking for order processing.
antis-ncc-admin/src/api/extend/salaryCalculation.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +
  3 +// 保存金三角开卡业绩统计数据
  4 +export function saveGoldTriangleStatistics(statisticsMonth) {
  5 + return request({
  6 + url: '/api/Extend/LqStatistics/save-gold-triangle-statistics',
  7 + method: 'POST',
  8 + data: { statisticsMonth }
  9 + })
  10 +}
  11 +
  12 +// 保存健康师个人开单业绩统计数据
  13 +export function savePersonalPerformanceStatistics(statisticsMonth) {
  14 + return request({
  15 + url: '/api/Extend/LqStatistics/save-personal-performance-statistics',
  16 + method: 'POST',
  17 + data: { statisticsMonth }
  18 + })
  19 +}
  20 +
  21 +// 保存科技部开单业绩统计数据
  22 +export function saveTechPerformanceStatistics(statisticsMonth) {
  23 + return request({
  24 + url: '/api/Extend/LqStatistics/save-tech-performance-statistics',
  25 + method: 'POST',
  26 + data: { statisticsMonth }
  27 + })
  28 +}
... ...
antis-ncc-admin/src/views/salaryCalculation/index.vue 0 → 100644
  1 +<template>
  2 + <div class="NCC-common-layout">
  3 + <div class="NCC-common-layout-center">
  4 + <!-- 页面标题 -->
  5 + <div class="page-header">
  6 + <h2 class="page-title">
  7 + <i class="el-icon-calculator"></i>
  8 + 工资计算系统
  9 + </h2>
  10 + <p class="page-description">统计各类业绩数据,为工资计算提供基础数据支撑</p>
  11 + </div>
  12 +
  13 + <!-- 统计月份选择 -->
  14 + <div class="NCC-common-search-box">
  15 + <el-form :inline="true" class="statistics-form">
  16 + <el-form-item label="统计月份">
  17 + <el-date-picker
  18 + v-model="statisticsMonth"
  19 + type="month"
  20 + placeholder="选择月份"
  21 + format="yyyyMM"
  22 + value-format="yyyyMM"
  23 + :clearable="false"
  24 + class="month-picker"
  25 + />
  26 + </el-form-item>
  27 + <el-form-item>
  28 + <el-button
  29 + type="primary"
  30 + icon="el-icon-refresh"
  31 + @click="refreshMonth"
  32 + :loading="loading"
  33 + >
  34 + 刷新月份
  35 + </el-button>
  36 + </el-form-item>
  37 + </el-form>
  38 + </div>
  39 +
  40 + <!-- 统计按钮区域 -->
  41 + <div class="statistics-buttons">
  42 + <div class="button-grid">
  43 + <!-- 金三角开卡业绩统计 -->
  44 + <div class="statistics-card gold-triangle">
  45 + <div class="card-header">
  46 + <i class="el-icon-trophy"></i>
  47 + <h3>金三角开卡业绩统计</h3>
  48 + </div>
  49 + <div class="card-content">
  50 + <p class="card-description">统计金三角团队的开卡业绩数据</p>
  51 + <el-button
  52 + type="primary"
  53 + size="large"
  54 + :loading="loadingStates.goldTriangle"
  55 + @click="handleGoldTriangleStatistics"
  56 + class="statistics-btn"
  57 + >
  58 + <i class="el-icon-data-analysis"></i>
  59 + 开始统计
  60 + </el-button>
  61 + </div>
  62 + </div>
  63 +
  64 + <!-- 健康师个人开单业绩统计 -->
  65 + <div class="statistics-card personal-performance">
  66 + <div class="card-header">
  67 + <i class="el-icon-user"></i>
  68 + <h3>健康师个人开单业绩统计</h3>
  69 + </div>
  70 + <div class="card-content">
  71 + <p class="card-description">统计健康师个人开单业绩,包含基础业绩和合作业绩</p>
  72 + <el-button
  73 + type="success"
  74 + size="large"
  75 + :loading="loadingStates.personalPerformance"
  76 + @click="handlePersonalPerformanceStatistics"
  77 + class="statistics-btn"
  78 + >
  79 + <i class="el-icon-data-analysis"></i>
  80 + 开始统计
  81 + </el-button>
  82 + </div>
  83 + </div>
  84 +
  85 + <!-- 科技部开单业绩统计 -->
  86 + <div class="statistics-card tech-performance">
  87 + <div class="card-header">
  88 + <i class="el-icon-cpu"></i>
  89 + <h3>科技部开单业绩统计</h3>
  90 + </div>
  91 + <div class="card-content">
  92 + <p class="card-description">统计科技部老师的开单业绩数据</p>
  93 + <el-button
  94 + type="warning"
  95 + size="large"
  96 + :loading="loadingStates.techPerformance"
  97 + @click="handleTechPerformanceStatistics"
  98 + class="statistics-btn"
  99 + >
  100 + <i class="el-icon-data-analysis"></i>
  101 + 开始统计
  102 + </el-button>
  103 + </div>
  104 + </div>
  105 + </div>
  106 + </div>
  107 +
  108 + <!-- 结果显示区域 -->
  109 + <div class="result-section">
  110 + <div class="result-header">
  111 + <h3>
  112 + <i class="el-icon-document"></i>
  113 + 统计结果
  114 + </h3>
  115 + <el-button
  116 + type="text"
  117 + icon="el-icon-delete"
  118 + @click="clearResults"
  119 + v-if="resultData.length > 0"
  120 + >
  121 + 清空结果
  122 + </el-button>
  123 + </div>
  124 +
  125 + <div class="result-content" v-if="resultData.length > 0">
  126 + <div
  127 + v-for="(result, index) in resultData"
  128 + :key="index"
  129 + class="result-item"
  130 + :class="result.type"
  131 + >
  132 + <div class="result-header-item">
  133 + <div class="result-title">
  134 + <i :class="getResultIcon(result.type)"></i>
  135 + {{ result.title }}
  136 + </div>
  137 + <div class="result-time">{{ result.time }}</div>
  138 + </div>
  139 + <div class="result-body">
  140 + <div class="result-status" :class="result.success ? 'success' : 'error'">
  141 + <i :class="result.success ? 'el-icon-success' : 'el-icon-error'"></i>
  142 + {{ result.success ? '统计成功' : '统计失败' }}
  143 + </div>
  144 + <div class="result-details">
  145 + <pre class="result-json">{{ formatResult(result.data) }}</pre>
  146 + </div>
  147 + </div>
  148 + </div>
  149 + </div>
  150 +
  151 + <div v-else class="empty-result">
  152 + <i class="el-icon-document-copy"></i>
  153 + <p>暂无统计结果,请点击上方按钮开始统计</p>
  154 + </div>
  155 + </div>
  156 + </div>
  157 + </div>
  158 +</template>
  159 +
  160 +<script>
  161 +import {
  162 + saveGoldTriangleStatistics,
  163 + savePersonalPerformanceStatistics,
  164 + saveTechPerformanceStatistics
  165 +} from '@/api/extend/salaryCalculation'
  166 +
  167 +export default {
  168 + name: 'SalaryCalculation',
  169 + data() {
  170 + return {
  171 + // 统计月份,默认为当前月份
  172 + statisticsMonth: this.getCurrentMonth(),
  173 + // 加载状态
  174 + loading: false,
  175 + loadingStates: {
  176 + goldTriangle: false,
  177 + personalPerformance: false,
  178 + techPerformance: false
  179 + },
  180 + // 结果数据
  181 + resultData: []
  182 + }
  183 + },
  184 + methods: {
  185 + // 获取当前月份
  186 + getCurrentMonth() {
  187 + const now = new Date()
  188 + const year = now.getFullYear()
  189 + const month = String(now.getMonth() + 1).padStart(2, '0')
  190 + return `${year}${month}`
  191 + },
  192 +
  193 + // 刷新月份
  194 + refreshMonth() {
  195 + this.statisticsMonth = this.getCurrentMonth()
  196 + this.$message.success('月份已刷新为当前月份')
  197 + },
  198 +
  199 + // 金三角开卡业绩统计
  200 + async handleGoldTriangleStatistics() {
  201 + this.loadingStates.goldTriangle = true
  202 + try {
  203 + const response = await saveGoldTriangleStatistics(this.statisticsMonth)
  204 + this.addResult({
  205 + type: 'gold-triangle',
  206 + title: '金三角开卡业绩统计',
  207 + success: true,
  208 + data: response.data,
  209 + time: new Date().toLocaleString()
  210 + })
  211 + this.$message.success('金三角开卡业绩统计完成')
  212 + } catch (error) {
  213 + this.addResult({
  214 + type: 'gold-triangle',
  215 + title: '金三角开卡业绩统计',
  216 + success: false,
  217 + data: error.message || '统计失败',
  218 + time: new Date().toLocaleString()
  219 + })
  220 + this.$message.error('金三角开卡业绩统计失败')
  221 + } finally {
  222 + this.loadingStates.goldTriangle = false
  223 + }
  224 + },
  225 +
  226 + // 健康师个人开单业绩统计
  227 + async handlePersonalPerformanceStatistics() {
  228 + this.loadingStates.personalPerformance = true
  229 + try {
  230 + const response = await savePersonalPerformanceStatistics(this.statisticsMonth)
  231 + this.addResult({
  232 + type: 'personal-performance',
  233 + title: '健康师个人开单业绩统计',
  234 + success: true,
  235 + data: response.data,
  236 + time: new Date().toLocaleString()
  237 + })
  238 + this.$message.success('健康师个人开单业绩统计完成')
  239 + } catch (error) {
  240 + this.addResult({
  241 + type: 'personal-performance',
  242 + title: '健康师个人开单业绩统计',
  243 + success: false,
  244 + data: error.message || '统计失败',
  245 + time: new Date().toLocaleString()
  246 + })
  247 + this.$message.error('健康师个人开单业绩统计失败')
  248 + } finally {
  249 + this.loadingStates.personalPerformance = false
  250 + }
  251 + },
  252 +
  253 + // 科技部开单业绩统计
  254 + async handleTechPerformanceStatistics() {
  255 + this.loadingStates.techPerformance = true
  256 + try {
  257 + const response = await saveTechPerformanceStatistics(this.statisticsMonth)
  258 + this.addResult({
  259 + type: 'tech-performance',
  260 + title: '科技部开单业绩统计',
  261 + success: true,
  262 + data: response.data,
  263 + time: new Date().toLocaleString()
  264 + })
  265 + this.$message.success('科技部开单业绩统计完成')
  266 + } catch (error) {
  267 + this.addResult({
  268 + type: 'tech-performance',
  269 + title: '科技部开单业绩统计',
  270 + success: false,
  271 + data: error.message || '统计失败',
  272 + time: new Date().toLocaleString()
  273 + })
  274 + this.$message.error('科技部开单业绩统计失败')
  275 + } finally {
  276 + this.loadingStates.techPerformance = false
  277 + }
  278 + },
  279 +
  280 + // 添加结果
  281 + addResult(result) {
  282 + this.resultData.unshift(result)
  283 + // 限制结果数量,最多保留10条
  284 + if (this.resultData.length > 10) {
  285 + this.resultData = this.resultData.slice(0, 10)
  286 + }
  287 + },
  288 +
  289 + // 清空结果
  290 + clearResults() {
  291 + this.resultData = []
  292 + this.$message.success('结果已清空')
  293 + },
  294 +
  295 + // 获取结果图标
  296 + getResultIcon(type) {
  297 + const iconMap = {
  298 + 'gold-triangle': 'el-icon-trophy',
  299 + 'personal-performance': 'el-icon-user',
  300 + 'tech-performance': 'el-icon-cpu'
  301 + }
  302 + return iconMap[type] || 'el-icon-document'
  303 + },
  304 +
  305 + // 格式化结果显示
  306 + formatResult(data) {
  307 + if (typeof data === 'string') {
  308 + return data
  309 + }
  310 + return JSON.stringify(data, null, 2)
  311 + }
  312 + }
  313 +}
  314 +</script>
  315 +
  316 +<style lang="scss" scoped>
  317 +.page-header {
  318 + text-align: center;
  319 + margin-bottom: 30px;
  320 + padding: 20px 0;
  321 + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  322 + border-radius: 12px;
  323 + color: white;
  324 +
  325 + .page-title {
  326 + margin: 0 0 10px 0;
  327 + font-size: 28px;
  328 + font-weight: 600;
  329 +
  330 + i {
  331 + margin-right: 10px;
  332 + font-size: 32px;
  333 + }
  334 + }
  335 +
  336 + .page-description {
  337 + margin: 0;
  338 + font-size: 16px;
  339 + opacity: 0.9;
  340 + }
  341 +}
  342 +
  343 +.statistics-form {
  344 + background: white;
  345 + padding: 20px;
  346 + border-radius: 12px;
  347 + box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
  348 + margin-bottom: 20px;
  349 +
  350 + .month-picker {
  351 + width: 200px;
  352 + }
  353 +}
  354 +
  355 +.statistics-buttons {
  356 + margin-bottom: 30px;
  357 +
  358 + .button-grid {
  359 + display: grid;
  360 + grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
  361 + gap: 20px;
  362 + }
  363 +}
  364 +
  365 +.statistics-card {
  366 + background: white;
  367 + border-radius: 12px;
  368 + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
  369 + transition: all 0.3s ease;
  370 + overflow: hidden;
  371 +
  372 + &:hover {
  373 + transform: translateY(-5px);
  374 + box-shadow: 0 8px 30px rgba(0, 0, 0, 0.15);
  375 + }
  376 +
  377 + .card-header {
  378 + padding: 20px 20px 15px;
  379 + border-bottom: 1px solid #f0f0f0;
  380 +
  381 + i {
  382 + font-size: 24px;
  383 + margin-right: 10px;
  384 + }
  385 +
  386 + h3 {
  387 + margin: 0;
  388 + font-size: 18px;
  389 + font-weight: 600;
  390 + display: inline-block;
  391 + }
  392 + }
  393 +
  394 + .card-content {
  395 + padding: 20px;
  396 +
  397 + .card-description {
  398 + color: #666;
  399 + margin: 0 0 20px 0;
  400 + line-height: 1.6;
  401 + }
  402 +
  403 + .statistics-btn {
  404 + width: 100%;
  405 + height: 50px;
  406 + font-size: 16px;
  407 + font-weight: 600;
  408 + border-radius: 8px;
  409 +
  410 + i {
  411 + margin-right: 8px;
  412 + font-size: 18px;
  413 + }
  414 + }
  415 + }
  416 +
  417 + // 不同卡片的主题色
  418 + &.gold-triangle {
  419 + .card-header i {
  420 + color: #409EFF;
  421 + }
  422 + }
  423 +
  424 + &.personal-performance {
  425 + .card-header i {
  426 + color: #67C23A;
  427 + }
  428 + }
  429 +
  430 + &.tech-performance {
  431 + .card-header i {
  432 + color: #E6A23C;
  433 + }
  434 + }
  435 +}
  436 +
  437 +.result-section {
  438 + background: white;
  439 + border-radius: 12px;
  440 + box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
  441 + overflow: hidden;
  442 +
  443 + .result-header {
  444 + display: flex;
  445 + justify-content: space-between;
  446 + align-items: center;
  447 + padding: 20px;
  448 + background: #f8f9fa;
  449 + border-bottom: 1px solid #e9ecef;
  450 +
  451 + h3 {
  452 + margin: 0;
  453 + font-size: 18px;
  454 + font-weight: 600;
  455 + color: #333;
  456 +
  457 + i {
  458 + margin-right: 8px;
  459 + color: #409EFF;
  460 + }
  461 + }
  462 + }
  463 +
  464 + .result-content {
  465 + max-height: 600px;
  466 + overflow-y: auto;
  467 + }
  468 +
  469 + .result-item {
  470 + border-bottom: 1px solid #f0f0f0;
  471 +
  472 + &:last-child {
  473 + border-bottom: none;
  474 + }
  475 +
  476 + .result-header-item {
  477 + display: flex;
  478 + justify-content: space-between;
  479 + align-items: center;
  480 + padding: 15px 20px;
  481 + background: #fafafa;
  482 +
  483 + .result-title {
  484 + font-weight: 600;
  485 + color: #333;
  486 +
  487 + i {
  488 + margin-right: 8px;
  489 + }
  490 + }
  491 +
  492 + .result-time {
  493 + color: #999;
  494 + font-size: 14px;
  495 + }
  496 + }
  497 +
  498 + .result-body {
  499 + padding: 20px;
  500 +
  501 + .result-status {
  502 + display: flex;
  503 + align-items: center;
  504 + margin-bottom: 15px;
  505 + font-weight: 600;
  506 +
  507 + i {
  508 + margin-right: 8px;
  509 + font-size: 16px;
  510 + }
  511 +
  512 + &.success {
  513 + color: #67C23A;
  514 + }
  515 +
  516 + &.error {
  517 + color: #F56C6C;
  518 + }
  519 + }
  520 +
  521 + .result-details {
  522 + .result-json {
  523 + background: #f8f9fa;
  524 + border: 1px solid #e9ecef;
  525 + border-radius: 6px;
  526 + padding: 15px;
  527 + margin: 0;
  528 + font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
  529 + font-size: 13px;
  530 + line-height: 1.5;
  531 + color: #333;
  532 + white-space: pre-wrap;
  533 + word-break: break-all;
  534 + max-height: 300px;
  535 + overflow-y: auto;
  536 + }
  537 + }
  538 + }
  539 +
  540 + // 不同类型的结果项样式
  541 + &.gold-triangle {
  542 + .result-header-item {
  543 + border-left: 4px solid #409EFF;
  544 + }
  545 + }
  546 +
  547 + &.personal-performance {
  548 + .result-header-item {
  549 + border-left: 4px solid #67C23A;
  550 + }
  551 + }
  552 +
  553 + &.tech-performance {
  554 + .result-header-item {
  555 + border-left: 4px solid #E6A23C;
  556 + }
  557 + }
  558 + }
  559 +
  560 + .empty-result {
  561 + text-align: center;
  562 + padding: 60px 20px;
  563 + color: #999;
  564 +
  565 + i {
  566 + font-size: 48px;
  567 + margin-bottom: 20px;
  568 + color: #ddd;
  569 + }
  570 +
  571 + p {
  572 + margin: 0;
  573 + font-size: 16px;
  574 + }
  575 + }
  576 +}
  577 +
  578 +// 响应式设计
  579 +@media (max-width: 768px) {
  580 + .button-grid {
  581 + grid-template-columns: 1fr;
  582 + }
  583 +
  584 + .page-header {
  585 + .page-title {
  586 + font-size: 24px;
  587 + }
  588 +
  589 + .page-description {
  590 + font-size: 14px;
  591 + }
  592 + }
  593 +
  594 + .statistics-card {
  595 + .card-header h3 {
  596 + font-size: 16px;
  597 + }
  598 +
  599 + .card-content .statistics-btn {
  600 + height: 45px;
  601 + font-size: 14px;
  602 + }
  603 + }
  604 +}
  605 +</style>
... ...