Commit 64ac7d699557aa04b7992616f2b12d473712f943

Authored by 李宇
1 parent 12ccf031

最新

antis-ncc-admin/src/api/extend/headquartersStock.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +
  3 +// 获取总部股份统计列表
  4 +export function getHeadquartersStockList(params) {
  5 + return request({
  6 + url: '/api/Extend/lqsharestatisticshq/list',
  7 + method: 'GET',
  8 + data: params
  9 + })
  10 +}
  11 +
  12 +// 生成总部股份统计数据
  13 +export function generateHeadquartersStock(data) {
  14 + return request({
  15 + url: '/api/Extend/lqsharestatisticshq/generate',
  16 + method: 'POST',
  17 + data: data
  18 + })
  19 +}
  20 +
... ...
antis-ncc-admin/src/api/extend/shopStock.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +
  3 +// 获取门店股份统计列表
  4 +export function getShopStockList(params) {
  5 + return request({
  6 + url: '/api/Extend/lqsharestatisticsstore/list',
  7 + method: 'GET',
  8 + data: params
  9 + })
  10 +}
  11 +
  12 +// 生成门店股份统计数据
  13 +export function generateShopStock(data) {
  14 + return request({
  15 + url: '/api/Extend/lqsharestatisticsstore/generate',
  16 + method: 'POST',
  17 + data: data
  18 + })
  19 +}
  20 +
  21 +// 获取门店下拉框列表
  22 +export function getStoreSelector() {
  23 + return request({
  24 + url: '/api/Extend/LqMdxx/Selector',
  25 + method: 'GET'
  26 + })
  27 +}
  28 +
... ...
antis-ncc-admin/src/api/extend/techStock.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +
  3 +// 获取科技部股份统计列表
  4 +export function getTechStockList(params) {
  5 + return request({
  6 + url: '/api/Extend/lqsharestatisticstechdept/list',
  7 + method: 'GET',
  8 + data: params
  9 + })
  10 +}
  11 +
  12 +// 生成科技部股份统计数据
  13 +export function generateTechStock(data) {
  14 + return request({
  15 + url: '/api/Extend/lqsharestatisticstechdept/generate',
  16 + method: 'POST',
  17 + data: data
  18 + })
  19 +}
  20 +
... ...
antis-ncc-admin/src/views/statisticsList/form13.vue
... ... @@ -209,12 +209,12 @@
209 209 </el-table-column>
210 210 <el-table-column prop="sourceType" label="合作机构" min-width="100">
211 211 <template slot-scope="scope">
212   - <span>{{ scope.row.paymentMethod == '合作' ? (scope.row.hgjg || '无') : '无' }}</span>
  212 + <span>{{ scope.row.paymentMethod == '合作' ? (scope.row.hgjgName || '无') : '无' }}</span>
213 213 </template>
214 214 </el-table-column>
215 215 <el-table-column prop="sourceType" label="结算结构" min-width="100">
216 216 <template slot-scope="scope">
217   - <span>{{ scope.row.paymentMethod != '合作' ? (scope.row.fkyy || '无') : '无' }}</span>
  217 + <span>{{ scope.row.paymentMethod != '合作' ? (scope.row.fkyyName || '无') : '无' }}</span>
218 218 </template>
219 219 </el-table-column>
220 220 <el-table-column prop="remark" label="备注" min-width="150" show-overflow-tooltip>
... ...
antis-ncc-admin/src/views/wageManagement/headquarters-stock-detail-dialog.vue 0 → 100644
  1 +<template>
  2 + <el-dialog
  3 + title="总部股份详情"
  4 + :visible="visible"
  5 + width="1000px"
  6 + :close-on-click-modal="false"
  7 + @close="handleClose"
  8 + class="detail-dialog"
  9 + >
  10 + <div v-if="detailData" class="detail-content">
  11 + <!-- 基本信息 -->
  12 + <div class="detail-section">
  13 + <div class="section-title">
  14 + <i class="el-icon-user"></i>
  15 + <span>基本信息</span>
  16 + </div>
  17 + <div class="section-content">
  18 + <div class="info-row">
  19 + <div class="info-item">
  20 + <span class="label">统计月份:</span>
  21 + <span class="value">{{ formatMonth(detailData.StatisticsMonth) || '无' }}</span>
  22 + </div>
  23 + <div class="info-item" v-if="detailData.CreateTime">
  24 + <span class="label">创建时间:</span>
  25 + <span class="value">{{ formatDateTime(detailData.CreateTime) }}</span>
  26 + </div>
  27 + <div class="info-item" v-if="detailData.UpdateTime">
  28 + <span class="label">更新时间:</span>
  29 + <span class="value">{{ formatDateTime(detailData.UpdateTime) }}</span>
  30 + </div>
  31 + </div>
  32 + </div>
  33 + </div>
  34 +
  35 + <!-- 收入信息 -->
  36 + <div class="detail-section">
  37 + <div class="section-title">
  38 + <i class="el-icon-trophy"></i>
  39 + <span>收入信息</span>
  40 + </div>
  41 + <div class="section-content">
  42 + <div class="info-row">
  43 + <div class="info-item">
  44 + <span class="label">收入-全部:</span>
  45 + <span class="value highlight large">{{ formatMoney(detailData.IncomeGeneral) }}</span>
  46 + </div>
  47 + <div class="info-item">
  48 + <span class="label">收入-科技部:</span>
  49 + <span class="value highlight large">{{ formatMoney(detailData.IncomeTechDept) }}</span>
  50 + </div>
  51 + </div>
  52 + </div>
  53 + </div>
  54 +
  55 + <!-- 成本信息 -->
  56 + <div class="detail-section">
  57 + <div class="section-title">
  58 + <i class="el-icon-shopping-cart-2"></i>
  59 + <span>成本信息</span>
  60 + </div>
  61 + <div class="section-content">
  62 + <div class="info-row">
  63 + <div class="info-item">
  64 + <span class="label">成本-报销:</span>
  65 + <span class="value">{{ formatMoney(detailData.CostReimbursement) }}</span>
  66 + </div>
  67 + <div class="info-item">
  68 + <span class="label">成本-人工:</span>
  69 + <span class="value">{{ formatMoney(detailData.CostLabor) }}</span>
  70 + </div>
  71 + <div class="info-item">
  72 + <span class="label">成本-教育部房租:</span>
  73 + <span class="value">{{ formatMoney(detailData.CostEducationRent) }}</span>
  74 + </div>
  75 + <div class="info-item">
  76 + <span class="label">成本-仓库房租:</span>
  77 + <span class="value">{{ formatMoney(detailData.CostWarehouseRent) }}</span>
  78 + </div>
  79 + <div class="info-item">
  80 + <span class="label">成本-总部房租:</span>
  81 + <span class="value">{{ formatMoney(detailData.CostHQRent) }}</span>
  82 + </div>
  83 + </div>
  84 + </div>
  85 + </div>
  86 +
  87 + <!-- 利润结果 -->
  88 + <div class="detail-section">
  89 + <div class="section-title">
  90 + <i class="el-icon-data-line"></i>
  91 + <span>利润结果</span>
  92 + </div>
  93 + <div class="section-content">
  94 + <div class="info-row">
  95 + <div class="info-item">
  96 + <span class="label">运营利润:</span>
  97 + <span class="value highlight large profit">{{ formatMoney(detailData.OperationalProfit) }}</span>
  98 + </div>
  99 + </div>
  100 + </div>
  101 + </div>
  102 + </div>
  103 + <div slot="footer" class="dialog-footer">
  104 + <el-button @click="handleClose">关闭</el-button>
  105 + </div>
  106 + </el-dialog>
  107 +</template>
  108 +
  109 +<script>
  110 +export default {
  111 + name: 'HeadquartersStockDetailDialog',
  112 + props: {
  113 + visible: {
  114 + type: Boolean,
  115 + default: false
  116 + },
  117 + detailData: {
  118 + type: Object,
  119 + default: null
  120 + }
  121 + },
  122 + methods: {
  123 + handleClose() {
  124 + this.$emit('close')
  125 + },
  126 + formatMoney(value) {
  127 + if (value === null || value === undefined || value === '') {
  128 + return '0.00'
  129 + }
  130 + return Number(value).toFixed(2)
  131 + },
  132 + formatMonth(value) {
  133 + if (!value || value.length !== 6) {
  134 + return value || '无'
  135 + }
  136 + const year = value.substring(0, 4)
  137 + const month = value.substring(4, 6)
  138 + return `${year}-${month}`
  139 + },
  140 + formatDateTime(value) {
  141 + if (!value) return '无'
  142 + const date = new Date(value)
  143 + const year = date.getFullYear()
  144 + const month = String(date.getMonth() + 1).padStart(2, '0')
  145 + const day = String(date.getDate()).padStart(2, '0')
  146 + const hours = String(date.getHours()).padStart(2, '0')
  147 + const minutes = String(date.getMinutes()).padStart(2, '0')
  148 + const seconds = String(date.getSeconds()).padStart(2, '0')
  149 + return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
  150 + }
  151 + }
  152 +}
  153 +</script>
  154 +
  155 +<style lang="scss" scoped>
  156 +.detail-dialog {
  157 + .detail-content {
  158 + max-height: 70vh;
  159 + overflow-y: auto;
  160 + padding: 20px;
  161 + }
  162 +
  163 + .detail-section {
  164 + margin-bottom: 24px;
  165 + background: #f9f9f9;
  166 + border-radius: 8px;
  167 + padding: 16px;
  168 +
  169 + &:last-child {
  170 + margin-bottom: 0;
  171 + }
  172 +
  173 + .section-title {
  174 + display: flex;
  175 + align-items: center;
  176 + gap: 8px;
  177 + margin-bottom: 16px;
  178 + font-size: 16px;
  179 + font-weight: 600;
  180 + color: #303133;
  181 + border-bottom: 2px solid #409EFF;
  182 + padding-bottom: 8px;
  183 +
  184 + i {
  185 + color: #409EFF;
  186 + font-size: 18px;
  187 + }
  188 + }
  189 +
  190 + .section-content {
  191 + .info-row {
  192 + display: flex;
  193 + flex-wrap: wrap;
  194 + gap: 20px;
  195 +
  196 + .info-item {
  197 + flex: 0 0 calc(33.333% - 14px);
  198 + min-width: 250px;
  199 + display: flex;
  200 + align-items: center;
  201 + padding: 8px 12px;
  202 + background: #fff;
  203 + border-radius: 4px;
  204 + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
  205 +
  206 + .label {
  207 + color: #606266;
  208 + font-weight: 500;
  209 + margin-right: 8px;
  210 + white-space: nowrap;
  211 + }
  212 +
  213 + .value {
  214 + color: #303133;
  215 + flex: 1;
  216 + text-align: right;
  217 +
  218 + &.highlight {
  219 + color: #409EFF;
  220 + font-weight: 600;
  221 + }
  222 +
  223 + &.large {
  224 + font-size: 18px;
  225 + }
  226 +
  227 + &.profit {
  228 + color: #67C23A;
  229 + }
  230 + }
  231 + }
  232 + }
  233 + }
  234 + }
  235 +
  236 + .dialog-footer {
  237 + text-align: right;
  238 + padding: 20px;
  239 + border-top: 1px solid #e4e7ed;
  240 + }
  241 +}
  242 +
  243 +// 滚动条样式
  244 +.detail-content::-webkit-scrollbar {
  245 + width: 6px;
  246 +}
  247 +
  248 +.detail-content::-webkit-scrollbar-track {
  249 + background: #f1f1f1;
  250 + border-radius: 3px;
  251 +}
  252 +
  253 +.detail-content::-webkit-scrollbar-thumb {
  254 + background: #c1c1c1;
  255 + border-radius: 3px;
  256 +
  257 + &:hover {
  258 + background: #a8a8a8;
  259 + }
  260 +}
  261 +</style>
  262 +
... ...
antis-ncc-admin/src/views/wageManagement/headquarters-stock.vue
... ... @@ -8,8 +8,24 @@
8 8 </div>
9 9 <div class="header-right">
10 10 <el-button
  11 + type="success"
  12 + icon="el-icon-download"
  13 + @click="handleExport"
  14 + :loading="exportLoading"
  15 + >
  16 + 导出
  17 + </el-button>
  18 + <el-button
  19 + type="primary"
  20 + icon="el-icon-cpu"
  21 + @click="showCalculateDialog"
  22 + :loading="calculateLoading"
  23 + >
  24 + 计算股份
  25 + </el-button>
  26 + <el-button
11 27 icon="el-icon-refresh"
12   - @click="handleRefresh"
  28 + @click="getList"
13 29 :loading="loading"
14 30 >
15 31 刷新
... ... @@ -17,28 +33,382 @@
17 33 </div>
18 34 </div>
19 35  
20   - <!-- 内容区域 -->
21   - <div class="content-container">
22   - <!-- 待开发 -->
  36 + <!-- 搜索区域 -->
  37 + <div class="search-container">
  38 + <el-form :model="queryParams" ref="queryForm" :inline="true" label-width="80px">
  39 + <el-form-item label="统计月份" prop="statisticsMonth">
  40 + <el-date-picker
  41 + v-model="queryParams.statisticsMonth"
  42 + type="month"
  43 + placeholder="选择月份"
  44 + format="yyyyMM"
  45 + value-format="yyyyMM"
  46 + style="width: 200px"
  47 + />
  48 + </el-form-item>
  49 + <el-form-item>
  50 + <el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
  51 + <el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
  52 + </el-form-item>
  53 + </el-form>
23 54 </div>
  55 +
  56 + <!-- 数据表格 -->
  57 + <div class="table-container">
  58 + <NCC-table
  59 + v-loading="loading"
  60 + :data="list"
  61 + border
  62 + stripe
  63 + height="calc(100vh - 420px)"
  64 + :header-cell-style="{ background: '#f5f7fa', color: '#606266' }"
  65 + >
  66 + <!-- 基本信息 -->
  67 + <el-table-column prop="StatisticsMonth" label="统计月份" width="150" align="center" fixed="left">
  68 + <template slot-scope="scope">
  69 + <div class="month-name">
  70 + <i class="el-icon-date month-icon"></i>
  71 + <span>{{ formatMonth(scope.row.StatisticsMonth) || '无' }}</span>
  72 + </div>
  73 + </template>
  74 + </el-table-column>
  75 +
  76 + <!-- 收入信息 -->
  77 + <el-table-column label="收入信息" align="center">
  78 + <el-table-column prop="IncomeGeneral" label="收入-全部" width="140" align="center">
  79 + <template slot-scope="scope">
  80 + <span class="highlight">{{ formatMoney(scope.row.IncomeGeneral) }}</span>
  81 + </template>
  82 + </el-table-column>
  83 + <el-table-column prop="IncomeTechDept" label="收入-科技部" width="140" align="center">
  84 + <template slot-scope="scope">
  85 + <span class="highlight">{{ formatMoney(scope.row.IncomeTechDept) }}</span>
  86 + </template>
  87 + </el-table-column>
  88 + </el-table-column>
  89 +
  90 + <!-- 成本信息 -->
  91 + <el-table-column label="成本信息" align="center">
  92 + <el-table-column prop="CostReimbursement" label="成本-报销" width="140" align="center">
  93 + <template slot-scope="scope">
  94 + <span>{{ formatMoney(scope.row.CostReimbursement) }}</span>
  95 + </template>
  96 + </el-table-column>
  97 + <el-table-column prop="CostLabor" label="成本-人工" width="140" align="center">
  98 + <template slot-scope="scope">
  99 + <span>{{ formatMoney(scope.row.CostLabor) }}</span>
  100 + </template>
  101 + </el-table-column>
  102 + <el-table-column prop="CostEducationRent" label="成本-教育部房租" width="160" align="center">
  103 + <template slot-scope="scope">
  104 + <span>{{ formatMoney(scope.row.CostEducationRent) }}</span>
  105 + </template>
  106 + </el-table-column>
  107 + <el-table-column prop="CostWarehouseRent" label="成本-仓库房租" width="160" align="center">
  108 + <template slot-scope="scope">
  109 + <span>{{ formatMoney(scope.row.CostWarehouseRent) }}</span>
  110 + </template>
  111 + </el-table-column>
  112 + <el-table-column prop="CostHQRent" label="成本-总部房租" width="160" align="center">
  113 + <template slot-scope="scope">
  114 + <span>{{ formatMoney(scope.row.CostHQRent) }}</span>
  115 + </template>
  116 + </el-table-column>
  117 + </el-table-column>
  118 +
  119 + <!-- 利润结果 -->
  120 + <el-table-column label="利润结果" align="center">
  121 + <el-table-column prop="OperationalProfit" label="运营利润" width="140" align="center">
  122 + <template slot-scope="scope">
  123 + <span class="highlight profit">{{ formatMoney(scope.row.OperationalProfit) }}</span>
  124 + </template>
  125 + </el-table-column>
  126 + </el-table-column>
  127 +
  128 + <!-- 操作列 -->
  129 + <el-table-column label="操作" width="120" align="center" fixed="right">
  130 + <template slot-scope="scope">
  131 + <el-button
  132 + type="text"
  133 + size="mini"
  134 + icon="el-icon-view"
  135 + @click="showDetail(scope.row)"
  136 + >
  137 + 查看详情
  138 + </el-button>
  139 + </template>
  140 + </el-table-column>
  141 + </NCC-table>
  142 + </div>
  143 +
  144 + <!-- 计算股份弹窗 -->
  145 + <el-dialog
  146 + title="计算总部股份"
  147 + :visible="calculateDialogVisible"
  148 + width="500px"
  149 + :close-on-click-modal="false"
  150 + @close="handleCalculateDialogClose"
  151 + >
  152 + <el-form label-width="100px" label-position="right">
  153 + <el-form-item label="统计月份" required>
  154 + <el-date-picker
  155 + v-model="calculateMonth"
  156 + type="month"
  157 + placeholder="请选择月份"
  158 + format="yyyyMM"
  159 + value-format="yyyyMM"
  160 + style="width: 100%"
  161 + />
  162 + </el-form-item>
  163 + </el-form>
  164 + <div slot="footer" class="dialog-footer">
  165 + <el-button @click="handleCalculateDialogClose">取消</el-button>
  166 + <el-button
  167 + type="primary"
  168 + @click="handleCalculateConfirm"
  169 + :loading="calculateLoading"
  170 + >
  171 + 确定
  172 + </el-button>
  173 + </div>
  174 + </el-dialog>
  175 +
  176 + <!-- 详情弹窗 -->
  177 + <headquarters-stock-detail-dialog
  178 + :visible="detailVisible"
  179 + :detail-data="currentDetailData"
  180 + @close="handleDetailClose"
  181 + />
24 182 </div>
25 183 </template>
26 184  
27 185 <script>
  186 +import { getHeadquartersStockList, generateHeadquartersStock } from '@/api/extend/headquartersStock'
  187 +import HeadquartersStockDetailDialog from './headquarters-stock-detail-dialog.vue'
  188 +
28 189 export default {
29 190 name: 'HeadquartersStock',
  191 + components: {
  192 + HeadquartersStockDetailDialog
  193 + },
30 194 data() {
  195 + // 获取当前月份
  196 + const getCurrentMonth = () => {
  197 + const now = new Date()
  198 + const year = now.getFullYear()
  199 + const month = String(now.getMonth() + 1).padStart(2, '0')
  200 + return `${year}${month}`
  201 + }
  202 +
31 203 return {
32   - loading: false
  204 + loading: false,
  205 + calculateLoading: false,
  206 + exportLoading: false,
  207 + list: [],
  208 + calculateDialogVisible: false,
  209 + calculateMonth: getCurrentMonth(),
  210 + detailVisible: false,
  211 + currentDetailData: null,
  212 + queryParams: {
  213 + statisticsMonth: getCurrentMonth() // yyyyMM 格式
  214 + }
33 215 }
34 216 },
  217 + created() {
  218 + this.getList()
  219 + },
35 220 methods: {
36   - handleRefresh() {
  221 + // 获取列表数据
  222 + async getList() {
37 223 this.loading = true
38   - setTimeout(() => {
  224 + try {
  225 + const params = {
  226 + StatisticsMonth: this.queryParams.statisticsMonth || undefined
  227 + }
  228 +
  229 + const response = await getHeadquartersStockList(params)
  230 +
  231 + if (response.code === 200) {
  232 + this.list = response.data.data || []
  233 + } else {
  234 + this.$message.error(response.msg || '获取数据失败')
  235 + }
  236 + } catch (error) {
  237 + console.error('获取总部股份列表失败:', error)
  238 + this.$message.error('获取数据失败')
  239 + } finally {
39 240 this.loading = false
40   - this.$message.success('刷新成功')
41   - }, 500)
  241 + }
  242 + },
  243 +
  244 + // 搜索
  245 + handleQuery() {
  246 + this.getList()
  247 + },
  248 +
  249 + // 重置搜索
  250 + resetQuery() {
  251 + const getCurrentMonth = () => {
  252 + const now = new Date()
  253 + const year = now.getFullYear()
  254 + const month = String(now.getMonth() + 1).padStart(2, '0')
  255 + return `${year}${month}`
  256 + }
  257 +
  258 + this.queryParams = {
  259 + statisticsMonth: getCurrentMonth()
  260 + }
  261 + this.getList()
  262 + },
  263 +
  264 + // 显示计算股份弹窗
  265 + showCalculateDialog() {
  266 + const getCurrentMonth = () => {
  267 + const now = new Date()
  268 + const year = now.getFullYear()
  269 + const month = String(now.getMonth() + 1).padStart(2, '0')
  270 + return `${year}${month}`
  271 + }
  272 + this.calculateMonth = getCurrentMonth()
  273 + this.calculateDialogVisible = true
  274 + },
  275 +
  276 + // 关闭计算股份弹窗
  277 + handleCalculateDialogClose() {
  278 + this.calculateDialogVisible = false
  279 + },
  280 +
  281 + // 确认计算股份
  282 + async handleCalculateConfirm() {
  283 + if (!this.calculateMonth) {
  284 + this.$message.warning('请选择统计月份')
  285 + return
  286 + }
  287 +
  288 + this.calculateLoading = true
  289 + try {
  290 + const data = {
  291 + statisticsMonth: this.calculateMonth
  292 + }
  293 +
  294 + const response = await generateHeadquartersStock(data)
  295 + if (response.code === 200) {
  296 + this.$message.success('计算股份成功')
  297 + this.calculateDialogVisible = false
  298 + this.getList()
  299 + } else {
  300 + this.$message.error(response.msg || '计算股份失败')
  301 + }
  302 + } catch (error) {
  303 + console.error('计算股份失败:', error)
  304 + this.$message.error(error.message || '计算股份失败')
  305 + } finally {
  306 + this.calculateLoading = false
  307 + }
  308 + },
  309 +
  310 + // 显示详情
  311 + showDetail(row) {
  312 + this.currentDetailData = row
  313 + this.detailVisible = true
  314 + },
  315 +
  316 + // 关闭详情弹窗
  317 + handleDetailClose() {
  318 + this.detailVisible = false
  319 + this.currentDetailData = null
  320 + },
  321 +
  322 + // 导出功能
  323 + async handleExport() {
  324 + if (!this.list || this.list.length === 0) {
  325 + this.$message.warning('暂无数据可导出')
  326 + return
  327 + }
  328 +
  329 + this.exportLoading = true
  330 + try {
  331 + // 动态导入 xlsx 库
  332 + const XLSX = await import('xlsx')
  333 +
  334 + // 构建表头(根据后端DTO字段的中文映射)
  335 + const headers = [
  336 + '统计月份',
  337 + '收入-全部',
  338 + '收入-科技部',
  339 + '成本-报销',
  340 + '成本-人工',
  341 + '成本-教育部房租',
  342 + '成本-仓库房租',
  343 + '成本-总部房租',
  344 + '运营利润'
  345 + ]
  346 +
  347 + // 构建数据行
  348 + const dataRows = this.list.map(item => [
  349 + this.formatMonth(item.StatisticsMonth) || '无',
  350 + this.formatMoney(item.IncomeGeneral),
  351 + this.formatMoney(item.IncomeTechDept),
  352 + this.formatMoney(item.CostReimbursement),
  353 + this.formatMoney(item.CostLabor),
  354 + this.formatMoney(item.CostEducationRent),
  355 + this.formatMoney(item.CostWarehouseRent),
  356 + this.formatMoney(item.CostHQRent),
  357 + this.formatMoney(item.OperationalProfit)
  358 + ])
  359 +
  360 + // 合并表头和数据
  361 + const excelData = [headers, ...dataRows]
  362 +
  363 + // 创建工作表
  364 + const ws = XLSX.utils.aoa_to_sheet(excelData)
  365 +
  366 + // 设置列宽
  367 + const colWidths = headers.map(() => ({ wch: 15 }))
  368 + ws['!cols'] = colWidths
  369 +
  370 + // 创建工作簿
  371 + const wb = XLSX.utils.book_new()
  372 + XLSX.utils.book_append_sheet(wb, ws, '总部股份')
  373 +
  374 + // 生成文件名
  375 + const now = new Date()
  376 + const timestamp = now.getFullYear() +
  377 + String(now.getMonth() + 1).padStart(2, '0') +
  378 + String(now.getDate()).padStart(2, '0') +
  379 + String(now.getHours()).padStart(2, '0') +
  380 + String(now.getMinutes()).padStart(2, '0') +
  381 + String(now.getSeconds()).padStart(2, '0')
  382 + const fileName = `总部股份_${timestamp}.xlsx`
  383 +
  384 + // 导出文件
  385 + XLSX.writeFile(wb, fileName)
  386 +
  387 + this.$message.success('导出成功')
  388 + } catch (error) {
  389 + console.error('导出失败:', error)
  390 + this.$message.error('导出失败,请重试')
  391 + } finally {
  392 + this.exportLoading = false
  393 + }
  394 + },
  395 +
  396 + // 格式化金额
  397 + formatMoney(value) {
  398 + if (value === null || value === undefined || value === '') {
  399 + return '0.00'
  400 + }
  401 + return Number(value).toFixed(2)
  402 + },
  403 +
  404 + // 格式化月份
  405 + formatMonth(value) {
  406 + if (!value || value.length !== 6) {
  407 + return value || '无'
  408 + }
  409 + const year = value.substring(0, 4)
  410 + const month = value.substring(4, 6)
  411 + return `${year}-${month}`
42 412 }
43 413 }
44 414 }
... ... @@ -81,12 +451,58 @@ export default {
81 451 }
82 452 }
83 453  
84   -.content-container {
  454 +.search-container {
  455 + margin-bottom: 20px;
85 456 padding: 20px;
86 457 background: #fff;
87 458 border-radius: 8px;
88 459 box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
89   - min-height: 400px;
90 460 }
91   -</style>
92 461  
  462 +.table-container {
  463 + margin-bottom: 20px;
  464 + padding: 20px;
  465 + background: #fff;
  466 + border-radius: 8px;
  467 + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  468 +}
  469 +
  470 +.month-name {
  471 + display: flex;
  472 + align-items: center;
  473 + gap: 6px;
  474 +
  475 + .month-icon {
  476 + color: #409EFF;
  477 + font-size: 16px;
  478 + }
  479 +}
  480 +
  481 +.highlight {
  482 + color: #409EFF;
  483 + font-weight: 600;
  484 +}
  485 +
  486 +.profit {
  487 + color: #67C23A;
  488 +}
  489 +
  490 +// 表格样式优化
  491 +::v-deep .el-table {
  492 + .el-table__header-wrapper {
  493 + th {
  494 + background-color: #f5f7fa;
  495 + color: #606266;
  496 + font-weight: 600;
  497 + }
  498 + }
  499 +
  500 + .el-table__body-wrapper {
  501 + .el-table__row {
  502 + &:hover {
  503 + background-color: #f5f7fa;
  504 + }
  505 + }
  506 + }
  507 +}
  508 +</style>
... ...
antis-ncc-admin/src/views/wageManagement/major-project-director-detail-dialog.vue 0 → 100644
  1 +<template>
  2 + <el-dialog
  3 + title="大项目主管工资详情"
  4 + :visible="visible"
  5 + width="1400px"
  6 + :close-on-click-modal="false"
  7 + @close="handleClose"
  8 + class="detail-dialog"
  9 + >
  10 + <div v-if="detailData" class="detail-content">
  11 + <!-- 基本信息 -->
  12 + <div class="detail-section">
  13 + <div class="section-title">
  14 + <i class="el-icon-user"></i>
  15 + <span>基本信息</span>
  16 + </div>
  17 + <div class="section-content">
  18 + <div class="info-row">
  19 + <div class="info-item">
  20 + <span class="label">员工姓名:</span>
  21 + <span class="value">{{ detailData.EmployeeName || '无' }}</span>
  22 + </div>
  23 + <div class="info-item">
  24 + <span class="label">员工账号:</span>
  25 + <span class="value">{{ detailData.EmployeeAccount || '无' }}</span>
  26 + </div>
  27 + <div class="info-item">
  28 + <span class="label">核算岗位:</span>
  29 + <span class="value">{{ detailData.Position || '无' }}</span>
  30 + </div>
  31 + <div class="info-item">
  32 + <span class="label">统计月份:</span>
  33 + <span class="value">{{ detailData.StatisticsMonth || '无' }}</span>
  34 + </div>
  35 + <div class="info-item" v-if="detailData.IsTerminated !== undefined && detailData.IsTerminated !== null">
  36 + <span class="label">是否离职:</span>
  37 + <el-tag :type="detailData.IsTerminated === 1 ? 'danger' : 'success'" size="mini">
  38 + {{ detailData.IsTerminated === 1 ? '已离职' : '在职' }}
  39 + </el-tag>
  40 + </div>
  41 + <div class="info-item" v-if="detailData.IsLocked !== undefined && detailData.IsLocked !== null">
  42 + <span class="label">锁定状态:</span>
  43 + <el-tag :type="detailData.IsLocked === 1 ? 'danger' : 'success'" size="mini">
  44 + {{ detailData.IsLocked === 1 ? '已锁定' : '未锁定' }}
  45 + </el-tag>
  46 + </div>
  47 + </div>
  48 + </div>
  49 + </div>
  50 +
  51 + <!-- 业绩信息 -->
  52 + <div class="detail-section">
  53 + <div class="section-title">
  54 + <i class="el-icon-trophy"></i>
  55 + <span>业绩信息</span>
  56 + </div>
  57 + <div class="section-content">
  58 + <div class="info-row">
  59 + <div class="info-item">
  60 + <span class="label">开单金额:</span>
  61 + <span class="value">{{ formatMoney(detailData.BillingAmount) }}</span>
  62 + </div>
  63 + <div class="info-item">
  64 + <span class="label">退卡金额:</span>
  65 + <span class="value">{{ formatMoney(detailData.RefundAmount) }}</span>
  66 + </div>
  67 + <div class="info-item">
  68 + <span class="label">总业绩:</span>
  69 + <span class="value highlight">{{ formatMoney(detailData.TotalPerformance) }}</span>
  70 + </div>
  71 + </div>
  72 + </div>
  73 + </div>
  74 +
  75 + <!-- 提成信息 -->
  76 + <div class="detail-section">
  77 + <div class="section-title">
  78 + <i class="el-icon-money"></i>
  79 + <span>提成信息</span>
  80 + </div>
  81 + <div class="section-content">
  82 + <div class="info-row">
  83 + <div class="info-item">
  84 + <span class="label">提成比例:</span>
  85 + <span class="value">{{ formatPercent(detailData.CommissionRate) }}</span>
  86 + </div>
  87 + <div class="info-item">
  88 + <span class="label">提成金额:</span>
  89 + <span class="value highlight">{{ formatMoney(detailData.CommissionAmount) }}</span>
  90 + </div>
  91 + </div>
  92 + </div>
  93 + </div>
  94 +
  95 + <!-- 工资信息 -->
  96 + <div class="detail-section">
  97 + <div class="section-title">
  98 + <i class="el-icon-wallet"></i>
  99 + <span>工资信息</span>
  100 + </div>
  101 + <div class="section-content">
  102 + <div class="info-row">
  103 + <div class="info-item">
  104 + <span class="label">底薪:</span>
  105 + <span class="value">{{ formatMoney(detailData.BaseSalary) }}</span>
  106 + </div>
  107 + <div class="info-item">
  108 + <span class="label">在店天数:</span>
  109 + <span class="value">{{ detailData.WorkingDays || '0' }}</span>
  110 + </div>
  111 + <div class="info-item">
  112 + <span class="label">请假天数:</span>
  113 + <span class="value">{{ detailData.LeaveDays || '0' }}</span>
  114 + </div>
  115 + <div class="info-item">
  116 + <span class="label">核算应发工资:</span>
  117 + <span class="value highlight">{{ formatMoney(detailData.CalculatedGrossSalary) }}</span>
  118 + </div>
  119 + <div class="info-item">
  120 + <span class="label">最终应发工资:</span>
  121 + <span class="value highlight">{{ formatMoney(detailData.FinalGrossSalary) }}</span>
  122 + </div>
  123 + </div>
  124 + </div>
  125 + </div>
  126 +
  127 + <!-- 补贴信息 -->
  128 + <div class="detail-section">
  129 + <div class="section-title">
  130 + <i class="el-icon-circle-plus"></i>
  131 + <span>补贴信息</span>
  132 + </div>
  133 + <div class="section-content">
  134 + <div class="info-row">
  135 + <div class="info-item">
  136 + <span class="label">当月培训补贴:</span>
  137 + <span class="value">{{ formatMoney(detailData.MonthlyTrainingSubsidy) }}</span>
  138 + </div>
  139 + <div class="info-item">
  140 + <span class="label">当月交通补贴:</span>
  141 + <span class="value">{{ formatMoney(detailData.MonthlyTransportSubsidy) }}</span>
  142 + </div>
  143 + <div class="info-item">
  144 + <span class="label">上月培训补贴:</span>
  145 + <span class="value">{{ formatMoney(detailData.LastMonthTrainingSubsidy) }}</span>
  146 + </div>
  147 + <div class="info-item">
  148 + <span class="label">上月交通补贴:</span>
  149 + <span class="value">{{ formatMoney(detailData.LastMonthTransportSubsidy) }}</span>
  150 + </div>
  151 + <div class="info-item">
  152 + <span class="label">补贴合计:</span>
  153 + <span class="value highlight">{{ formatMoney(detailData.TotalSubsidy) }}</span>
  154 + </div>
  155 + </div>
  156 + </div>
  157 + </div>
  158 +
  159 + <!-- 扣款信息 -->
  160 + <div class="detail-section">
  161 + <div class="section-title">
  162 + <i class="el-icon-circle-minus"></i>
  163 + <span>扣款信息</span>
  164 + </div>
  165 + <div class="section-content">
  166 + <div class="info-row">
  167 + <div class="info-item">
  168 + <span class="label">缺卡扣款:</span>
  169 + <span class="value">{{ formatMoney(detailData.MissingCard) }}</span>
  170 + </div>
  171 + <div class="info-item">
  172 + <span class="label">迟到扣款:</span>
  173 + <span class="value">{{ formatMoney(detailData.LateArrival) }}</span>
  174 + </div>
  175 + <div class="info-item">
  176 + <span class="label">请假扣款:</span>
  177 + <span class="value">{{ formatMoney(detailData.LeaveDeduction) }}</span>
  178 + </div>
  179 + <div class="info-item">
  180 + <span class="label">扣社保:</span>
  181 + <span class="value">{{ formatMoney(detailData.SocialInsuranceDeduction) }}</span>
  182 + </div>
  183 + <div class="info-item">
  184 + <span class="label">扣除奖励:</span>
  185 + <span class="value">{{ formatMoney(detailData.RewardDeduction) }}</span>
  186 + </div>
  187 + <div class="info-item">
  188 + <span class="label">扣住宿费:</span>
  189 + <span class="value">{{ formatMoney(detailData.AccommodationDeduction) }}</span>
  190 + </div>
  191 + <div class="info-item">
  192 + <span class="label">扣学习期费用:</span>
  193 + <span class="value">{{ formatMoney(detailData.StudyPeriodDeduction) }}</span>
  194 + </div>
  195 + <div class="info-item">
  196 + <span class="label">扣工作服费用:</span>
  197 + <span class="value">{{ formatMoney(detailData.WorkClothesDeduction) }}</span>
  198 + </div>
  199 + <div class="info-item">
  200 + <span class="label">扣款合计:</span>
  201 + <span class="value highlight">{{ formatMoney(detailData.TotalDeduction) }}</span>
  202 + </div>
  203 + </div>
  204 + </div>
  205 + </div>
  206 +
  207 + <!-- 其他信息 -->
  208 + <div class="detail-section">
  209 + <div class="section-title">
  210 + <i class="el-icon-document"></i>
  211 + <span>其他信息</span>
  212 + </div>
  213 + <div class="section-content">
  214 + <div class="info-row">
  215 + <div class="info-item">
  216 + <span class="label">发奖金:</span>
  217 + <span class="value">{{ formatMoney(detailData.Bonus) }}</span>
  218 + </div>
  219 + <div class="info-item">
  220 + <span class="label">退手机押金:</span>
  221 + <span class="value">{{ formatMoney(detailData.ReturnPhoneDeposit) }}</span>
  222 + </div>
  223 + <div class="info-item">
  224 + <span class="label">退住宿押金:</span>
  225 + <span class="value">{{ formatMoney(detailData.ReturnAccommodationDeposit) }}</span>
  226 + </div>
  227 + <div class="info-item">
  228 + <span class="label">补发上月:</span>
  229 + <span class="value">{{ formatMoney(detailData.LastMonthSupplement) }}</span>
  230 + </div>
  231 + </div>
  232 + </div>
  233 + </div>
  234 +
  235 + <!-- 支付信息 -->
  236 + <div class="detail-section">
  237 + <div class="section-title">
  238 + <i class="el-icon-credit-card"></i>
  239 + <span>支付信息</span>
  240 + </div>
  241 + <div class="section-content">
  242 + <div class="info-row">
  243 + <div class="info-item">
  244 + <span class="label">实发工资:</span>
  245 + <span class="value highlight large">{{ formatMoney(detailData.ActualSalary) }}</span>
  246 + </div>
  247 + <div class="info-item">
  248 + <span class="label">当月是否发放:</span>
  249 + <span class="value">{{ detailData.MonthlyPaymentStatus || '无' }}</span>
  250 + </div>
  251 + <div class="info-item">
  252 + <span class="label">支付金额:</span>
  253 + <span class="value">{{ formatMoney(detailData.PaidAmount) }}</span>
  254 + </div>
  255 + <div class="info-item">
  256 + <span class="label">待支付金额:</span>
  257 + <span class="value">{{ formatMoney(detailData.PendingAmount) }}</span>
  258 + </div>
  259 + <div class="info-item">
  260 + <span class="label">当月支付总额:</span>
  261 + <span class="value highlight">{{ formatMoney(detailData.MonthlyTotalPayment) }}</span>
  262 + </div>
  263 + </div>
  264 + </div>
  265 + </div>
  266 + </div>
  267 +
  268 + <div v-else class="no-data">
  269 + <i class="el-icon-warning"></i>
  270 + <span>暂无数据</span>
  271 + </div>
  272 + </el-dialog>
  273 +</template>
  274 +
  275 +<script>
  276 +export default {
  277 + name: 'MajorProjectDirectorDetailDialog',
  278 + props: {
  279 + visible: {
  280 + type: Boolean,
  281 + default: false
  282 + },
  283 + detailData: {
  284 + type: Object,
  285 + default: null
  286 + }
  287 + },
  288 + methods: {
  289 + handleClose() {
  290 + this.$emit('update:visible', false)
  291 + this.$emit('close')
  292 + },
  293 + formatMoney(value) {
  294 + if (value === null || value === undefined || value === '') {
  295 + return '0.00'
  296 + }
  297 + return Number(value).toFixed(2)
  298 + },
  299 + formatPercent(value) {
  300 + if (value === null || value === undefined || value === '') {
  301 + return '0.00%'
  302 + }
  303 + return (Number(value) * 100).toFixed(2) + '%'
  304 + }
  305 + }
  306 +}
  307 +</script>
  308 +
  309 +<style lang="scss" scoped>
  310 +.detail-dialog {
  311 + .detail-content {
  312 + max-height: 70vh;
  313 + overflow-y: auto;
  314 + padding-right: 10px;
  315 + }
  316 +
  317 + .detail-section {
  318 + margin-bottom: 20px;
  319 + background: #fff;
  320 + border-radius: 8px;
  321 + padding: 20px;
  322 + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  323 +
  324 + .section-title {
  325 + display: flex;
  326 + align-items: center;
  327 + gap: 8px;
  328 + margin-bottom: 16px;
  329 + padding-bottom: 12px;
  330 + border-bottom: 2px solid #409EFF;
  331 + font-size: 16px;
  332 + font-weight: 600;
  333 + color: #303133;
  334 +
  335 + i {
  336 + color: #409EFF;
  337 + font-size: 18px;
  338 + }
  339 + }
  340 +
  341 + .section-content {
  342 + .info-row {
  343 + display: flex;
  344 + flex-wrap: wrap;
  345 + gap: 20px 40px;
  346 +
  347 + .info-item {
  348 + display: flex;
  349 + align-items: center;
  350 + min-width: 200px;
  351 + flex: 0 0 calc(33.333% - 27px);
  352 +
  353 + .label {
  354 + color: #606266;
  355 + font-size: 14px;
  356 + white-space: nowrap;
  357 + margin-right: 8px;
  358 + }
  359 +
  360 + .value {
  361 + color: #303133;
  362 + font-size: 14px;
  363 + font-weight: 500;
  364 +
  365 + &.highlight {
  366 + color: #409EFF;
  367 + font-weight: 600;
  368 + }
  369 +
  370 + &.large {
  371 + font-size: 18px;
  372 + color: #67C23A;
  373 + }
  374 + }
  375 + }
  376 + }
  377 + }
  378 + }
  379 +
  380 + .no-data {
  381 + display: flex;
  382 + flex-direction: column;
  383 + align-items: center;
  384 + justify-content: center;
  385 + padding: 60px 20px;
  386 + color: #909399;
  387 +
  388 + i {
  389 + font-size: 48px;
  390 + margin-bottom: 16px;
  391 + }
  392 +
  393 + span {
  394 + font-size: 14px;
  395 + }
  396 + }
  397 +}
  398 +
  399 +// 滚动条样式
  400 +.detail-content::-webkit-scrollbar {
  401 + width: 6px;
  402 +}
  403 +
  404 +.detail-content::-webkit-scrollbar-track {
  405 + background: #f1f1f1;
  406 + border-radius: 3px;
  407 +}
  408 +
  409 +.detail-content::-webkit-scrollbar-thumb {
  410 + background: #c1c1c1;
  411 + border-radius: 3px;
  412 +
  413 + &:hover {
  414 + background: #a8a8a8;
  415 + }
  416 +}
  417 +</style>
  418 +
... ...
antis-ncc-admin/src/views/wageManagement/major-project-directors.vue
... ... @@ -8,6 +8,14 @@
8 8 </div>
9 9 <div class="header-right">
10 10 <el-button
  11 + type="success"
  12 + icon="el-icon-download"
  13 + @click="handleExport"
  14 + :loading="exportLoading"
  15 + >
  16 + 导出
  17 + </el-button>
  18 + <el-button
11 19 type="primary"
12 20 icon="el-icon-cpu"
13 21 @click="showCalculateDialog"
... ... @@ -289,18 +297,27 @@
289 297 </el-table-column>
290 298 </el-table-column>
291 299  
292   - <!-- 其他字段 -->
293   - <el-table-column prop="StoreDetail" label="门店明细" width="200" align="center" fixed="right">
  300 + <!-- 操作列 -->
  301 + <el-table-column label="操作" width="200" align="center" fixed="right">
294 302 <template slot-scope="scope">
295 303 <el-button
296 304 type="text"
297 305 size="mini"
  306 + icon="el-icon-view"
  307 + @click="showDetail(scope.row)"
  308 + >
  309 + 查看详情
  310 + </el-button>
  311 + <el-button
  312 + type="text"
  313 + size="mini"
  314 + icon="el-icon-document"
298 315 @click="showStoreDetail(scope.row)"
299 316 v-if="scope.row.StoreDetail"
  317 + style="margin-left: 8px;"
300 318 >
301   - 查看明细
  319 + 门店明细
302 320 </el-button>
303   - <span v-else>无</span>
304 321 </template>
305 322 </el-table-column>
306 323 </NCC-table>
... ... @@ -348,6 +365,13 @@
348 365 </div>
349 366 </el-dialog>
350 367  
  368 + <!-- 详情弹窗 -->
  369 + <major-project-director-detail-dialog
  370 + :visible="detailVisible"
  371 + :detail-data="currentDetailData"
  372 + @close="handleDetailClose"
  373 + />
  374 +
351 375 <!-- 门店明细弹窗 -->
352 376 <el-dialog
353 377 title="门店明细"
... ... @@ -382,9 +406,13 @@
382 406  
383 407 <script>
384 408 import { getMajorProjectDirectorSalaryList, calculateMajorProjectDirectorSalary } from '@/api/extend/majorProjectDirectorSalary'
  409 +import MajorProjectDirectorDetailDialog from './major-project-director-detail-dialog.vue'
385 410  
386 411 export default {
387 412 name: 'MajorProjectDirectorSalary',
  413 + components: {
  414 + MajorProjectDirectorDetailDialog
  415 + },
388 416 data() {
389 417 // 获取当前年月
390 418 const getCurrentYear = () => {
... ... @@ -400,11 +428,14 @@ export default {
400 428 return {
401 429 loading: false,
402 430 calculateLoading: false,
  431 + exportLoading: false,
403 432 list: [],
404 433 total: 0,
405 434 calculateDialogVisible: false,
406 435 calculateYear: getCurrentYear(),
407 436 calculateMonth: getCurrentMonth(),
  437 + detailVisible: false,
  438 + currentDetailData: null,
408 439 storeDetailVisible: false,
409 440 storeDetailList: [],
410 441 queryParams: {
... ... @@ -575,6 +606,152 @@ export default {
575 606 this.storeDetailList = []
576 607 },
577 608  
  609 + // 显示详情
  610 + showDetail(row) {
  611 + this.currentDetailData = row
  612 + this.detailVisible = true
  613 + },
  614 +
  615 + // 关闭详情弹窗
  616 + handleDetailClose() {
  617 + this.detailVisible = false
  618 + this.currentDetailData = null
  619 + },
  620 +
  621 + // 导出功能
  622 + async handleExport() {
  623 + if (!this.list || this.list.length === 0) {
  624 + this.$message.warning('暂无数据可导出')
  625 + return
  626 + }
  627 +
  628 + this.exportLoading = true
  629 + try {
  630 + // 动态导入 xlsx 库
  631 + const XLSX = await import('xlsx')
  632 +
  633 + // 构建表头(根据后端DTO字段的中文映射)
  634 + const headers = [
  635 + '员工姓名',
  636 + '员工账号',
  637 + '核算岗位',
  638 + '统计月份',
  639 + '是否离职',
  640 + '开单金额',
  641 + '退卡金额',
  642 + '总业绩',
  643 + '底薪',
  644 + '提成比例',
  645 + '提成金额',
  646 + '在店天数',
  647 + '请假天数',
  648 + '核算应发工资',
  649 + '最终应发工资',
  650 + '当月培训补贴',
  651 + '当月交通补贴',
  652 + '上月培训补贴',
  653 + '上月交通补贴',
  654 + '补贴合计',
  655 + '缺卡扣款',
  656 + '迟到扣款',
  657 + '请假扣款',
  658 + '扣社保',
  659 + '扣除奖励',
  660 + '扣住宿费',
  661 + '扣学习期费用',
  662 + '扣工作服费用',
  663 + '扣款合计',
  664 + '发奖金',
  665 + '退手机押金',
  666 + '退住宿押金',
  667 + '补发上月',
  668 + '实发工资',
  669 + '当月是否发放',
  670 + '支付金额',
  671 + '待支付金额',
  672 + '当月支付总额',
  673 + '是否锁定'
  674 + ]
  675 +
  676 + // 构建数据行
  677 + const dataRows = this.list.map(item => [
  678 + item.EmployeeName || '无',
  679 + item.EmployeeAccount || '无',
  680 + item.Position || '无',
  681 + item.StatisticsMonth || '无',
  682 + item.IsTerminated === 1 ? '已离职' : '在职',
  683 + this.formatMoney(item.BillingAmount),
  684 + this.formatMoney(item.RefundAmount),
  685 + this.formatMoney(item.TotalPerformance),
  686 + this.formatMoney(item.BaseSalary),
  687 + this.formatPercent(item.CommissionRate),
  688 + this.formatMoney(item.CommissionAmount),
  689 + item.WorkingDays || '0',
  690 + item.LeaveDays || '0',
  691 + this.formatMoney(item.CalculatedGrossSalary),
  692 + this.formatMoney(item.FinalGrossSalary),
  693 + this.formatMoney(item.MonthlyTrainingSubsidy),
  694 + this.formatMoney(item.MonthlyTransportSubsidy),
  695 + this.formatMoney(item.LastMonthTrainingSubsidy),
  696 + this.formatMoney(item.LastMonthTransportSubsidy),
  697 + this.formatMoney(item.TotalSubsidy),
  698 + this.formatMoney(item.MissingCard),
  699 + this.formatMoney(item.LateArrival),
  700 + this.formatMoney(item.LeaveDeduction),
  701 + this.formatMoney(item.SocialInsuranceDeduction),
  702 + this.formatMoney(item.RewardDeduction),
  703 + this.formatMoney(item.AccommodationDeduction),
  704 + this.formatMoney(item.StudyPeriodDeduction),
  705 + this.formatMoney(item.WorkClothesDeduction),
  706 + this.formatMoney(item.TotalDeduction),
  707 + this.formatMoney(item.Bonus),
  708 + this.formatMoney(item.ReturnPhoneDeposit),
  709 + this.formatMoney(item.ReturnAccommodationDeposit),
  710 + this.formatMoney(item.LastMonthSupplement),
  711 + this.formatMoney(item.ActualSalary),
  712 + item.MonthlyPaymentStatus || '无',
  713 + this.formatMoney(item.PaidAmount),
  714 + this.formatMoney(item.PendingAmount),
  715 + this.formatMoney(item.MonthlyTotalPayment),
  716 + item.IsLocked === 1 ? '已锁定' : '未锁定'
  717 + ])
  718 +
  719 + // 合并表头和数据
  720 + const excelData = [headers, ...dataRows]
  721 +
  722 + // 创建工作表
  723 + const ws = XLSX.utils.aoa_to_sheet(excelData)
  724 +
  725 + // 设置列宽
  726 + const colWidths = headers.map(() => ({ wch: 15 }))
  727 + ws['!cols'] = colWidths
  728 +
  729 + // 创建工作簿
  730 + const wb = XLSX.utils.book_new()
  731 + XLSX.utils.book_append_sheet(wb, ws, '大项目主管工资')
  732 +
  733 + // 生成文件名
  734 + const now = new Date()
  735 + const timestamp = now.getFullYear() +
  736 + String(now.getMonth() + 1).padStart(2, '0') +
  737 + String(now.getDate()).padStart(2, '0') +
  738 + String(now.getHours()).padStart(2, '0') +
  739 + String(now.getMinutes()).padStart(2, '0') +
  740 + String(now.getSeconds()).padStart(2, '0')
  741 + const fileName = `大项目主管工资_${timestamp}.xlsx`
  742 +
  743 + // 导出文件
  744 + XLSX.writeFile(wb, fileName)
  745 +
  746 + this.$message.success('导出成功')
  747 + } catch (error) {
  748 + console.error('导出失败:', error)
  749 + this.$message.error('导出失败,请重试')
  750 + } finally {
  751 + this.exportLoading = false
  752 + }
  753 + },
  754 +
578 755 // 格式化金额
579 756 formatMoney(value) {
580 757 if (value === null || value === undefined || value === '') {
... ...
antis-ncc-admin/src/views/wageManagement/shop-stock-detail-dialog.vue 0 → 100644
  1 +<template>
  2 + <el-dialog
  3 + title="门店股份详情"
  4 + :visible="visible"
  5 + width="1400px"
  6 + :close-on-click-modal="false"
  7 + @close="handleClose"
  8 + class="detail-dialog"
  9 + >
  10 + <div v-if="detailData" class="detail-content">
  11 + <!-- 基本信息 -->
  12 + <div class="detail-section">
  13 + <div class="section-title">
  14 + <i class="el-icon-user"></i>
  15 + <span>基本信息</span>
  16 + </div>
  17 + <div class="section-content">
  18 + <div class="info-row">
  19 + <div class="info-item">
  20 + <span class="label">门店名称:</span>
  21 + <span class="value">{{ detailData.StoreName || '无' }}</span>
  22 + </div>
  23 + <div class="info-item">
  24 + <span class="label">统计月份:</span>
  25 + <span class="value">{{ formatMonth(detailData.StatisticsMonth) || '无' }}</span>
  26 + </div>
  27 + <div class="info-item" v-if="detailData.CreateTime">
  28 + <span class="label">创建时间:</span>
  29 + <span class="value">{{ formatDateTime(detailData.CreateTime) }}</span>
  30 + </div>
  31 + <div class="info-item" v-if="detailData.UpdateTime">
  32 + <span class="label">更新时间:</span>
  33 + <span class="value">{{ formatDateTime(detailData.UpdateTime) }}</span>
  34 + </div>
  35 + </div>
  36 + </div>
  37 + </div>
  38 +
  39 + <!-- 收入信息 -->
  40 + <div class="detail-section">
  41 + <div class="section-title">
  42 + <i class="el-icon-trophy"></i>
  43 + <span>收入信息</span>
  44 + </div>
  45 + <div class="section-content">
  46 + <div class="info-row">
  47 + <div class="info-item">
  48 + <span class="label">主营收入:</span>
  49 + <span class="value highlight">{{ formatMoney(detailData.MainIncome) }}</span>
  50 + </div>
  51 + <div class="info-item">
  52 + <span class="label">生美消耗业绩:</span>
  53 + <span class="value">{{ formatMoney(detailData.ConsumeLifeBeauty) }}</span>
  54 + </div>
  55 + <div class="info-item">
  56 + <span class="label">科美消耗业绩:</span>
  57 + <span class="value">{{ formatMoney(detailData.ConsumeTechBeauty) }}</span>
  58 + </div>
  59 + <div class="info-item">
  60 + <span class="label">医美消耗业绩:</span>
  61 + <span class="value">{{ formatMoney(detailData.ConsumeMedicalBeauty) }}</span>
  62 + </div>
  63 + <div class="info-item">
  64 + <span class="label">合作消费业绩:</span>
  65 + <span class="value">{{ formatMoney(detailData.ConsumeCooperation) }}</span>
  66 + </div>
  67 + <div class="info-item">
  68 + <span class="label">产品消耗业绩:</span>
  69 + <span class="value">{{ formatMoney(detailData.ConsumeProduct) }}</span>
  70 + </div>
  71 + <div class="info-item">
  72 + <span class="label">其他消耗业绩:</span>
  73 + <span class="value">{{ formatMoney(detailData.ConsumeOther) }}</span>
  74 + </div>
  75 + <div class="info-item">
  76 + <span class="label">其他收入:</span>
  77 + <span class="value">{{ formatMoney(detailData.OtherIncome) }}</span>
  78 + </div>
  79 + <div class="info-item">
  80 + <span class="label">预收款:</span>
  81 + <span class="value">{{ formatMoney(detailData.AdvanceReceipt) }}</span>
  82 + </div>
  83 + <div class="info-item">
  84 + <span class="label">实际退款:</span>
  85 + <span class="value">{{ formatMoney(detailData.Refund) }}</span>
  86 + </div>
  87 + <div class="info-item">
  88 + <span class="label">应收:</span>
  89 + <span class="value">{{ formatMoney(detailData.Receivables) }}</span>
  90 + </div>
  91 + <div class="info-item">
  92 + <span class="label">银行存款:</span>
  93 + <span class="value">{{ formatMoney(detailData.BankDeposit) }}</span>
  94 + </div>
  95 + </div>
  96 + </div>
  97 + </div>
  98 +
  99 + <!-- 成本信息 -->
  100 + <div class="detail-section">
  101 + <div class="section-title">
  102 + <i class="el-icon-shopping-cart-2"></i>
  103 + <span>成本信息</span>
  104 + </div>
  105 + <div class="section-content">
  106 + <div class="info-row">
  107 + <div class="info-item">
  108 + <span class="label">主营成本-产品:</span>
  109 + <span class="value">{{ formatMoney(detailData.CostProduct) }}</span>
  110 + </div>
  111 + <div class="info-item">
  112 + <span class="label">主营成本-福田:</span>
  113 + <span class="value">{{ formatMoney(detailData.CostFutian) }}</span>
  114 + </div>
  115 + <div class="info-item">
  116 + <span class="label">主营成本-毛巾:</span>
  117 + <span class="value">{{ formatMoney(detailData.CostTowel) }}</span>
  118 + </div>
  119 + <div class="info-item">
  120 + <span class="label">主营成本-科技部:</span>
  121 + <span class="value">{{ formatMoney(detailData.CostTechDept) }}</span>
  122 + </div>
  123 + <div class="info-item">
  124 + <span class="label">主营成本-管理费:</span>
  125 + <span class="value">{{ formatMoney(detailData.CostManagementFee) }}</span>
  126 + </div>
  127 + <div class="info-item">
  128 + <span class="label">主营成本-合作:</span>
  129 + <span class="value">{{ formatMoney(detailData.CostCooperation) }}</span>
  130 + </div>
  131 + <div class="info-item">
  132 + <span class="label">主营成本-大项目:</span>
  133 + <span class="value">{{ formatMoney(detailData.CostMajorProject) }}</span>
  134 + </div>
  135 + <div class="info-item">
  136 + <span class="label">其他成本:</span>
  137 + <span class="value">{{ formatMoney(detailData.CostOther) }}</span>
  138 + </div>
  139 + </div>
  140 + </div>
  141 + </div>
  142 +
  143 + <!-- 人工工资信息 -->
  144 + <div class="detail-section">
  145 + <div class="section-title">
  146 + <i class="el-icon-wallet"></i>
  147 + <span>人工工资信息</span>
  148 + </div>
  149 + <div class="section-content">
  150 + <div class="info-row">
  151 + <div class="info-item">
  152 + <span class="label">健康师底薪:</span>
  153 + <span class="value">{{ formatMoney(detailData.SalaryBaseHealthCoach) }}</span>
  154 + </div>
  155 + <div class="info-item">
  156 + <span class="label">店助底薪:</span>
  157 + <span class="value">{{ formatMoney(detailData.SalaryBaseAssistant) }}</span>
  158 + </div>
  159 + <div class="info-item">
  160 + <span class="label">店助主任底薪:</span>
  161 + <span class="value">{{ formatMoney(detailData.SalaryBaseDirector) }}</span>
  162 + </div>
  163 + <div class="info-item">
  164 + <span class="label">店长底薪:</span>
  165 + <span class="value">{{ formatMoney(detailData.SalaryBaseStoreManager) }}</span>
  166 + </div>
  167 + <div class="info-item">
  168 + <span class="label">总经理/经理底薪:</span>
  169 + <span class="value">{{ formatMoney(detailData.SalaryBaseGeneralManager) }}</span>
  170 + </div>
  171 + <div class="info-item">
  172 + <span class="label">健康师提成:</span>
  173 + <span class="value">{{ formatMoney(detailData.SalaryCommissionHealthCoach) }}</span>
  174 + </div>
  175 + <div class="info-item">
  176 + <span class="label">店助提成:</span>
  177 + <span class="value">{{ formatMoney(detailData.SalaryCommissionAssistant) }}</span>
  178 + </div>
  179 + <div class="info-item">
  180 + <span class="label">店助主任提成:</span>
  181 + <span class="value">{{ formatMoney(detailData.SalaryCommissionDirector) }}</span>
  182 + </div>
  183 + <div class="info-item">
  184 + <span class="label">店长提成:</span>
  185 + <span class="value">{{ formatMoney(detailData.SalaryCommissionStoreManager) }}</span>
  186 + </div>
  187 + <div class="info-item">
  188 + <span class="label">总经理/经理提成:</span>
  189 + <span class="value">{{ formatMoney(detailData.SalaryCommissionGeneralManager) }}</span>
  190 + </div>
  191 + <div class="info-item">
  192 + <span class="label">手工:</span>
  193 + <span class="value">{{ formatMoney(detailData.SalaryManual) }}</span>
  194 + </div>
  195 + <div class="info-item">
  196 + <span class="label">出勤:</span>
  197 + <span class="value">{{ formatMoney(detailData.SalaryAttendance) }}</span>
  198 + </div>
  199 + <div class="info-item">
  200 + <span class="label">手机保管费:</span>
  201 + <span class="value">{{ formatMoney(detailData.SalaryPhoneCustody) }}</span>
  202 + </div>
  203 + <div class="info-item">
  204 + <span class="label">人头:</span>
  205 + <span class="value">{{ formatMoney(detailData.SalaryHeadcountReward) }}</span>
  206 + </div>
  207 + <div class="info-item">
  208 + <span class="label">门店T区:</span>
  209 + <span class="value">{{ formatMoney(detailData.SalaryTZone) }}</span>
  210 + </div>
  211 + </div>
  212 + </div>
  213 + </div>
  214 +
  215 + <!-- 费用与其他 -->
  216 + <div class="detail-section">
  217 + <div class="section-title">
  218 + <i class="el-icon-money"></i>
  219 + <span>费用与其他</span>
  220 + </div>
  221 + <div class="section-content">
  222 + <div class="info-row">
  223 + <div class="info-item">
  224 + <span class="label">社保:</span>
  225 + <span class="value">{{ formatMoney(detailData.SocialSecurity) }}</span>
  226 + </div>
  227 + <div class="info-item">
  228 + <span class="label">门店房租:</span>
  229 + <span class="value">{{ formatMoney(detailData.StoreRent) }}</span>
  230 + </div>
  231 + <div class="info-item">
  232 + <span class="label">宿舍房租:</span>
  233 + <span class="value">{{ formatMoney(detailData.DormRent) }}</span>
  234 + </div>
  235 + <div class="info-item">
  236 + <span class="label">当期费用:</span>
  237 + <span class="value">{{ formatMoney(detailData.CurrentPeriodExpense) }}</span>
  238 + </div>
  239 + <div class="info-item">
  240 + <span class="label">当前费用-秦董:</span>
  241 + <span class="value">{{ formatMoney(detailData.CurrentPeriodExpenseQin) }}</span>
  242 + </div>
  243 + <div class="info-item">
  244 + <span class="label">财务费用:</span>
  245 + <span class="value">{{ formatMoney(detailData.FinancialFee) }}</span>
  246 + </div>
  247 + </div>
  248 + </div>
  249 + </div>
  250 +
  251 + <!-- 奖励 -->
  252 + <div class="detail-section">
  253 + <div class="section-title">
  254 + <i class="el-icon-medal"></i>
  255 + <span>奖励</span>
  256 + </div>
  257 + <div class="section-content">
  258 + <div class="info-row">
  259 + <div class="info-item">
  260 + <span class="label">奖励-医美:</span>
  261 + <span class="value">{{ formatMoney(detailData.RewardMedicalBeauty) }}</span>
  262 + </div>
  263 + <div class="info-item">
  264 + <span class="label">奖励-其他:</span>
  265 + <span class="value">{{ formatMoney(detailData.RewardOther) }}</span>
  266 + </div>
  267 + <div class="info-item">
  268 + <span class="label">奖励-大单奖:</span>
  269 + <span class="value">{{ formatMoney(detailData.RewardLargeOrder) }}</span>
  270 + </div>
  271 + <div class="info-item">
  272 + <span class="label">奖励-首单奖:</span>
  273 + <span class="value">{{ formatMoney(detailData.RewardFirstOrder) }}</span>
  274 + </div>
  275 + <div class="info-item">
  276 + <span class="label">奖励:</span>
  277 + <span class="value">{{ formatMoney(detailData.RewardGeneral) }}</span>
  278 + </div>
  279 + </div>
  280 + </div>
  281 + </div>
  282 +
  283 + <!-- 其他保留字段 -->
  284 + <div class="detail-section">
  285 + <div class="section-title">
  286 + <i class="el-icon-document"></i>
  287 + <span>其他</span>
  288 + </div>
  289 + <div class="section-content">
  290 + <div class="info-row">
  291 + <div class="info-item">
  292 + <span class="label">其他1:</span>
  293 + <span class="value">{{ formatMoney(detailData.Other1) }}</span>
  294 + </div>
  295 + <div class="info-item">
  296 + <span class="label">其他2:</span>
  297 + <span class="value">{{ formatMoney(detailData.Other2) }}</span>
  298 + </div>
  299 + </div>
  300 + </div>
  301 + </div>
  302 +
  303 + <!-- 利润结果 -->
  304 + <div class="detail-section">
  305 + <div class="section-title">
  306 + <i class="el-icon-data-line"></i>
  307 + <span>利润结果</span>
  308 + </div>
  309 + <div class="section-content">
  310 + <div class="info-row">
  311 + <div class="info-item">
  312 + <span class="label">利润:</span>
  313 + <span class="value highlight large">{{ formatMoney(detailData.Profit) }}</span>
  314 + </div>
  315 + <div class="info-item">
  316 + <span class="label">财务利润:</span>
  317 + <span class="value highlight large">{{ formatMoney(detailData.FinancialProfit) }}</span>
  318 + </div>
  319 + </div>
  320 + </div>
  321 + </div>
  322 + </div>
  323 + <div slot="footer" class="dialog-footer">
  324 + <el-button @click="handleClose">关闭</el-button>
  325 + </div>
  326 + </el-dialog>
  327 +</template>
  328 +
  329 +<script>
  330 +export default {
  331 + name: 'ShopStockDetailDialog',
  332 + props: {
  333 + visible: {
  334 + type: Boolean,
  335 + default: false
  336 + },
  337 + detailData: {
  338 + type: Object,
  339 + default: null
  340 + }
  341 + },
  342 + methods: {
  343 + handleClose() {
  344 + this.$emit('close')
  345 + },
  346 + formatMoney(value) {
  347 + if (value === null || value === undefined || value === '') {
  348 + return '0.00'
  349 + }
  350 + return Number(value).toFixed(2)
  351 + },
  352 + formatMonth(value) {
  353 + if (!value || value.length !== 6) {
  354 + return value || '无'
  355 + }
  356 + const year = value.substring(0, 4)
  357 + const month = value.substring(4, 6)
  358 + return `${year}-${month}`
  359 + },
  360 + formatDateTime(value) {
  361 + if (!value) return '无'
  362 + const date = new Date(value)
  363 + const year = date.getFullYear()
  364 + const month = String(date.getMonth() + 1).padStart(2, '0')
  365 + const day = String(date.getDate()).padStart(2, '0')
  366 + const hours = String(date.getHours()).padStart(2, '0')
  367 + const minutes = String(date.getMinutes()).padStart(2, '0')
  368 + const seconds = String(date.getSeconds()).padStart(2, '0')
  369 + return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
  370 + }
  371 + }
  372 +}
  373 +</script>
  374 +
  375 +<style lang="scss" scoped>
  376 +.detail-dialog {
  377 + .detail-content {
  378 + max-height: 70vh;
  379 + overflow-y: auto;
  380 + padding: 20px;
  381 + }
  382 +
  383 + .detail-section {
  384 + margin-bottom: 24px;
  385 + background: #f9f9f9;
  386 + border-radius: 8px;
  387 + padding: 16px;
  388 +
  389 + &:last-child {
  390 + margin-bottom: 0;
  391 + }
  392 +
  393 + .section-title {
  394 + display: flex;
  395 + align-items: center;
  396 + gap: 8px;
  397 + margin-bottom: 16px;
  398 + font-size: 16px;
  399 + font-weight: 600;
  400 + color: #303133;
  401 + border-bottom: 2px solid #409EFF;
  402 + padding-bottom: 8px;
  403 +
  404 + i {
  405 + color: #409EFF;
  406 + font-size: 18px;
  407 + }
  408 + }
  409 +
  410 + .section-content {
  411 + .info-row {
  412 + display: flex;
  413 + flex-wrap: wrap;
  414 + gap: 20px;
  415 +
  416 + .info-item {
  417 + flex: 0 0 calc(25% - 15px);
  418 + min-width: 200px;
  419 + display: flex;
  420 + align-items: center;
  421 + padding: 8px 12px;
  422 + background: #fff;
  423 + border-radius: 4px;
  424 + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
  425 +
  426 + .label {
  427 + color: #606266;
  428 + font-weight: 500;
  429 + margin-right: 8px;
  430 + white-space: nowrap;
  431 + }
  432 +
  433 + .value {
  434 + color: #303133;
  435 + flex: 1;
  436 + text-align: right;
  437 +
  438 + &.highlight {
  439 + color: #409EFF;
  440 + font-weight: 600;
  441 + }
  442 +
  443 + &.large {
  444 + font-size: 18px;
  445 + }
  446 + }
  447 + }
  448 + }
  449 + }
  450 + }
  451 +
  452 + .dialog-footer {
  453 + text-align: right;
  454 + padding: 20px;
  455 + border-top: 1px solid #e4e7ed;
  456 + }
  457 +}
  458 +
  459 +// 滚动条样式
  460 +.detail-content::-webkit-scrollbar {
  461 + width: 6px;
  462 +}
  463 +
  464 +.detail-content::-webkit-scrollbar-track {
  465 + background: #f1f1f1;
  466 + border-radius: 3px;
  467 +}
  468 +
  469 +.detail-content::-webkit-scrollbar-thumb {
  470 + background: #c1c1c1;
  471 + border-radius: 3px;
  472 +
  473 + &:hover {
  474 + background: #a8a8a8;
  475 + }
  476 +}
  477 +</style>
  478 +
... ...
antis-ncc-admin/src/views/wageManagement/shop-stock.vue
... ... @@ -8,8 +8,24 @@
8 8 </div>
9 9 <div class="header-right">
10 10 <el-button
  11 + type="success"
  12 + icon="el-icon-download"
  13 + @click="handleExport"
  14 + :loading="exportLoading"
  15 + >
  16 + 导出
  17 + </el-button>
  18 + <el-button
  19 + type="primary"
  20 + icon="el-icon-cpu"
  21 + @click="showCalculateDialog"
  22 + :loading="calculateLoading"
  23 + >
  24 + 计算股份
  25 + </el-button>
  26 + <el-button
11 27 icon="el-icon-refresh"
12   - @click="handleRefresh"
  28 + @click="getList"
13 29 :loading="loading"
14 30 >
15 31 刷新
... ... @@ -17,28 +33,754 @@
17 33 </div>
18 34 </div>
19 35  
20   - <!-- 内容区域 -->
21   - <div class="content-container">
22   - <!-- 待开发 -->
  36 + <!-- 搜索区域 -->
  37 + <div class="search-container">
  38 + <el-form :model="queryParams" ref="queryForm" :inline="true" label-width="80px">
  39 + <el-form-item label="统计月份" prop="statisticsMonth">
  40 + <el-date-picker
  41 + v-model="queryParams.statisticsMonth"
  42 + type="month"
  43 + placeholder="选择月份"
  44 + format="yyyyMM"
  45 + value-format="yyyyMM"
  46 + style="width: 200px"
  47 + />
  48 + </el-form-item>
  49 + <el-form-item label="门店" prop="storeId">
  50 + <el-select
  51 + v-model="queryParams.storeId"
  52 + placeholder="请选择门店"
  53 + clearable
  54 + filterable
  55 + style="width: 200px"
  56 + >
  57 + <el-option
  58 + v-for="store in storeOptions"
  59 + :key="store.value"
  60 + :label="store.label"
  61 + :value="store.value"
  62 + />
  63 + </el-select>
  64 + </el-form-item>
  65 + <el-form-item>
  66 + <el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
  67 + <el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
  68 + </el-form-item>
  69 + </el-form>
23 70 </div>
  71 +
  72 + <!-- 数据表格 -->
  73 + <div class="table-container">
  74 + <NCC-table
  75 + v-loading="loading"
  76 + :data="list"
  77 + border
  78 + stripe
  79 + height="calc(100vh - 420px)"
  80 + :header-cell-style="{ background: '#f5f7fa', color: '#606266' }"
  81 + >
  82 + <!-- 基本信息 -->
  83 + <el-table-column prop="StoreName" label="门店名称" width="150" align="center" fixed="left">
  84 + <template slot-scope="scope">
  85 + <div class="store-name">
  86 + <i class="el-icon-shop store-icon"></i>
  87 + <span>{{ scope.row.StoreName || '无' }}</span>
  88 + </div>
  89 + </template>
  90 + </el-table-column>
  91 + <el-table-column prop="StatisticsMonth" label="统计月份" width="120" align="center" fixed="left">
  92 + <template slot-scope="scope">
  93 + <span>{{ formatMonth(scope.row.StatisticsMonth) || '无' }}</span>
  94 + </template>
  95 + </el-table-column>
  96 +
  97 + <!-- 收入部分 -->
  98 + <el-table-column label="收入信息" align="center">
  99 + <el-table-column prop="MainIncome" label="主营收入" width="120" align="center">
  100 + <template slot-scope="scope">
  101 + <span>{{ formatMoney(scope.row.MainIncome) }}</span>
  102 + </template>
  103 + </el-table-column>
  104 + <el-table-column prop="ConsumeLifeBeauty" label="生美消耗业绩" width="140" align="center">
  105 + <template slot-scope="scope">
  106 + <span>{{ formatMoney(scope.row.ConsumeLifeBeauty) }}</span>
  107 + </template>
  108 + </el-table-column>
  109 + <el-table-column prop="ConsumeTechBeauty" label="科美消耗业绩" width="140" align="center">
  110 + <template slot-scope="scope">
  111 + <span>{{ formatMoney(scope.row.ConsumeTechBeauty) }}</span>
  112 + </template>
  113 + </el-table-column>
  114 + <el-table-column prop="ConsumeMedicalBeauty" label="医美消耗业绩" width="140" align="center">
  115 + <template slot-scope="scope">
  116 + <span>{{ formatMoney(scope.row.ConsumeMedicalBeauty) }}</span>
  117 + </template>
  118 + </el-table-column>
  119 + <el-table-column prop="ConsumeCooperation" label="合作消费业绩" width="140" align="center">
  120 + <template slot-scope="scope">
  121 + <span>{{ formatMoney(scope.row.ConsumeCooperation) }}</span>
  122 + </template>
  123 + </el-table-column>
  124 + <el-table-column prop="ConsumeProduct" label="产品消耗业绩" width="140" align="center">
  125 + <template slot-scope="scope">
  126 + <span>{{ formatMoney(scope.row.ConsumeProduct) }}</span>
  127 + </template>
  128 + </el-table-column>
  129 + <el-table-column prop="ConsumeOther" label="其他消耗业绩" width="140" align="center">
  130 + <template slot-scope="scope">
  131 + <span>{{ formatMoney(scope.row.ConsumeOther) }}</span>
  132 + </template>
  133 + </el-table-column>
  134 + <el-table-column prop="OtherIncome" label="其他收入" width="120" align="center">
  135 + <template slot-scope="scope">
  136 + <span>{{ formatMoney(scope.row.OtherIncome) }}</span>
  137 + </template>
  138 + </el-table-column>
  139 + <el-table-column prop="AdvanceReceipt" label="预收款" width="120" align="center">
  140 + <template slot-scope="scope">
  141 + <span>{{ formatMoney(scope.row.AdvanceReceipt) }}</span>
  142 + </template>
  143 + </el-table-column>
  144 + <el-table-column prop="Refund" label="实际退款" width="120" align="center">
  145 + <template slot-scope="scope">
  146 + <span>{{ formatMoney(scope.row.Refund) }}</span>
  147 + </template>
  148 + </el-table-column>
  149 + <el-table-column prop="Receivables" label="应收" width="120" align="center">
  150 + <template slot-scope="scope">
  151 + <span>{{ formatMoney(scope.row.Receivables) }}</span>
  152 + </template>
  153 + </el-table-column>
  154 + <el-table-column prop="BankDeposit" label="银行存款" width="120" align="center">
  155 + <template slot-scope="scope">
  156 + <span>{{ formatMoney(scope.row.BankDeposit) }}</span>
  157 + </template>
  158 + </el-table-column>
  159 + </el-table-column>
  160 +
  161 + <!-- 成本部分 -->
  162 + <el-table-column label="成本信息" align="center">
  163 + <el-table-column prop="CostProduct" label="主营成本-产品" width="140" align="center">
  164 + <template slot-scope="scope">
  165 + <span>{{ formatMoney(scope.row.CostProduct) }}</span>
  166 + </template>
  167 + </el-table-column>
  168 + <el-table-column prop="CostFutian" label="主营成本-福田" width="140" align="center">
  169 + <template slot-scope="scope">
  170 + <span>{{ formatMoney(scope.row.CostFutian) }}</span>
  171 + </template>
  172 + </el-table-column>
  173 + <el-table-column prop="CostTowel" label="主营成本-毛巾" width="140" align="center">
  174 + <template slot-scope="scope">
  175 + <span>{{ formatMoney(scope.row.CostTowel) }}</span>
  176 + </template>
  177 + </el-table-column>
  178 + <el-table-column prop="CostTechDept" label="主营成本-科技部" width="150" align="center">
  179 + <template slot-scope="scope">
  180 + <span>{{ formatMoney(scope.row.CostTechDept) }}</span>
  181 + </template>
  182 + </el-table-column>
  183 + <el-table-column prop="CostManagementFee" label="主营成本-管理费" width="150" align="center">
  184 + <template slot-scope="scope">
  185 + <span>{{ formatMoney(scope.row.CostManagementFee) }}</span>
  186 + </template>
  187 + </el-table-column>
  188 + <el-table-column prop="CostCooperation" label="主营成本-合作" width="140" align="center">
  189 + <template slot-scope="scope">
  190 + <span>{{ formatMoney(scope.row.CostCooperation) }}</span>
  191 + </template>
  192 + </el-table-column>
  193 + <el-table-column prop="CostMajorProject" label="主营成本-大项目" width="150" align="center">
  194 + <template slot-scope="scope">
  195 + <span>{{ formatMoney(scope.row.CostMajorProject) }}</span>
  196 + </template>
  197 + </el-table-column>
  198 + <el-table-column prop="CostOther" label="其他成本" width="120" align="center">
  199 + <template slot-scope="scope">
  200 + <span>{{ formatMoney(scope.row.CostOther) }}</span>
  201 + </template>
  202 + </el-table-column>
  203 + </el-table-column>
  204 +
  205 + <!-- 人工工资部分 -->
  206 + <el-table-column label="人工工资信息" align="center">
  207 + <el-table-column prop="SalaryBaseHealthCoach" label="健康师底薪" width="120" align="center">
  208 + <template slot-scope="scope">
  209 + <span>{{ formatMoney(scope.row.SalaryBaseHealthCoach) }}</span>
  210 + </template>
  211 + </el-table-column>
  212 + <el-table-column prop="SalaryBaseAssistant" label="店助底薪" width="120" align="center">
  213 + <template slot-scope="scope">
  214 + <span>{{ formatMoney(scope.row.SalaryBaseAssistant) }}</span>
  215 + </template>
  216 + </el-table-column>
  217 + <el-table-column prop="SalaryBaseDirector" label="店助主任底薪" width="140" align="center">
  218 + <template slot-scope="scope">
  219 + <span>{{ formatMoney(scope.row.SalaryBaseDirector) }}</span>
  220 + </template>
  221 + </el-table-column>
  222 + <el-table-column prop="SalaryBaseStoreManager" label="店长底薪" width="120" align="center">
  223 + <template slot-scope="scope">
  224 + <span>{{ formatMoney(scope.row.SalaryBaseStoreManager) }}</span>
  225 + </template>
  226 + </el-table-column>
  227 + <el-table-column prop="SalaryBaseGeneralManager" label="总经理/经理底薪" width="160" align="center">
  228 + <template slot-scope="scope">
  229 + <span>{{ formatMoney(scope.row.SalaryBaseGeneralManager) }}</span>
  230 + </template>
  231 + </el-table-column>
  232 + <el-table-column prop="SalaryCommissionHealthCoach" label="健康师提成" width="120" align="center">
  233 + <template slot-scope="scope">
  234 + <span>{{ formatMoney(scope.row.SalaryCommissionHealthCoach) }}</span>
  235 + </template>
  236 + </el-table-column>
  237 + <el-table-column prop="SalaryCommissionAssistant" label="店助提成" width="120" align="center">
  238 + <template slot-scope="scope">
  239 + <span>{{ formatMoney(scope.row.SalaryCommissionAssistant) }}</span>
  240 + </template>
  241 + </el-table-column>
  242 + <el-table-column prop="SalaryCommissionDirector" label="店助主任提成" width="140" align="center">
  243 + <template slot-scope="scope">
  244 + <span>{{ formatMoney(scope.row.SalaryCommissionDirector) }}</span>
  245 + </template>
  246 + </el-table-column>
  247 + <el-table-column prop="SalaryCommissionStoreManager" label="店长提成" width="120" align="center">
  248 + <template slot-scope="scope">
  249 + <span>{{ formatMoney(scope.row.SalaryCommissionStoreManager) }}</span>
  250 + </template>
  251 + </el-table-column>
  252 + <el-table-column prop="SalaryCommissionGeneralManager" label="总经理/经理提成" width="160" align="center">
  253 + <template slot-scope="scope">
  254 + <span>{{ formatMoney(scope.row.SalaryCommissionGeneralManager) }}</span>
  255 + </template>
  256 + </el-table-column>
  257 + <el-table-column prop="SalaryManual" label="手工" width="100" align="center">
  258 + <template slot-scope="scope">
  259 + <span>{{ formatMoney(scope.row.SalaryManual) }}</span>
  260 + </template>
  261 + </el-table-column>
  262 + <el-table-column prop="SalaryAttendance" label="出勤" width="100" align="center">
  263 + <template slot-scope="scope">
  264 + <span>{{ formatMoney(scope.row.SalaryAttendance) }}</span>
  265 + </template>
  266 + </el-table-column>
  267 + <el-table-column prop="SalaryPhoneCustody" label="手机保管费" width="120" align="center">
  268 + <template slot-scope="scope">
  269 + <span>{{ formatMoney(scope.row.SalaryPhoneCustody) }}</span>
  270 + </template>
  271 + </el-table-column>
  272 + <el-table-column prop="SalaryHeadcountReward" label="人头" width="100" align="center">
  273 + <template slot-scope="scope">
  274 + <span>{{ formatMoney(scope.row.SalaryHeadcountReward) }}</span>
  275 + </template>
  276 + </el-table-column>
  277 + <el-table-column prop="SalaryTZone" label="门店T区" width="120" align="center">
  278 + <template slot-scope="scope">
  279 + <span>{{ formatMoney(scope.row.SalaryTZone) }}</span>
  280 + </template>
  281 + </el-table-column>
  282 + </el-table-column>
  283 +
  284 + <!-- 费用与其他 -->
  285 + <el-table-column label="费用与其他" align="center">
  286 + <el-table-column prop="SocialSecurity" label="社保" width="100" align="center">
  287 + <template slot-scope="scope">
  288 + <span>{{ formatMoney(scope.row.SocialSecurity) }}</span>
  289 + </template>
  290 + </el-table-column>
  291 + <el-table-column prop="StoreRent" label="门店房租" width="120" align="center">
  292 + <template slot-scope="scope">
  293 + <span>{{ formatMoney(scope.row.StoreRent) }}</span>
  294 + </template>
  295 + </el-table-column>
  296 + <el-table-column prop="DormRent" label="宿舍房租" width="120" align="center">
  297 + <template slot-scope="scope">
  298 + <span>{{ formatMoney(scope.row.DormRent) }}</span>
  299 + </template>
  300 + </el-table-column>
  301 + <el-table-column prop="CurrentPeriodExpense" label="当期费用" width="120" align="center">
  302 + <template slot-scope="scope">
  303 + <span>{{ formatMoney(scope.row.CurrentPeriodExpense) }}</span>
  304 + </template>
  305 + </el-table-column>
  306 + <el-table-column prop="CurrentPeriodExpenseQin" label="当前费用-秦董" width="140" align="center">
  307 + <template slot-scope="scope">
  308 + <span>{{ formatMoney(scope.row.CurrentPeriodExpenseQin) }}</span>
  309 + </template>
  310 + </el-table-column>
  311 + <el-table-column prop="FinancialFee" label="财务费用" width="120" align="center">
  312 + <template slot-scope="scope">
  313 + <span>{{ formatMoney(scope.row.FinancialFee) }}</span>
  314 + </template>
  315 + </el-table-column>
  316 + </el-table-column>
  317 +
  318 + <!-- 奖励 -->
  319 + <el-table-column label="奖励" align="center">
  320 + <el-table-column prop="RewardMedicalBeauty" label="奖励-医美" width="120" align="center">
  321 + <template slot-scope="scope">
  322 + <span>{{ formatMoney(scope.row.RewardMedicalBeauty) }}</span>
  323 + </template>
  324 + </el-table-column>
  325 + <el-table-column prop="RewardOther" label="奖励-其他" width="120" align="center">
  326 + <template slot-scope="scope">
  327 + <span>{{ formatMoney(scope.row.RewardOther) }}</span>
  328 + </template>
  329 + </el-table-column>
  330 + <el-table-column prop="RewardLargeOrder" label="奖励-大单奖" width="130" align="center">
  331 + <template slot-scope="scope">
  332 + <span>{{ formatMoney(scope.row.RewardLargeOrder) }}</span>
  333 + </template>
  334 + </el-table-column>
  335 + <el-table-column prop="RewardFirstOrder" label="奖励-首单奖" width="130" align="center">
  336 + <template slot-scope="scope">
  337 + <span>{{ formatMoney(scope.row.RewardFirstOrder) }}</span>
  338 + </template>
  339 + </el-table-column>
  340 + <el-table-column prop="RewardGeneral" label="奖励" width="100" align="center">
  341 + <template slot-scope="scope">
  342 + <span>{{ formatMoney(scope.row.RewardGeneral) }}</span>
  343 + </template>
  344 + </el-table-column>
  345 + </el-table-column>
  346 +
  347 + <!-- 其他保留字段 -->
  348 + <el-table-column label="其他" align="center">
  349 + <el-table-column prop="Other1" label="其他1" width="100" align="center">
  350 + <template slot-scope="scope">
  351 + <span>{{ formatMoney(scope.row.Other1) }}</span>
  352 + </template>
  353 + </el-table-column>
  354 + <el-table-column prop="Other2" label="其他2" width="100" align="center">
  355 + <template slot-scope="scope">
  356 + <span>{{ formatMoney(scope.row.Other2) }}</span>
  357 + </template>
  358 + </el-table-column>
  359 + </el-table-column>
  360 +
  361 + <!-- 利润结果 -->
  362 + <el-table-column label="利润结果" align="center">
  363 + <el-table-column prop="Profit" label="利润" width="120" align="center">
  364 + <template slot-scope="scope">
  365 + <span class="highlight">{{ formatMoney(scope.row.Profit) }}</span>
  366 + </template>
  367 + </el-table-column>
  368 + <el-table-column prop="FinancialProfit" label="财务利润" width="120" align="center">
  369 + <template slot-scope="scope">
  370 + <span class="highlight">{{ formatMoney(scope.row.FinancialProfit) }}</span>
  371 + </template>
  372 + </el-table-column>
  373 + </el-table-column>
  374 +
  375 + <!-- 操作列 -->
  376 + <el-table-column label="操作" width="120" align="center" fixed="right">
  377 + <template slot-scope="scope">
  378 + <el-button
  379 + type="text"
  380 + size="mini"
  381 + icon="el-icon-view"
  382 + @click="showDetail(scope.row)"
  383 + >
  384 + 查看详情
  385 + </el-button>
  386 + </template>
  387 + </el-table-column>
  388 + </NCC-table>
  389 + </div>
  390 +
  391 + <!-- 计算股份弹窗 -->
  392 + <el-dialog
  393 + title="计算门店股份"
  394 + :visible="calculateDialogVisible"
  395 + width="500px"
  396 + :close-on-click-modal="false"
  397 + @close="handleCalculateDialogClose"
  398 + >
  399 + <el-form label-width="100px" label-position="right">
  400 + <el-form-item label="统计月份" required>
  401 + <el-date-picker
  402 + v-model="calculateMonth"
  403 + type="month"
  404 + placeholder="请选择月份"
  405 + format="yyyyMM"
  406 + value-format="yyyyMM"
  407 + style="width: 100%"
  408 + />
  409 + </el-form-item>
  410 + <el-form-item label="门店" required>
  411 + <el-select
  412 + v-model="calculateStoreId"
  413 + placeholder="请选择门店(不选则计算所有门店)"
  414 + clearable
  415 + filterable
  416 + style="width: 100%"
  417 + >
  418 + <el-option
  419 + v-for="store in storeOptions"
  420 + :key="store.value"
  421 + :label="store.label"
  422 + :value="store.value"
  423 + />
  424 + </el-select>
  425 + </el-form-item>
  426 + </el-form>
  427 + <div slot="footer" class="dialog-footer">
  428 + <el-button @click="handleCalculateDialogClose">取消</el-button>
  429 + <el-button
  430 + type="primary"
  431 + @click="handleCalculateConfirm"
  432 + :loading="calculateLoading"
  433 + >
  434 + 确定
  435 + </el-button>
  436 + </div>
  437 + </el-dialog>
  438 +
  439 + <!-- 详情弹窗 -->
  440 + <shop-stock-detail-dialog
  441 + :visible="detailVisible"
  442 + :detail-data="currentDetailData"
  443 + @close="handleDetailClose"
  444 + />
24 445 </div>
25 446 </template>
26 447  
27 448 <script>
  449 +import { getShopStockList, generateShopStock, getStoreSelector } from '@/api/extend/shopStock'
  450 +import ShopStockDetailDialog from './shop-stock-detail-dialog.vue'
  451 +
28 452 export default {
29 453 name: 'ShopStock',
  454 + components: {
  455 + ShopStockDetailDialog
  456 + },
30 457 data() {
  458 + // 获取当前月份
  459 + const getCurrentMonth = () => {
  460 + const now = new Date()
  461 + const year = now.getFullYear()
  462 + const month = String(now.getMonth() + 1).padStart(2, '0')
  463 + return `${year}${month}`
  464 + }
  465 +
31 466 return {
32   - loading: false
  467 + loading: false,
  468 + calculateLoading: false,
  469 + exportLoading: false,
  470 + list: [],
  471 + storeOptions: [],
  472 + calculateDialogVisible: false,
  473 + calculateMonth: getCurrentMonth(),
  474 + calculateStoreId: '',
  475 + detailVisible: false,
  476 + currentDetailData: null,
  477 + queryParams: {
  478 + statisticsMonth: getCurrentMonth(), // yyyyMM 格式
  479 + storeId: ''
  480 + }
33 481 }
34 482 },
  483 + created() {
  484 + this.getStoreOptions()
  485 + this.getList()
  486 + },
35 487 methods: {
36   - handleRefresh() {
  488 + // 获取门店选项
  489 + async getStoreOptions() {
  490 + try {
  491 + const response = await getStoreSelector()
  492 + if (response.code === 200 && response.data) {
  493 + this.storeOptions = response.data.list.map(item => ({
  494 + value: item.id,
  495 + label: item.fullName
  496 + }))
  497 + }
  498 + } catch (error) {
  499 + console.error('获取门店列表失败:', error)
  500 + }
  501 + },
  502 +
  503 + // 获取列表数据
  504 + async getList() {
37 505 this.loading = true
38   - setTimeout(() => {
  506 + try {
  507 + const params = {
  508 + StatisticsMonth: this.queryParams.statisticsMonth || undefined,
  509 + StoreId: this.queryParams.storeId || undefined
  510 + }
  511 +
  512 + const response = await getShopStockList(params)
  513 + console.log(response)
  514 + if (response.code === 200) {
  515 + this.list = response.data.data || []
  516 + } else {
  517 + this.$message.error(response.msg || '获取数据失败')
  518 + }
  519 + } catch (error) {
  520 + console.error('获取门店股份列表失败:', error)
  521 + this.$message.error('获取数据失败')
  522 + } finally {
39 523 this.loading = false
40   - this.$message.success('刷新成功')
41   - }, 500)
  524 + }
  525 + },
  526 +
  527 + // 搜索
  528 + handleQuery() {
  529 + this.getList()
  530 + },
  531 +
  532 + // 重置搜索
  533 + resetQuery() {
  534 + const getCurrentMonth = () => {
  535 + const now = new Date()
  536 + const year = now.getFullYear()
  537 + const month = String(now.getMonth() + 1).padStart(2, '0')
  538 + return `${year}${month}`
  539 + }
  540 +
  541 + this.queryParams = {
  542 + statisticsMonth: getCurrentMonth(),
  543 + storeId: ''
  544 + }
  545 + this.getList()
  546 + },
  547 +
  548 + // 显示计算股份弹窗
  549 + showCalculateDialog() {
  550 + const getCurrentMonth = () => {
  551 + const now = new Date()
  552 + const year = now.getFullYear()
  553 + const month = String(now.getMonth() + 1).padStart(2, '0')
  554 + return `${year}${month}`
  555 + }
  556 + this.calculateMonth = getCurrentMonth()
  557 + this.calculateStoreId = ''
  558 + this.calculateDialogVisible = true
  559 + },
  560 +
  561 + // 关闭计算股份弹窗
  562 + handleCalculateDialogClose() {
  563 + this.calculateDialogVisible = false
  564 + },
  565 +
  566 + // 确认计算股份
  567 + async handleCalculateConfirm() {
  568 + if (!this.calculateMonth) {
  569 + this.$message.warning('请选择统计月份')
  570 + return
  571 + }
  572 +
  573 + this.calculateLoading = true
  574 + try {
  575 + const data = {
  576 + statisticsMonth: this.calculateMonth,
  577 + storeId: this.calculateStoreId || undefined
  578 + }
  579 +
  580 + const response = await generateShopStock(data)
  581 + if (response.code === 200) {
  582 + this.$message.success('计算股份成功')
  583 + this.calculateDialogVisible = false
  584 + this.getList()
  585 + } else {
  586 + this.$message.error(response.msg || '计算股份失败')
  587 + }
  588 + } catch (error) {
  589 + console.error('计算股份失败:', error)
  590 + this.$message.error(error.message || '计算股份失败')
  591 + } finally {
  592 + this.calculateLoading = false
  593 + }
  594 + },
  595 +
  596 + // 显示详情
  597 + showDetail(row) {
  598 + this.currentDetailData = row
  599 + this.detailVisible = true
  600 + },
  601 +
  602 + // 关闭详情弹窗
  603 + handleDetailClose() {
  604 + this.detailVisible = false
  605 + this.currentDetailData = null
  606 + },
  607 +
  608 + // 导出功能
  609 + async handleExport() {
  610 + if (!this.list || this.list.length === 0) {
  611 + this.$message.warning('暂无数据可导出')
  612 + return
  613 + }
  614 +
  615 + this.exportLoading = true
  616 + try {
  617 + // 动态导入 xlsx 库
  618 + const XLSX = await import('xlsx')
  619 +
  620 + // 构建表头(根据后端DTO字段的中文映射)
  621 + const headers = [
  622 + '门店名称',
  623 + '统计月份',
  624 + '主营收入',
  625 + '生美消耗业绩',
  626 + '科美消耗业绩',
  627 + '医美消耗业绩',
  628 + '合作消费业绩',
  629 + '产品消耗业绩',
  630 + '其他消耗业绩',
  631 + '其他收入',
  632 + '预收款',
  633 + '实际退款',
  634 + '应收',
  635 + '银行存款',
  636 + '主营成本-产品',
  637 + '主营成本-福田',
  638 + '主营成本-毛巾',
  639 + '主营成本-科技部',
  640 + '主营成本-管理费',
  641 + '主营成本-合作',
  642 + '主营成本-大项目',
  643 + '其他成本',
  644 + '健康师底薪',
  645 + '店助底薪',
  646 + '店助主任底薪',
  647 + '店长底薪',
  648 + '总经理/经理底薪',
  649 + '健康师提成',
  650 + '店助提成',
  651 + '店助主任提成',
  652 + '店长提成',
  653 + '总经理/经理提成',
  654 + '手工',
  655 + '出勤',
  656 + '手机保管费',
  657 + '人头',
  658 + '门店T区',
  659 + '社保',
  660 + '门店房租',
  661 + '宿舍房租',
  662 + '当期费用',
  663 + '当前费用-秦董',
  664 + '财务费用',
  665 + '奖励-医美',
  666 + '奖励-其他',
  667 + '奖励-大单奖',
  668 + '奖励-首单奖',
  669 + '奖励',
  670 + '其他1',
  671 + '其他2',
  672 + '利润',
  673 + '财务利润'
  674 + ]
  675 +
  676 + // 构建数据行
  677 + const dataRows = this.list.map(item => [
  678 + item.StoreName || '无',
  679 + this.formatMonth(item.StatisticsMonth) || '无',
  680 + this.formatMoney(item.MainIncome),
  681 + this.formatMoney(item.ConsumeLifeBeauty),
  682 + this.formatMoney(item.ConsumeTechBeauty),
  683 + this.formatMoney(item.ConsumeMedicalBeauty),
  684 + this.formatMoney(item.ConsumeCooperation),
  685 + this.formatMoney(item.ConsumeProduct),
  686 + this.formatMoney(item.ConsumeOther),
  687 + this.formatMoney(item.OtherIncome),
  688 + this.formatMoney(item.AdvanceReceipt),
  689 + this.formatMoney(item.Refund),
  690 + this.formatMoney(item.Receivables),
  691 + this.formatMoney(item.BankDeposit),
  692 + this.formatMoney(item.CostProduct),
  693 + this.formatMoney(item.CostFutian),
  694 + this.formatMoney(item.CostTowel),
  695 + this.formatMoney(item.CostTechDept),
  696 + this.formatMoney(item.CostManagementFee),
  697 + this.formatMoney(item.CostCooperation),
  698 + this.formatMoney(item.CostMajorProject),
  699 + this.formatMoney(item.CostOther),
  700 + this.formatMoney(item.SalaryBaseHealthCoach),
  701 + this.formatMoney(item.SalaryBaseAssistant),
  702 + this.formatMoney(item.SalaryBaseDirector),
  703 + this.formatMoney(item.SalaryBaseStoreManager),
  704 + this.formatMoney(item.SalaryBaseGeneralManager),
  705 + this.formatMoney(item.SalaryCommissionHealthCoach),
  706 + this.formatMoney(item.SalaryCommissionAssistant),
  707 + this.formatMoney(item.SalaryCommissionDirector),
  708 + this.formatMoney(item.SalaryCommissionStoreManager),
  709 + this.formatMoney(item.SalaryCommissionGeneralManager),
  710 + this.formatMoney(item.SalaryManual),
  711 + this.formatMoney(item.SalaryAttendance),
  712 + this.formatMoney(item.SalaryPhoneCustody),
  713 + this.formatMoney(item.SalaryHeadcountReward),
  714 + this.formatMoney(item.SalaryTZone),
  715 + this.formatMoney(item.SocialSecurity),
  716 + this.formatMoney(item.StoreRent),
  717 + this.formatMoney(item.DormRent),
  718 + this.formatMoney(item.CurrentPeriodExpense),
  719 + this.formatMoney(item.CurrentPeriodExpenseQin),
  720 + this.formatMoney(item.FinancialFee),
  721 + this.formatMoney(item.RewardMedicalBeauty),
  722 + this.formatMoney(item.RewardOther),
  723 + this.formatMoney(item.RewardLargeOrder),
  724 + this.formatMoney(item.RewardFirstOrder),
  725 + this.formatMoney(item.RewardGeneral),
  726 + this.formatMoney(item.Other1),
  727 + this.formatMoney(item.Other2),
  728 + this.formatMoney(item.Profit),
  729 + this.formatMoney(item.FinancialProfit)
  730 + ])
  731 +
  732 + // 合并表头和数据
  733 + const excelData = [headers, ...dataRows]
  734 +
  735 + // 创建工作表
  736 + const ws = XLSX.utils.aoa_to_sheet(excelData)
  737 +
  738 + // 设置列宽
  739 + const colWidths = headers.map(() => ({ wch: 15 }))
  740 + ws['!cols'] = colWidths
  741 +
  742 + // 创建工作簿
  743 + const wb = XLSX.utils.book_new()
  744 + XLSX.utils.book_append_sheet(wb, ws, '门店股份')
  745 +
  746 + // 生成文件名
  747 + const now = new Date()
  748 + const timestamp = now.getFullYear() +
  749 + String(now.getMonth() + 1).padStart(2, '0') +
  750 + String(now.getDate()).padStart(2, '0') +
  751 + String(now.getHours()).padStart(2, '0') +
  752 + String(now.getMinutes()).padStart(2, '0') +
  753 + String(now.getSeconds()).padStart(2, '0')
  754 + const fileName = `门店股份_${timestamp}.xlsx`
  755 +
  756 + // 导出文件
  757 + XLSX.writeFile(wb, fileName)
  758 +
  759 + this.$message.success('导出成功')
  760 + } catch (error) {
  761 + console.error('导出失败:', error)
  762 + this.$message.error('导出失败,请重试')
  763 + } finally {
  764 + this.exportLoading = false
  765 + }
  766 + },
  767 +
  768 + // 格式化金额
  769 + formatMoney(value) {
  770 + if (value === null || value === undefined || value === '') {
  771 + return '0.00'
  772 + }
  773 + return Number(value).toFixed(2)
  774 + },
  775 +
  776 + // 格式化月份
  777 + formatMonth(value) {
  778 + if (!value || value.length !== 6) {
  779 + return value || '无'
  780 + }
  781 + const year = value.substring(0, 4)
  782 + const month = value.substring(4, 6)
  783 + return `${year}-${month}`
42 784 }
43 785 }
44 786 }
... ... @@ -81,12 +823,54 @@ export default {
81 823 }
82 824 }
83 825  
84   -.content-container {
  826 +.search-container {
  827 + margin-bottom: 20px;
85 828 padding: 20px;
86 829 background: #fff;
87 830 border-radius: 8px;
88 831 box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
89   - min-height: 400px;
90 832 }
91   -</style>
92 833  
  834 +.table-container {
  835 + margin-bottom: 20px;
  836 + padding: 20px;
  837 + background: #fff;
  838 + border-radius: 8px;
  839 + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  840 +}
  841 +
  842 +.store-name {
  843 + display: flex;
  844 + align-items: center;
  845 + gap: 6px;
  846 +
  847 + .store-icon {
  848 + color: #409EFF;
  849 + font-size: 16px;
  850 + }
  851 +}
  852 +
  853 +.highlight {
  854 + color: #409EFF;
  855 + font-weight: 600;
  856 +}
  857 +
  858 +// 表格样式优化
  859 +::v-deep .el-table {
  860 + .el-table__header-wrapper {
  861 + th {
  862 + background-color: #f5f7fa;
  863 + color: #606266;
  864 + font-weight: 600;
  865 + }
  866 + }
  867 +
  868 + .el-table__body-wrapper {
  869 + .el-table__row {
  870 + &:hover {
  871 + background-color: #f5f7fa;
  872 + }
  873 + }
  874 + }
  875 +}
  876 +</style>
... ...
antis-ncc-admin/src/views/wageManagement/tech-general-manager-detail-dialog.vue 0 → 100644
  1 +<template>
  2 + <el-dialog
  3 + title="科技部总经理工资详情"
  4 + :visible="visible"
  5 + width="1400px"
  6 + :close-on-click-modal="false"
  7 + @close="handleClose"
  8 + class="detail-dialog"
  9 + >
  10 + <div v-if="detailData" class="detail-content">
  11 + <!-- 基本信息 -->
  12 + <div class="detail-section">
  13 + <div class="section-title">
  14 + <i class="el-icon-user"></i>
  15 + <span>基本信息</span>
  16 + </div>
  17 + <div class="section-content">
  18 + <div class="info-row">
  19 + <div class="info-item">
  20 + <span class="label">员工姓名:</span>
  21 + <span class="value">{{ detailData.EmployeeName || '无' }}</span>
  22 + </div>
  23 + <div class="info-item">
  24 + <span class="label">员工账号:</span>
  25 + <span class="value">{{ detailData.EmployeeAccount || '无' }}</span>
  26 + </div>
  27 + <div class="info-item">
  28 + <span class="label">核算岗位:</span>
  29 + <span class="value">{{ detailData.Position || '无' }}</span>
  30 + </div>
  31 + <div class="info-item">
  32 + <span class="label">统计月份:</span>
  33 + <span class="value">{{ detailData.StatisticsMonth || '无' }}</span>
  34 + </div>
  35 + <div class="info-item" v-if="detailData.IsTerminated !== undefined && detailData.IsTerminated !== null">
  36 + <span class="label">是否离职:</span>
  37 + <el-tag :type="detailData.IsTerminated === 1 ? 'danger' : 'success'" size="mini">
  38 + {{ detailData.IsTerminated === 1 ? '已离职' : '在职' }}
  39 + </el-tag>
  40 + </div>
  41 + <div class="info-item" v-if="detailData.IsLocked !== undefined && detailData.IsLocked !== null">
  42 + <span class="label">锁定状态:</span>
  43 + <el-tag :type="detailData.IsLocked === 1 ? 'danger' : 'success'" size="mini">
  44 + {{ detailData.IsLocked === 1 ? '已锁定' : '未锁定' }}
  45 + </el-tag>
  46 + </div>
  47 + </div>
  48 + </div>
  49 + </div>
  50 +
  51 + <!-- 业绩信息 -->
  52 + <div class="detail-section">
  53 + <div class="section-title">
  54 + <i class="el-icon-trophy"></i>
  55 + <span>业绩信息</span>
  56 + </div>
  57 + <div class="section-content">
  58 + <div class="info-row">
  59 + <div class="info-item">
  60 + <span class="label">溯源金额:</span>
  61 + <span class="value highlight">{{ formatMoney(detailData.TraceabilityAmount) }}</span>
  62 + </div>
  63 + <div class="info-item">
  64 + <span class="label">Cell金额:</span>
  65 + <span class="value highlight">{{ formatMoney(detailData.CellAmount) }}</span>
  66 + </div>
  67 + </div>
  68 + </div>
  69 + </div>
  70 +
  71 + <!-- 提成信息 -->
  72 + <div class="detail-section">
  73 + <div class="section-title">
  74 + <i class="el-icon-money"></i>
  75 + <span>提成信息</span>
  76 + </div>
  77 + <div class="section-content">
  78 + <div class="info-row">
  79 + <div class="info-item">
  80 + <span class="label">溯源金额提成比例:</span>
  81 + <span class="value">{{ formatPercent(detailData.TraceabilityCommissionRate) }}</span>
  82 + </div>
  83 + <div class="info-item">
  84 + <span class="label">溯源金额提成金额:</span>
  85 + <span class="value highlight">{{ formatMoney(detailData.TraceabilityCommissionAmount) }}</span>
  86 + </div>
  87 + <div class="info-item">
  88 + <span class="label">Cell金额提成比例:</span>
  89 + <span class="value">{{ formatPercent(detailData.CellCommissionRate) }}</span>
  90 + </div>
  91 + <div class="info-item">
  92 + <span class="label">Cell金额提成金额:</span>
  93 + <span class="value highlight">{{ formatMoney(detailData.CellCommissionAmount) }}</span>
  94 + </div>
  95 + <div class="info-item">
  96 + <span class="label">提成合计:</span>
  97 + <span class="value highlight">{{ formatMoney(detailData.TotalCommission) }}</span>
  98 + </div>
  99 + </div>
  100 + </div>
  101 + </div>
  102 +
  103 + <!-- 工资信息 -->
  104 + <div class="detail-section">
  105 + <div class="section-title">
  106 + <i class="el-icon-wallet"></i>
  107 + <span>工资信息</span>
  108 + </div>
  109 + <div class="section-content">
  110 + <div class="info-row">
  111 + <div class="info-item">
  112 + <span class="label">底薪:</span>
  113 + <span class="value">{{ formatMoney(detailData.BaseSalary) }}</span>
  114 + </div>
  115 + <div class="info-item">
  116 + <span class="label">在店天数:</span>
  117 + <span class="value">{{ detailData.WorkingDays || '0' }}</span>
  118 + </div>
  119 + <div class="info-item">
  120 + <span class="label">请假天数:</span>
  121 + <span class="value">{{ detailData.LeaveDays || '0' }}</span>
  122 + </div>
  123 + <div class="info-item">
  124 + <span class="label">核算应发工资:</span>
  125 + <span class="value highlight">{{ formatMoney(detailData.CalculatedGrossSalary) }}</span>
  126 + </div>
  127 + <div class="info-item">
  128 + <span class="label">最终应发工资:</span>
  129 + <span class="value highlight">{{ formatMoney(detailData.FinalGrossSalary) }}</span>
  130 + </div>
  131 + </div>
  132 + </div>
  133 + </div>
  134 +
  135 + <!-- 补贴信息 -->
  136 + <div class="detail-section">
  137 + <div class="section-title">
  138 + <i class="el-icon-circle-plus"></i>
  139 + <span>补贴信息</span>
  140 + </div>
  141 + <div class="section-content">
  142 + <div class="info-row">
  143 + <div class="info-item">
  144 + <span class="label">当月培训补贴:</span>
  145 + <span class="value">{{ formatMoney(detailData.MonthlyTrainingSubsidy) }}</span>
  146 + </div>
  147 + <div class="info-item">
  148 + <span class="label">当月交通补贴:</span>
  149 + <span class="value">{{ formatMoney(detailData.MonthlyTransportSubsidy) }}</span>
  150 + </div>
  151 + <div class="info-item">
  152 + <span class="label">上月培训补贴:</span>
  153 + <span class="value">{{ formatMoney(detailData.LastMonthTrainingSubsidy) }}</span>
  154 + </div>
  155 + <div class="info-item">
  156 + <span class="label">上月交通补贴:</span>
  157 + <span class="value">{{ formatMoney(detailData.LastMonthTransportSubsidy) }}</span>
  158 + </div>
  159 + <div class="info-item">
  160 + <span class="label">补贴合计:</span>
  161 + <span class="value highlight">{{ formatMoney(detailData.TotalSubsidy) }}</span>
  162 + </div>
  163 + </div>
  164 + </div>
  165 + </div>
  166 +
  167 + <!-- 扣款信息 -->
  168 + <div class="detail-section">
  169 + <div class="section-title">
  170 + <i class="el-icon-circle-minus"></i>
  171 + <span>扣款信息</span>
  172 + </div>
  173 + <div class="section-content">
  174 + <div class="info-row">
  175 + <div class="info-item">
  176 + <span class="label">缺卡扣款:</span>
  177 + <span class="value">{{ formatMoney(detailData.MissingCard) }}</span>
  178 + </div>
  179 + <div class="info-item">
  180 + <span class="label">迟到扣款:</span>
  181 + <span class="value">{{ formatMoney(detailData.LateArrival) }}</span>
  182 + </div>
  183 + <div class="info-item">
  184 + <span class="label">请假扣款:</span>
  185 + <span class="value">{{ formatMoney(detailData.LeaveDeduction) }}</span>
  186 + </div>
  187 + <div class="info-item">
  188 + <span class="label">扣社保:</span>
  189 + <span class="value">{{ formatMoney(detailData.SocialInsuranceDeduction) }}</span>
  190 + </div>
  191 + <div class="info-item">
  192 + <span class="label">扣除奖励:</span>
  193 + <span class="value">{{ formatMoney(detailData.RewardDeduction) }}</span>
  194 + </div>
  195 + <div class="info-item">
  196 + <span class="label">扣住宿费:</span>
  197 + <span class="value">{{ formatMoney(detailData.AccommodationDeduction) }}</span>
  198 + </div>
  199 + <div class="info-item">
  200 + <span class="label">扣学习期费用:</span>
  201 + <span class="value">{{ formatMoney(detailData.StudyPeriodDeduction) }}</span>
  202 + </div>
  203 + <div class="info-item">
  204 + <span class="label">扣工作服费用:</span>
  205 + <span class="value">{{ formatMoney(detailData.WorkClothesDeduction) }}</span>
  206 + </div>
  207 + <div class="info-item">
  208 + <span class="label">扣款合计:</span>
  209 + <span class="value highlight">{{ formatMoney(detailData.TotalDeduction) }}</span>
  210 + </div>
  211 + </div>
  212 + </div>
  213 + </div>
  214 +
  215 + <!-- 其他信息 -->
  216 + <div class="detail-section">
  217 + <div class="section-title">
  218 + <i class="el-icon-document"></i>
  219 + <span>其他信息</span>
  220 + </div>
  221 + <div class="section-content">
  222 + <div class="info-row">
  223 + <div class="info-item">
  224 + <span class="label">发奖金:</span>
  225 + <span class="value">{{ formatMoney(detailData.Bonus) }}</span>
  226 + </div>
  227 + <div class="info-item">
  228 + <span class="label">退手机押金:</span>
  229 + <span class="value">{{ formatMoney(detailData.ReturnPhoneDeposit) }}</span>
  230 + </div>
  231 + <div class="info-item">
  232 + <span class="label">退住宿押金:</span>
  233 + <span class="value">{{ formatMoney(detailData.ReturnAccommodationDeposit) }}</span>
  234 + </div>
  235 + <div class="info-item">
  236 + <span class="label">补发上月:</span>
  237 + <span class="value">{{ formatMoney(detailData.LastMonthSupplement) }}</span>
  238 + </div>
  239 + </div>
  240 + </div>
  241 + </div>
  242 +
  243 + <!-- 支付信息 -->
  244 + <div class="detail-section">
  245 + <div class="section-title">
  246 + <i class="el-icon-credit-card"></i>
  247 + <span>支付信息</span>
  248 + </div>
  249 + <div class="section-content">
  250 + <div class="info-row">
  251 + <div class="info-item">
  252 + <span class="label">实发工资:</span>
  253 + <span class="value highlight large">{{ formatMoney(detailData.ActualSalary) }}</span>
  254 + </div>
  255 + <div class="info-item">
  256 + <span class="label">当月是否发放:</span>
  257 + <span class="value">{{ detailData.MonthlyPaymentStatus || '无' }}</span>
  258 + </div>
  259 + <div class="info-item">
  260 + <span class="label">支付金额:</span>
  261 + <span class="value">{{ formatMoney(detailData.PaidAmount) }}</span>
  262 + </div>
  263 + <div class="info-item">
  264 + <span class="label">待支付金额:</span>
  265 + <span class="value">{{ formatMoney(detailData.PendingAmount) }}</span>
  266 + </div>
  267 + <div class="info-item">
  268 + <span class="label">当月支付总额:</span>
  269 + <span class="value highlight">{{ formatMoney(detailData.MonthlyTotalPayment) }}</span>
  270 + </div>
  271 + </div>
  272 + </div>
  273 + </div>
  274 + </div>
  275 +
  276 + <div v-else class="no-data">
  277 + <i class="el-icon-warning"></i>
  278 + <span>暂无数据</span>
  279 + </div>
  280 + </el-dialog>
  281 +</template>
  282 +
  283 +<script>
  284 +export default {
  285 + name: 'TechGeneralManagerDetailDialog',
  286 + props: {
  287 + visible: {
  288 + type: Boolean,
  289 + default: false
  290 + },
  291 + detailData: {
  292 + type: Object,
  293 + default: null
  294 + }
  295 + },
  296 + methods: {
  297 + handleClose() {
  298 + this.$emit('update:visible', false)
  299 + this.$emit('close')
  300 + },
  301 + formatMoney(value) {
  302 + if (value === null || value === undefined || value === '') {
  303 + return '0.00'
  304 + }
  305 + return Number(value).toFixed(2)
  306 + },
  307 + formatPercent(value) {
  308 + if (value === null || value === undefined || value === '') {
  309 + return '0.00%'
  310 + }
  311 + return (Number(value) * 100).toFixed(2) + '%'
  312 + }
  313 + }
  314 +}
  315 +</script>
  316 +
  317 +<style lang="scss" scoped>
  318 +.detail-dialog {
  319 + .detail-content {
  320 + max-height: 70vh;
  321 + overflow-y: auto;
  322 + padding-right: 10px;
  323 + }
  324 +
  325 + .detail-section {
  326 + margin-bottom: 20px;
  327 + background: #fff;
  328 + border-radius: 8px;
  329 + padding: 20px;
  330 + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  331 +
  332 + .section-title {
  333 + display: flex;
  334 + align-items: center;
  335 + gap: 8px;
  336 + margin-bottom: 16px;
  337 + padding-bottom: 12px;
  338 + border-bottom: 2px solid #409EFF;
  339 + font-size: 16px;
  340 + font-weight: 600;
  341 + color: #303133;
  342 +
  343 + i {
  344 + color: #409EFF;
  345 + font-size: 18px;
  346 + }
  347 + }
  348 +
  349 + .section-content {
  350 + .info-row {
  351 + display: flex;
  352 + flex-wrap: wrap;
  353 + gap: 20px 40px;
  354 +
  355 + .info-item {
  356 + display: flex;
  357 + align-items: center;
  358 + min-width: 200px;
  359 + flex: 0 0 calc(33.333% - 27px);
  360 +
  361 + .label {
  362 + color: #606266;
  363 + font-size: 14px;
  364 + white-space: nowrap;
  365 + margin-right: 8px;
  366 + }
  367 +
  368 + .value {
  369 + color: #303133;
  370 + font-size: 14px;
  371 + font-weight: 500;
  372 +
  373 + &.highlight {
  374 + color: #409EFF;
  375 + font-weight: 600;
  376 + }
  377 +
  378 + &.large {
  379 + font-size: 18px;
  380 + color: #67C23A;
  381 + }
  382 + }
  383 + }
  384 + }
  385 + }
  386 + }
  387 +
  388 + .no-data {
  389 + display: flex;
  390 + flex-direction: column;
  391 + align-items: center;
  392 + justify-content: center;
  393 + padding: 60px 20px;
  394 + color: #909399;
  395 +
  396 + i {
  397 + font-size: 48px;
  398 + margin-bottom: 16px;
  399 + }
  400 +
  401 + span {
  402 + font-size: 14px;
  403 + }
  404 + }
  405 +}
  406 +
  407 +// 滚动条样式
  408 +.detail-content::-webkit-scrollbar {
  409 + width: 6px;
  410 +}
  411 +
  412 +.detail-content::-webkit-scrollbar-track {
  413 + background: #f1f1f1;
  414 + border-radius: 3px;
  415 +}
  416 +
  417 +.detail-content::-webkit-scrollbar-thumb {
  418 + background: #c1c1c1;
  419 + border-radius: 3px;
  420 +
  421 + &:hover {
  422 + background: #a8a8a8;
  423 + }
  424 +}
  425 +</style>
  426 +
... ...
antis-ncc-admin/src/views/wageManagement/tech-general-managers.vue
... ... @@ -8,6 +8,14 @@
8 8 </div>
9 9 <div class="header-right">
10 10 <el-button
  11 + type="success"
  12 + icon="el-icon-download"
  13 + @click="handleExport"
  14 + :loading="exportLoading"
  15 + >
  16 + 导出
  17 + </el-button>
  18 + <el-button
11 19 type="primary"
12 20 icon="el-icon-cpu"
13 21 @click="showCalculateDialog"
... ... @@ -299,18 +307,27 @@
299 307 </el-table-column>
300 308 </el-table-column>
301 309  
302   - <!-- 其他字段 -->
303   - <el-table-column prop="StoreDetail" label="门店明细" width="200" align="center" fixed="right">
  310 + <!-- 操作列 -->
  311 + <el-table-column label="操作" width="200" align="center" fixed="right">
304 312 <template slot-scope="scope">
305 313 <el-button
306 314 type="text"
307 315 size="mini"
  316 + icon="el-icon-view"
  317 + @click="showDetail(scope.row)"
  318 + >
  319 + 查看详情
  320 + </el-button>
  321 + <el-button
  322 + type="text"
  323 + size="mini"
  324 + icon="el-icon-document"
308 325 @click="showStoreDetail(scope.row)"
309 326 v-if="scope.row.StoreDetail"
  327 + style="margin-left: 8px;"
310 328 >
311   - 查看明细
  329 + 门店明细
312 330 </el-button>
313   - <span v-else>无</span>
314 331 </template>
315 332 </el-table-column>
316 333 </NCC-table>
... ... @@ -358,6 +375,13 @@
358 375 </div>
359 376 </el-dialog>
360 377  
  378 + <!-- 详情弹窗 -->
  379 + <tech-general-manager-detail-dialog
  380 + :visible="detailVisible"
  381 + :detail-data="currentDetailData"
  382 + @close="handleDetailClose"
  383 + />
  384 +
361 385 <!-- 门店明细弹窗 -->
362 386 <el-dialog
363 387 title="门店明细"
... ... @@ -411,9 +435,13 @@
411 435  
412 436 <script>
413 437 import { getTechGeneralManagerSalaryList, calculateTechGeneralManagerSalary } from '@/api/extend/techGeneralManagerSalary'
  438 +import TechGeneralManagerDetailDialog from './tech-general-manager-detail-dialog.vue'
414 439  
415 440 export default {
416 441 name: 'TechGeneralManagerSalary',
  442 + components: {
  443 + TechGeneralManagerDetailDialog
  444 + },
417 445 data() {
418 446 // 获取当前年月
419 447 const getCurrentYear = () => {
... ... @@ -429,11 +457,14 @@ export default {
429 457 return {
430 458 loading: false,
431 459 calculateLoading: false,
  460 + exportLoading: false,
432 461 list: [],
433 462 total: 0,
434 463 calculateDialogVisible: false,
435 464 calculateYear: getCurrentYear(),
436 465 calculateMonth: getCurrentMonth(),
  466 + detailVisible: false,
  467 + currentDetailData: null,
437 468 storeDetailVisible: false,
438 469 storeDetailList: [],
439 470 queryParams: {
... ... @@ -604,6 +635,156 @@ export default {
604 635 this.storeDetailList = []
605 636 },
606 637  
  638 + // 显示详情
  639 + showDetail(row) {
  640 + this.currentDetailData = row
  641 + this.detailVisible = true
  642 + },
  643 +
  644 + // 关闭详情弹窗
  645 + handleDetailClose() {
  646 + this.detailVisible = false
  647 + this.currentDetailData = null
  648 + },
  649 +
  650 + // 导出功能
  651 + async handleExport() {
  652 + if (!this.list || this.list.length === 0) {
  653 + this.$message.warning('暂无数据可导出')
  654 + return
  655 + }
  656 +
  657 + this.exportLoading = true
  658 + try {
  659 + // 动态导入 xlsx 库
  660 + const XLSX = await import('xlsx')
  661 +
  662 + // 构建表头(根据后端DTO字段的中文映射)
  663 + const headers = [
  664 + '员工姓名',
  665 + '员工账号',
  666 + '核算岗位',
  667 + '统计月份',
  668 + '是否离职',
  669 + '溯源金额',
  670 + 'Cell金额',
  671 + '底薪',
  672 + '溯源金额提成比例',
  673 + '溯源金额提成金额',
  674 + 'Cell金额提成比例',
  675 + 'Cell金额提成金额',
  676 + '提成合计',
  677 + '在店天数',
  678 + '请假天数',
  679 + '核算应发工资',
  680 + '最终应发工资',
  681 + '当月培训补贴',
  682 + '当月交通补贴',
  683 + '上月培训补贴',
  684 + '上月交通补贴',
  685 + '补贴合计',
  686 + '缺卡扣款',
  687 + '迟到扣款',
  688 + '请假扣款',
  689 + '扣社保',
  690 + '扣除奖励',
  691 + '扣住宿费',
  692 + '扣学习期费用',
  693 + '扣工作服费用',
  694 + '扣款合计',
  695 + '发奖金',
  696 + '退手机押金',
  697 + '退住宿押金',
  698 + '补发上月',
  699 + '实发工资',
  700 + '当月是否发放',
  701 + '支付金额',
  702 + '待支付金额',
  703 + '当月支付总额',
  704 + '是否锁定'
  705 + ]
  706 +
  707 + // 构建数据行
  708 + const dataRows = this.list.map(item => [
  709 + item.EmployeeName || '无',
  710 + item.EmployeeAccount || '无',
  711 + item.Position || '无',
  712 + item.StatisticsMonth || '无',
  713 + item.IsTerminated === 1 ? '已离职' : '在职',
  714 + this.formatMoney(item.TraceabilityAmount),
  715 + this.formatMoney(item.CellAmount),
  716 + this.formatMoney(item.BaseSalary),
  717 + this.formatPercent(item.TraceabilityCommissionRate),
  718 + this.formatMoney(item.TraceabilityCommissionAmount),
  719 + this.formatPercent(item.CellCommissionRate),
  720 + this.formatMoney(item.CellCommissionAmount),
  721 + this.formatMoney(item.TotalCommission),
  722 + item.WorkingDays || '0',
  723 + item.LeaveDays || '0',
  724 + this.formatMoney(item.CalculatedGrossSalary),
  725 + this.formatMoney(item.FinalGrossSalary),
  726 + this.formatMoney(item.MonthlyTrainingSubsidy),
  727 + this.formatMoney(item.MonthlyTransportSubsidy),
  728 + this.formatMoney(item.LastMonthTrainingSubsidy),
  729 + this.formatMoney(item.LastMonthTransportSubsidy),
  730 + this.formatMoney(item.TotalSubsidy),
  731 + this.formatMoney(item.MissingCard),
  732 + this.formatMoney(item.LateArrival),
  733 + this.formatMoney(item.LeaveDeduction),
  734 + this.formatMoney(item.SocialInsuranceDeduction),
  735 + this.formatMoney(item.RewardDeduction),
  736 + this.formatMoney(item.AccommodationDeduction),
  737 + this.formatMoney(item.StudyPeriodDeduction),
  738 + this.formatMoney(item.WorkClothesDeduction),
  739 + this.formatMoney(item.TotalDeduction),
  740 + this.formatMoney(item.Bonus),
  741 + this.formatMoney(item.ReturnPhoneDeposit),
  742 + this.formatMoney(item.ReturnAccommodationDeposit),
  743 + this.formatMoney(item.LastMonthSupplement),
  744 + this.formatMoney(item.ActualSalary),
  745 + item.MonthlyPaymentStatus || '无',
  746 + this.formatMoney(item.PaidAmount),
  747 + this.formatMoney(item.PendingAmount),
  748 + this.formatMoney(item.MonthlyTotalPayment),
  749 + item.IsLocked === 1 ? '已锁定' : '未锁定'
  750 + ])
  751 +
  752 + // 合并表头和数据
  753 + const excelData = [headers, ...dataRows]
  754 +
  755 + // 创建工作表
  756 + const ws = XLSX.utils.aoa_to_sheet(excelData)
  757 +
  758 + // 设置列宽
  759 + const colWidths = headers.map(() => ({ wch: 15 }))
  760 + ws['!cols'] = colWidths
  761 +
  762 + // 创建工作簿
  763 + const wb = XLSX.utils.book_new()
  764 + XLSX.utils.book_append_sheet(wb, ws, '科技部总经理工资')
  765 +
  766 + // 生成文件名
  767 + const now = new Date()
  768 + const timestamp = now.getFullYear() +
  769 + String(now.getMonth() + 1).padStart(2, '0') +
  770 + String(now.getDate()).padStart(2, '0') +
  771 + String(now.getHours()).padStart(2, '0') +
  772 + String(now.getMinutes()).padStart(2, '0') +
  773 + String(now.getSeconds()).padStart(2, '0')
  774 + const fileName = `科技部总经理工资_${timestamp}.xlsx`
  775 +
  776 + // 导出文件
  777 + XLSX.writeFile(wb, fileName)
  778 +
  779 + this.$message.success('导出成功')
  780 + } catch (error) {
  781 + console.error('导出失败:', error)
  782 + this.$message.error('导出失败,请重试')
  783 + } finally {
  784 + this.exportLoading = false
  785 + }
  786 + },
  787 +
607 788 // 格式化金额
608 789 formatMoney(value) {
609 790 if (value === null || value === undefined || value === '') {
... ...
antis-ncc-admin/src/views/wageManagement/tech-stock-detail-dialog.vue 0 → 100644
  1 +<template>
  2 + <el-dialog
  3 + title="科技部股份详情"
  4 + :visible="visible"
  5 + width="1200px"
  6 + :close-on-click-modal="false"
  7 + @close="handleClose"
  8 + class="detail-dialog"
  9 + >
  10 + <div v-if="detailData" class="detail-content">
  11 + <!-- 基本信息 -->
  12 + <div class="detail-section">
  13 + <div class="section-title">
  14 + <i class="el-icon-user"></i>
  15 + <span>基本信息</span>
  16 + </div>
  17 + <div class="section-content">
  18 + <div class="info-row">
  19 + <div class="info-item">
  20 + <span class="label">部门名称:</span>
  21 + <span class="value">{{ detailData.DepartmentName || '无' }}</span>
  22 + </div>
  23 + <div class="info-item">
  24 + <span class="label">统计月份:</span>
  25 + <span class="value">{{ formatMonth(detailData.StatisticsMonth) || '无' }}</span>
  26 + </div>
  27 + <div class="info-item" v-if="detailData.CreateTime">
  28 + <span class="label">创建时间:</span>
  29 + <span class="value">{{ formatDateTime(detailData.CreateTime) }}</span>
  30 + </div>
  31 + <div class="info-item" v-if="detailData.UpdateTime">
  32 + <span class="label">更新时间:</span>
  33 + <span class="value">{{ formatDateTime(detailData.UpdateTime) }}</span>
  34 + </div>
  35 + </div>
  36 + </div>
  37 + </div>
  38 +
  39 + <!-- 收入信息 -->
  40 + <div class="detail-section">
  41 + <div class="section-title">
  42 + <i class="el-icon-trophy"></i>
  43 + <span>收入信息</span>
  44 + </div>
  45 + <div class="section-content">
  46 + <div class="info-row">
  47 + <div class="info-item">
  48 + <span class="label">收入:</span>
  49 + <span class="value highlight large">{{ formatMoney(detailData.Income) }}</span>
  50 + </div>
  51 + </div>
  52 + </div>
  53 + </div>
  54 +
  55 + <!-- 成本信息 -->
  56 + <div class="detail-section">
  57 + <div class="section-title">
  58 + <i class="el-icon-shopping-cart-2"></i>
  59 + <span>成本信息</span>
  60 + </div>
  61 + <div class="section-content">
  62 + <div class="info-row">
  63 + <div class="info-item">
  64 + <span class="label">成本-报销:</span>
  65 + <span class="value">{{ formatMoney(detailData.CostReimbursement) }}</span>
  66 + </div>
  67 + <div class="info-item">
  68 + <span class="label">成本-人工-老师底薪:</span>
  69 + <span class="value">{{ formatMoney(detailData.CostTeacherBase) }}</span>
  70 + </div>
  71 + <div class="info-item">
  72 + <span class="label">成本-人工-手工费:</span>
  73 + <span class="value">{{ formatMoney(detailData.CostTeacherManual) }}</span>
  74 + </div>
  75 + <div class="info-item">
  76 + <span class="label">成本-人工-开单提成:</span>
  77 + <span class="value">{{ formatMoney(detailData.CostTeacherBillingComm) }}</span>
  78 + </div>
  79 + <div class="info-item">
  80 + <span class="label">成本-人工-消耗提成:</span>
  81 + <span class="value">{{ formatMoney(detailData.CostTeacherConsumeComm) }}</span>
  82 + </div>
  83 + <div class="info-item">
  84 + <span class="label">成本-人工-专家提成:</span>
  85 + <span class="value">{{ formatMoney(detailData.CostTeacherExpertComm) }}</span>
  86 + </div>
  87 + <div class="info-item">
  88 + <span class="label">成本-人工-加班:</span>
  89 + <span class="value">{{ formatMoney(detailData.CostTeacherOvertime) }}</span>
  90 + </div>
  91 + <div class="info-item">
  92 + <span class="label">成本-人工-总经理底薪:</span>
  93 + <span class="value">{{ formatMoney(detailData.CostGMBase) }}</span>
  94 + </div>
  95 + <div class="info-item">
  96 + <span class="label">成本-人工-总经理提成:</span>
  97 + <span class="value">{{ formatMoney(detailData.CostGMComm) }}</span>
  98 + </div>
  99 + <div class="info-item">
  100 + <span class="label">成本-其他1:</span>
  101 + <span class="value">{{ formatMoney(detailData.CostOther1) }}</span>
  102 + </div>
  103 + <div class="info-item">
  104 + <span class="label">成本-其他2:</span>
  105 + <span class="value">{{ formatMoney(detailData.CostOther2) }}</span>
  106 + </div>
  107 + </div>
  108 + </div>
  109 + </div>
  110 +
  111 + <!-- 奖励 -->
  112 + <div class="detail-section">
  113 + <div class="section-title">
  114 + <i class="el-icon-medal"></i>
  115 + <span>奖励</span>
  116 + </div>
  117 + <div class="section-content">
  118 + <div class="info-row">
  119 + <div class="info-item">
  120 + <span class="label">奖励-科技部:</span>
  121 + <span class="value">{{ formatMoney(detailData.RewardTechDept) }}</span>
  122 + </div>
  123 + </div>
  124 + </div>
  125 + </div>
  126 +
  127 + <!-- 利润结果 -->
  128 + <div class="detail-section">
  129 + <div class="section-title">
  130 + <i class="el-icon-data-line"></i>
  131 + <span>利润结果</span>
  132 + </div>
  133 + <div class="section-content">
  134 + <div class="info-row">
  135 + <div class="info-item">
  136 + <span class="label">利润:</span>
  137 + <span class="value highlight large">{{ formatMoney(detailData.Profit) }}</span>
  138 + </div>
  139 + </div>
  140 + </div>
  141 + </div>
  142 + </div>
  143 + <div slot="footer" class="dialog-footer">
  144 + <el-button @click="handleClose">关闭</el-button>
  145 + </div>
  146 + </el-dialog>
  147 +</template>
  148 +
  149 +<script>
  150 +export default {
  151 + name: 'TechStockDetailDialog',
  152 + props: {
  153 + visible: {
  154 + type: Boolean,
  155 + default: false
  156 + },
  157 + detailData: {
  158 + type: Object,
  159 + default: null
  160 + }
  161 + },
  162 + methods: {
  163 + handleClose() {
  164 + this.$emit('close')
  165 + },
  166 + formatMoney(value) {
  167 + if (value === null || value === undefined || value === '') {
  168 + return '0.00'
  169 + }
  170 + return Number(value).toFixed(2)
  171 + },
  172 + formatMonth(value) {
  173 + if (!value || value.length !== 6) {
  174 + return value || '无'
  175 + }
  176 + const year = value.substring(0, 4)
  177 + const month = value.substring(4, 6)
  178 + return `${year}-${month}`
  179 + },
  180 + formatDateTime(value) {
  181 + if (!value) return '无'
  182 + const date = new Date(value)
  183 + const year = date.getFullYear()
  184 + const month = String(date.getMonth() + 1).padStart(2, '0')
  185 + const day = String(date.getDate()).padStart(2, '0')
  186 + const hours = String(date.getHours()).padStart(2, '0')
  187 + const minutes = String(date.getMinutes()).padStart(2, '0')
  188 + const seconds = String(date.getSeconds()).padStart(2, '0')
  189 + return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
  190 + }
  191 + }
  192 +}
  193 +</script>
  194 +
  195 +<style lang="scss" scoped>
  196 +.detail-dialog {
  197 + .detail-content {
  198 + max-height: 70vh;
  199 + overflow-y: auto;
  200 + padding: 20px;
  201 + }
  202 +
  203 + .detail-section {
  204 + margin-bottom: 24px;
  205 + background: #f9f9f9;
  206 + border-radius: 8px;
  207 + padding: 16px;
  208 +
  209 + &:last-child {
  210 + margin-bottom: 0;
  211 + }
  212 +
  213 + .section-title {
  214 + display: flex;
  215 + align-items: center;
  216 + gap: 8px;
  217 + margin-bottom: 16px;
  218 + font-size: 16px;
  219 + font-weight: 600;
  220 + color: #303133;
  221 + border-bottom: 2px solid #409EFF;
  222 + padding-bottom: 8px;
  223 +
  224 + i {
  225 + color: #409EFF;
  226 + font-size: 18px;
  227 + }
  228 + }
  229 +
  230 + .section-content {
  231 + .info-row {
  232 + display: flex;
  233 + flex-wrap: wrap;
  234 + gap: 20px;
  235 +
  236 + .info-item {
  237 + flex: 0 0 calc(33.333% - 14px);
  238 + min-width: 250px;
  239 + display: flex;
  240 + align-items: center;
  241 + padding: 8px 12px;
  242 + background: #fff;
  243 + border-radius: 4px;
  244 + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
  245 +
  246 + .label {
  247 + color: #606266;
  248 + font-weight: 500;
  249 + margin-right: 8px;
  250 + white-space: nowrap;
  251 + }
  252 +
  253 + .value {
  254 + color: #303133;
  255 + flex: 1;
  256 + text-align: right;
  257 +
  258 + &.highlight {
  259 + color: #409EFF;
  260 + font-weight: 600;
  261 + }
  262 +
  263 + &.large {
  264 + font-size: 18px;
  265 + }
  266 + }
  267 + }
  268 + }
  269 + }
  270 + }
  271 +
  272 + .dialog-footer {
  273 + text-align: right;
  274 + padding: 20px;
  275 + border-top: 1px solid #e4e7ed;
  276 + }
  277 +}
  278 +
  279 +// 滚动条样式
  280 +.detail-content::-webkit-scrollbar {
  281 + width: 6px;
  282 +}
  283 +
  284 +.detail-content::-webkit-scrollbar-track {
  285 + background: #f1f1f1;
  286 + border-radius: 3px;
  287 +}
  288 +
  289 +.detail-content::-webkit-scrollbar-thumb {
  290 + background: #c1c1c1;
  291 + border-radius: 3px;
  292 +
  293 + &:hover {
  294 + background: #a8a8a8;
  295 + }
  296 +}
  297 +</style>
  298 +
... ...
antis-ncc-admin/src/views/wageManagement/tech-stock.vue
... ... @@ -8,8 +8,24 @@
8 8 </div>
9 9 <div class="header-right">
10 10 <el-button
  11 + type="success"
  12 + icon="el-icon-download"
  13 + @click="handleExport"
  14 + :loading="exportLoading"
  15 + >
  16 + 导出
  17 + </el-button>
  18 + <el-button
  19 + type="primary"
  20 + icon="el-icon-cpu"
  21 + @click="showCalculateDialog"
  22 + :loading="calculateLoading"
  23 + >
  24 + 计算股份
  25 + </el-button>
  26 + <el-button
11 27 icon="el-icon-refresh"
12   - @click="handleRefresh"
  28 + @click="getList"
13 29 :loading="loading"
14 30 >
15 31 刷新
... ... @@ -17,28 +33,465 @@
17 33 </div>
18 34 </div>
19 35  
20   - <!-- 内容区域 -->
21   - <div class="content-container">
22   - <!-- 待开发 -->
  36 + <!-- 搜索区域 -->
  37 + <div class="search-container">
  38 + <el-form :model="queryParams" ref="queryForm" :inline="true" label-width="80px">
  39 + <el-form-item label="统计月份" prop="statisticsMonth">
  40 + <el-date-picker
  41 + v-model="queryParams.statisticsMonth"
  42 + type="month"
  43 + placeholder="选择月份"
  44 + format="yyyyMM"
  45 + value-format="yyyyMM"
  46 + style="width: 200px"
  47 + />
  48 + </el-form-item>
  49 + <el-form-item label="部门" prop="departmentName">
  50 + <el-select
  51 + v-model="queryParams.departmentName"
  52 + placeholder="请选择部门"
  53 + clearable
  54 + filterable
  55 + style="width: 200px"
  56 + >
  57 + <el-option label="科技一部" value="科技一部" />
  58 + <el-option label="科技二部" value="科技二部" />
  59 + </el-select>
  60 + </el-form-item>
  61 + <el-form-item>
  62 + <el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
  63 + <el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
  64 + </el-form-item>
  65 + </el-form>
23 66 </div>
  67 +
  68 + <!-- 数据表格 -->
  69 + <div class="table-container">
  70 + <NCC-table
  71 + v-loading="loading"
  72 + :data="list"
  73 + border
  74 + stripe
  75 + height="calc(100vh - 420px)"
  76 + :header-cell-style="{ background: '#f5f7fa', color: '#606266' }"
  77 + >
  78 + <!-- 基本信息 -->
  79 + <el-table-column prop="DepartmentName" label="部门名称" width="150" align="center" fixed="left">
  80 + <template slot-scope="scope">
  81 + <div class="department-name">
  82 + <i class="el-icon-office-building department-icon"></i>
  83 + <span>{{ scope.row.DepartmentName || '无' }}</span>
  84 + </div>
  85 + </template>
  86 + </el-table-column>
  87 + <el-table-column prop="StatisticsMonth" label="统计月份" width="120" align="center" fixed="left">
  88 + <template slot-scope="scope">
  89 + <span>{{ formatMonth(scope.row.StatisticsMonth) || '无' }}</span>
  90 + </template>
  91 + </el-table-column>
  92 +
  93 + <!-- 收入信息 -->
  94 + <el-table-column label="收入信息" align="center">
  95 + <el-table-column prop="Income" label="收入" width="120" align="center">
  96 + <template slot-scope="scope">
  97 + <span class="highlight">{{ formatMoney(scope.row.Income) }}</span>
  98 + </template>
  99 + </el-table-column>
  100 + </el-table-column>
  101 +
  102 + <!-- 成本信息 -->
  103 + <el-table-column label="成本信息" align="center">
  104 + <el-table-column prop="CostReimbursement" label="成本-报销" width="120" align="center">
  105 + <template slot-scope="scope">
  106 + <span>{{ formatMoney(scope.row.CostReimbursement) }}</span>
  107 + </template>
  108 + </el-table-column>
  109 + <el-table-column prop="CostTeacherBase" label="成本-人工-老师底薪" width="160" align="center">
  110 + <template slot-scope="scope">
  111 + <span>{{ formatMoney(scope.row.CostTeacherBase) }}</span>
  112 + </template>
  113 + </el-table-column>
  114 + <el-table-column prop="CostTeacherManual" label="成本-人工-手工费" width="160" align="center">
  115 + <template slot-scope="scope">
  116 + <span>{{ formatMoney(scope.row.CostTeacherManual) }}</span>
  117 + </template>
  118 + </el-table-column>
  119 + <el-table-column prop="CostTeacherBillingComm" label="成本-人工-开单提成" width="160" align="center">
  120 + <template slot-scope="scope">
  121 + <span>{{ formatMoney(scope.row.CostTeacherBillingComm) }}</span>
  122 + </template>
  123 + </el-table-column>
  124 + <el-table-column prop="CostTeacherConsumeComm" label="成本-人工-消耗提成" width="160" align="center">
  125 + <template slot-scope="scope">
  126 + <span>{{ formatMoney(scope.row.CostTeacherConsumeComm) }}</span>
  127 + </template>
  128 + </el-table-column>
  129 + <el-table-column prop="CostTeacherExpertComm" label="成本-人工-专家提成" width="160" align="center">
  130 + <template slot-scope="scope">
  131 + <span>{{ formatMoney(scope.row.CostTeacherExpertComm) }}</span>
  132 + </template>
  133 + </el-table-column>
  134 + <el-table-column prop="CostTeacherOvertime" label="成本-人工-加班" width="140" align="center">
  135 + <template slot-scope="scope">
  136 + <span>{{ formatMoney(scope.row.CostTeacherOvertime) }}</span>
  137 + </template>
  138 + </el-table-column>
  139 + <el-table-column prop="CostGMBase" label="成本-人工-总经理底薪" width="170" align="center">
  140 + <template slot-scope="scope">
  141 + <span>{{ formatMoney(scope.row.CostGMBase) }}</span>
  142 + </template>
  143 + </el-table-column>
  144 + <el-table-column prop="CostGMComm" label="成本-人工-总经理提成" width="170" align="center">
  145 + <template slot-scope="scope">
  146 + <span>{{ formatMoney(scope.row.CostGMComm) }}</span>
  147 + </template>
  148 + </el-table-column>
  149 + <el-table-column prop="CostOther1" label="成本-其他1" width="120" align="center">
  150 + <template slot-scope="scope">
  151 + <span>{{ formatMoney(scope.row.CostOther1) }}</span>
  152 + </template>
  153 + </el-table-column>
  154 + <el-table-column prop="CostOther2" label="成本-其他2" width="120" align="center">
  155 + <template slot-scope="scope">
  156 + <span>{{ formatMoney(scope.row.CostOther2) }}</span>
  157 + </template>
  158 + </el-table-column>
  159 + </el-table-column>
  160 +
  161 + <!-- 奖励 -->
  162 + <el-table-column label="奖励" align="center">
  163 + <el-table-column prop="RewardTechDept" label="奖励-科技部" width="140" align="center">
  164 + <template slot-scope="scope">
  165 + <span>{{ formatMoney(scope.row.RewardTechDept) }}</span>
  166 + </template>
  167 + </el-table-column>
  168 + </el-table-column>
  169 +
  170 + <!-- 利润结果 -->
  171 + <el-table-column label="利润结果" align="center">
  172 + <el-table-column prop="Profit" label="利润" width="120" align="center">
  173 + <template slot-scope="scope">
  174 + <span class="highlight">{{ formatMoney(scope.row.Profit) }}</span>
  175 + </template>
  176 + </el-table-column>
  177 + </el-table-column>
  178 +
  179 + <!-- 操作列 -->
  180 + <el-table-column label="操作" width="120" align="center" fixed="right">
  181 + <template slot-scope="scope">
  182 + <el-button
  183 + type="text"
  184 + size="mini"
  185 + icon="el-icon-view"
  186 + @click="showDetail(scope.row)"
  187 + >
  188 + 查看详情
  189 + </el-button>
  190 + </template>
  191 + </el-table-column>
  192 + </NCC-table>
  193 + </div>
  194 +
  195 + <!-- 计算股份弹窗 -->
  196 + <el-dialog
  197 + title="计算科技部股份"
  198 + :visible="calculateDialogVisible"
  199 + width="500px"
  200 + :close-on-click-modal="false"
  201 + @close="handleCalculateDialogClose"
  202 + >
  203 + <el-form label-width="100px" label-position="right">
  204 + <el-form-item label="统计月份" required>
  205 + <el-date-picker
  206 + v-model="calculateMonth"
  207 + type="month"
  208 + placeholder="请选择月份"
  209 + format="yyyyMM"
  210 + value-format="yyyyMM"
  211 + style="width: 100%"
  212 + />
  213 + </el-form-item>
  214 + <el-form-item label="部门">
  215 + <el-select
  216 + v-model="calculateDepartmentName"
  217 + placeholder="请选择部门(不选则计算所有部门)"
  218 + clearable
  219 + filterable
  220 + style="width: 100%"
  221 + >
  222 + <el-option label="科技一部" value="科技一部" />
  223 + <el-option label="科技二部" value="科技二部" />
  224 + </el-select>
  225 + </el-form-item>
  226 + </el-form>
  227 + <div slot="footer" class="dialog-footer">
  228 + <el-button @click="handleCalculateDialogClose">取消</el-button>
  229 + <el-button
  230 + type="primary"
  231 + @click="handleCalculateConfirm"
  232 + :loading="calculateLoading"
  233 + >
  234 + 确定
  235 + </el-button>
  236 + </div>
  237 + </el-dialog>
  238 +
  239 + <!-- 详情弹窗 -->
  240 + <tech-stock-detail-dialog
  241 + :visible="detailVisible"
  242 + :detail-data="currentDetailData"
  243 + @close="handleDetailClose"
  244 + />
24 245 </div>
25 246 </template>
26 247  
27 248 <script>
  249 +import { getTechStockList, generateTechStock } from '@/api/extend/techStock'
  250 +import TechStockDetailDialog from './tech-stock-detail-dialog.vue'
  251 +
28 252 export default {
29 253 name: 'TechStock',
  254 + components: {
  255 + TechStockDetailDialog
  256 + },
30 257 data() {
  258 + // 获取当前月份
  259 + const getCurrentMonth = () => {
  260 + const now = new Date()
  261 + const year = now.getFullYear()
  262 + const month = String(now.getMonth() + 1).padStart(2, '0')
  263 + return `${year}${month}`
  264 + }
  265 +
31 266 return {
32   - loading: false
  267 + loading: false,
  268 + calculateLoading: false,
  269 + exportLoading: false,
  270 + list: [],
  271 + calculateDialogVisible: false,
  272 + calculateMonth: getCurrentMonth(),
  273 + calculateDepartmentName: '',
  274 + detailVisible: false,
  275 + currentDetailData: null,
  276 + queryParams: {
  277 + statisticsMonth: getCurrentMonth(), // yyyyMM 格式
  278 + departmentName: ''
  279 + }
33 280 }
34 281 },
  282 + created() {
  283 + this.getList()
  284 + },
35 285 methods: {
36   - handleRefresh() {
  286 + // 获取列表数据
  287 + async getList() {
37 288 this.loading = true
38   - setTimeout(() => {
  289 + try {
  290 + const params = {
  291 + StatisticsMonth: this.queryParams.statisticsMonth || undefined,
  292 + DepartmentName: this.queryParams.departmentName || undefined
  293 + }
  294 +
  295 + const response = await getTechStockList(params)
  296 +
  297 + if (response.code === 200) {
  298 + this.list = response.data.data || []
  299 + } else {
  300 + this.$message.error(response.msg || '获取数据失败')
  301 + }
  302 + } catch (error) {
  303 + console.error('获取科技部股份列表失败:', error)
  304 + this.$message.error('获取数据失败')
  305 + } finally {
39 306 this.loading = false
40   - this.$message.success('刷新成功')
41   - }, 500)
  307 + }
  308 + },
  309 +
  310 + // 搜索
  311 + handleQuery() {
  312 + this.getList()
  313 + },
  314 +
  315 + // 重置搜索
  316 + resetQuery() {
  317 + const getCurrentMonth = () => {
  318 + const now = new Date()
  319 + const year = now.getFullYear()
  320 + const month = String(now.getMonth() + 1).padStart(2, '0')
  321 + return `${year}${month}`
  322 + }
  323 +
  324 + this.queryParams = {
  325 + statisticsMonth: getCurrentMonth(),
  326 + departmentName: ''
  327 + }
  328 + this.getList()
  329 + },
  330 +
  331 + // 显示计算股份弹窗
  332 + showCalculateDialog() {
  333 + const getCurrentMonth = () => {
  334 + const now = new Date()
  335 + const year = now.getFullYear()
  336 + const month = String(now.getMonth() + 1).padStart(2, '0')
  337 + return `${year}${month}`
  338 + }
  339 + this.calculateMonth = getCurrentMonth()
  340 + this.calculateDepartmentName = ''
  341 + this.calculateDialogVisible = true
  342 + },
  343 +
  344 + // 关闭计算股份弹窗
  345 + handleCalculateDialogClose() {
  346 + this.calculateDialogVisible = false
  347 + },
  348 +
  349 + // 确认计算股份
  350 + async handleCalculateConfirm() {
  351 + if (!this.calculateMonth) {
  352 + this.$message.warning('请选择统计月份')
  353 + return
  354 + }
  355 +
  356 + this.calculateLoading = true
  357 + try {
  358 + const data = {
  359 + statisticsMonth: this.calculateMonth,
  360 + departmentName: this.calculateDepartmentName || undefined
  361 + }
  362 +
  363 + const response = await generateTechStock(data)
  364 + if (response.code === 200) {
  365 + this.$message.success('计算股份成功')
  366 + this.calculateDialogVisible = false
  367 + this.getList()
  368 + } else {
  369 + this.$message.error(response.msg || '计算股份失败')
  370 + }
  371 + } catch (error) {
  372 + console.error('计算股份失败:', error)
  373 + this.$message.error(error.message || '计算股份失败')
  374 + } finally {
  375 + this.calculateLoading = false
  376 + }
  377 + },
  378 +
  379 + // 显示详情
  380 + showDetail(row) {
  381 + this.currentDetailData = row
  382 + this.detailVisible = true
  383 + },
  384 +
  385 + // 关闭详情弹窗
  386 + handleDetailClose() {
  387 + this.detailVisible = false
  388 + this.currentDetailData = null
  389 + },
  390 +
  391 + // 导出功能
  392 + async handleExport() {
  393 + if (!this.list || this.list.length === 0) {
  394 + this.$message.warning('暂无数据可导出')
  395 + return
  396 + }
  397 +
  398 + this.exportLoading = true
  399 + try {
  400 + // 动态导入 xlsx 库
  401 + const XLSX = await import('xlsx')
  402 +
  403 + // 构建表头(根据后端DTO字段的中文映射)
  404 + const headers = [
  405 + '部门名称',
  406 + '统计月份',
  407 + '收入',
  408 + '成本-报销',
  409 + '成本-人工-老师底薪',
  410 + '成本-人工-手工费',
  411 + '成本-人工-开单提成',
  412 + '成本-人工-消耗提成',
  413 + '成本-人工-专家提成',
  414 + '成本-人工-加班',
  415 + '成本-人工-总经理底薪',
  416 + '成本-人工-总经理提成',
  417 + '奖励-科技部',
  418 + '成本-其他1',
  419 + '成本-其他2',
  420 + '利润'
  421 + ]
  422 +
  423 + // 构建数据行
  424 + const dataRows = this.list.map(item => [
  425 + item.DepartmentName || '无',
  426 + this.formatMonth(item.StatisticsMonth) || '无',
  427 + this.formatMoney(item.Income),
  428 + this.formatMoney(item.CostReimbursement),
  429 + this.formatMoney(item.CostTeacherBase),
  430 + this.formatMoney(item.CostTeacherManual),
  431 + this.formatMoney(item.CostTeacherBillingComm),
  432 + this.formatMoney(item.CostTeacherConsumeComm),
  433 + this.formatMoney(item.CostTeacherExpertComm),
  434 + this.formatMoney(item.CostTeacherOvertime),
  435 + this.formatMoney(item.CostGMBase),
  436 + this.formatMoney(item.CostGMComm),
  437 + this.formatMoney(item.RewardTechDept),
  438 + this.formatMoney(item.CostOther1),
  439 + this.formatMoney(item.CostOther2),
  440 + this.formatMoney(item.Profit)
  441 + ])
  442 +
  443 + // 合并表头和数据
  444 + const excelData = [headers, ...dataRows]
  445 +
  446 + // 创建工作表
  447 + const ws = XLSX.utils.aoa_to_sheet(excelData)
  448 +
  449 + // 设置列宽
  450 + const colWidths = headers.map(() => ({ wch: 15 }))
  451 + ws['!cols'] = colWidths
  452 +
  453 + // 创建工作簿
  454 + const wb = XLSX.utils.book_new()
  455 + XLSX.utils.book_append_sheet(wb, ws, '科技部股份')
  456 +
  457 + // 生成文件名
  458 + const now = new Date()
  459 + const timestamp = now.getFullYear() +
  460 + String(now.getMonth() + 1).padStart(2, '0') +
  461 + String(now.getDate()).padStart(2, '0') +
  462 + String(now.getHours()).padStart(2, '0') +
  463 + String(now.getMinutes()).padStart(2, '0') +
  464 + String(now.getSeconds()).padStart(2, '0')
  465 + const fileName = `科技部股份_${timestamp}.xlsx`
  466 +
  467 + // 导出文件
  468 + XLSX.writeFile(wb, fileName)
  469 +
  470 + this.$message.success('导出成功')
  471 + } catch (error) {
  472 + console.error('导出失败:', error)
  473 + this.$message.error('导出失败,请重试')
  474 + } finally {
  475 + this.exportLoading = false
  476 + }
  477 + },
  478 +
  479 + // 格式化金额
  480 + formatMoney(value) {
  481 + if (value === null || value === undefined || value === '') {
  482 + return '0.00'
  483 + }
  484 + return Number(value).toFixed(2)
  485 + },
  486 +
  487 + // 格式化月份
  488 + formatMonth(value) {
  489 + if (!value || value.length !== 6) {
  490 + return value || '无'
  491 + }
  492 + const year = value.substring(0, 4)
  493 + const month = value.substring(4, 6)
  494 + return `${year}-${month}`
42 495 }
43 496 }
44 497 }
... ... @@ -81,12 +534,54 @@ export default {
81 534 }
82 535 }
83 536  
84   -.content-container {
  537 +.search-container {
  538 + margin-bottom: 20px;
85 539 padding: 20px;
86 540 background: #fff;
87 541 border-radius: 8px;
88 542 box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
89   - min-height: 400px;
90 543 }
91   -</style>
92 544  
  545 +.table-container {
  546 + margin-bottom: 20px;
  547 + padding: 20px;
  548 + background: #fff;
  549 + border-radius: 8px;
  550 + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  551 +}
  552 +
  553 +.department-name {
  554 + display: flex;
  555 + align-items: center;
  556 + gap: 6px;
  557 +
  558 + .department-icon {
  559 + color: #409EFF;
  560 + font-size: 16px;
  561 + }
  562 +}
  563 +
  564 +.highlight {
  565 + color: #409EFF;
  566 + font-weight: 600;
  567 +}
  568 +
  569 +// 表格样式优化
  570 +::v-deep .el-table {
  571 + .el-table__header-wrapper {
  572 + th {
  573 + background-color: #f5f7fa;
  574 + color: #606266;
  575 + font-weight: 600;
  576 + }
  577 + }
  578 +
  579 + .el-table__body-wrapper {
  580 + .el-table__row {
  581 + &:hover {
  582 + background-color: #f5f7fa;
  583 + }
  584 + }
  585 + }
  586 +}
  587 +</style>
... ...