feat(rebate):重构返利页面搜索和表格功能
- 使用原生HTML元素替换Element UI组件 - 新增操作类型、计算状态、审核状态、数据来源等搜索条件 - 添加表格控制按钮和自定义分页组件 - 实现状态标签样式和操作链接样式 - 优化表格样式和加载状态显示 - 完善搜索表单重置逻辑 - 调整页面布局和样式细节
Showing
2 changed files
with
563 additions
and
177 deletions
| ... | @@ -32,6 +32,8 @@ export interface RebateSearchParams { | ... | @@ -32,6 +32,8 @@ export interface RebateSearchParams { |
| 32 | productCode?: string | 32 | productCode?: string |
| 33 | operateType?: number | 33 | operateType?: number |
| 34 | calcFlag?: number | 34 | calcFlag?: number |
| 35 | + auditStatus?: number | ||
| 36 | + dataSource?: string | ||
| 35 | rebateStartDate?: string | 37 | rebateStartDate?: string |
| 36 | rebateEndDate?: string | 38 | rebateEndDate?: string |
| 37 | } | 39 | } | ... | ... |
| ... | @@ -2,51 +2,64 @@ | ... | @@ -2,51 +2,64 @@ |
| 2 | <div class="rebate-page"> | 2 | <div class="rebate-page"> |
| 3 | <!-- 搜索区域 --> | 3 | <!-- 搜索区域 --> |
| 4 | <div class="search-section"> | 4 | <div class="search-section"> |
| 5 | - <div class="search-form"> | 5 | + <div class="search-row"> |
| 6 | - <div class="search-row"> | 6 | + <div class="search-item"> |
| 7 | - <div class="search-item"> | 7 | + <label>经销商名称:</label> |
| 8 | - <span class="search-label">经销商名称</span> | 8 | + <input |
| 9 | - <el-input | 9 | + v-model="searchForm.dealerName" |
| 10 | - v-model="searchForm.dealerName" | 10 | + class="search-input" |
| 11 | placeholder="请输入经销商名称" | 11 | placeholder="请输入经销商名称" |
| 12 | - clearable | 12 | + /> |
| 13 | - size="small" | 13 | + </div> |
| 14 | - style="width: 160px" | 14 | + <div class="search-item"> |
| 15 | - /> | 15 | + <label>返利编号:</label> |
| 16 | - </div> | 16 | + <input |
| 17 | - <div class="search-item"> | 17 | + v-model="searchForm.rebateNo" |
| 18 | - <span class="search-label">返利编号</span> | 18 | + class="search-input" |
| 19 | - <el-input | 19 | + placeholder="请输入返利编号" |
| 20 | - v-model="searchForm.rebateNo" | 20 | + /> |
| 21 | - placeholder="请输入返利编号" | 21 | + </div> |
| 22 | - clearable | 22 | + <div class="search-item"> |
| 23 | - size="small" | 23 | + <label>操作类型:</label> |
| 24 | - style="width: 160px" | 24 | + <select v-model="searchForm.operateType" class="search-select"> |
| 25 | - /> | 25 | + <option value="">所有</option> |
| 26 | - </div> | 26 | + <option value="1">新增</option> |
| 27 | - <div class="search-item"> | 27 | + <option value="2">修改</option> |
| 28 | - <span class="search-label">日期</span> | 28 | + <option value="3">删除</option> |
| 29 | - <el-date-picker | 29 | + </select> |
| 30 | - v-model="dateRange" | 30 | + </div> |
| 31 | - type="daterange" | 31 | + <div class="search-item"> |
| 32 | - range-separator="至" | 32 | + <label>计算状态:</label> |
| 33 | - start-placeholder="开始日期" | 33 | + <select v-model="searchForm.calcFlag" class="search-select"> |
| 34 | - end-placeholder="结束日期" | 34 | + <option value="">所有</option> |
| 35 | - size="small" | 35 | + <option value="0">未计算</option> |
| 36 | - format="YYYY-MM-DD" | 36 | + <option value="1">已计算</option> |
| 37 | - value-format="YYYY-MM-DD" | 37 | + </select> |
| 38 | - @change="handleDateRangeChange" | 38 | + </div> |
| 39 | - style="width: 240px" | 39 | + </div> |
| 40 | - /> | 40 | + <div class="search-row"> |
| 41 | - </div> | 41 | + <div class="search-item"> |
| 42 | - <div class="search-actions"> | 42 | + <label>审核状态:</label> |
| 43 | - <el-button type="primary" size="small" @click="handleSearch" :loading="loading"> | 43 | + <select v-model="searchForm.auditStatus" class="search-select"> |
| 44 | - 查询 | 44 | + <option value="">所有</option> |
| 45 | - </el-button> | 45 | + <option value="0">待审核</option> |
| 46 | - <el-button size="small" @click="handleReset"> | 46 | + <option value="1">审核通过</option> |
| 47 | - 重置 | 47 | + <option value="2">审核中</option> |
| 48 | - </el-button> | 48 | + <option value="3">审核拒绝</option> |
| 49 | - </div> | 49 | + </select> |
| 50 | + </div> | ||
| 51 | + <div class="search-item"> | ||
| 52 | + <label>数据来源:</label> | ||
| 53 | + <select v-model="searchForm.dataSource" class="search-select"> | ||
| 54 | + <option value="">所有</option> | ||
| 55 | + <option value="ERP系统">ERP系统</option> | ||
| 56 | + <option value="手工录入">手工录入</option> | ||
| 57 | + <option value="API导入">API导入</option> | ||
| 58 | + </select> | ||
| 59 | + </div> | ||
| 60 | + <div class="search-actions"> | ||
| 61 | + <button @click="handleSearch" class="search-btn" :disabled="loading">🔍 搜索</button> | ||
| 62 | + <button @click="handleReset" class="reset-btn">🔄 重置</button> | ||
| 50 | </div> | 63 | </div> |
| 51 | </div> | 64 | </div> |
| 52 | </div> | 65 | </div> |
| ... | @@ -96,95 +109,103 @@ | ... | @@ -96,95 +109,103 @@ |
| 96 | </div> | 109 | </div> |
| 97 | </div> | 110 | </div> |
| 98 | 111 | ||
| 112 | + <!-- 操作按钮区域 --> | ||
| 113 | + <div class="action-section"> | ||
| 114 | + <div class="action-buttons"> | ||
| 115 | + <button @click="handleExport" class="action-btn secondary" :disabled="exportLoading"> | ||
| 116 | + 📋 导出 | ||
| 117 | + </button> | ||
| 118 | + </div> | ||
| 119 | + </div> | ||
| 120 | + | ||
| 99 | <!-- 数据表格 --> | 121 | <!-- 数据表格 --> |
| 100 | <div class="table-section"> | 122 | <div class="table-section"> |
| 101 | <div class="table-header"> | 123 | <div class="table-header"> |
| 102 | - <div class="table-title"> | 124 | + <div class="table-controls"> |
| 103 | - 返利记录 | 125 | + <button @click="handleTableSearch" class="control-btn">🔍</button> |
| 104 | - </div> | 126 | + <button @click="handleTableRefresh" class="control-btn">🔄</button> |
| 105 | - <div class="table-actions"> | 127 | + <button @click="handleTableExport" class="control-btn">📋</button> |
| 106 | - <el-button type="primary" size="small" @click="handleExport" :loading="exportLoading"> | 128 | + <button @click="handleTableViewToggle" class="control-btn">⊞</button> |
| 107 | - <el-icon><Download /></el-icon> | ||
| 108 | - 导出 | ||
| 109 | - </el-button> | ||
| 110 | - <div class="table-info"> | ||
| 111 | - 数据状态: {{ loading ? '加载中...' : `共${rebateList.length}条记录` }} | ||
| 112 | - </div> | ||
| 113 | </div> | 129 | </div> |
| 114 | </div> | 130 | </div> |
| 115 | 131 | ||
| 116 | - <el-table | 132 | + <div class="table-container"> |
| 117 | - v-loading="loading" | 133 | + <div v-if="loading" class="loading-overlay"> |
| 118 | - :data="rebateList" | 134 | + <div class="loading-spinner">加载中...</div> |
| 119 | - style="width: 100%" | 135 | + </div> |
| 120 | - :header-cell-style="{ background: '#fafafa', color: '#333', fontWeight: 'normal' }" | 136 | + <table class="data-table"> |
| 121 | - :empty-text="rebateList.length === 0 ? '暂无数据' : ''" | 137 | + <thead> |
| 122 | - size="small" | 138 | + <tr> |
| 123 | - > | 139 | + <th>返利编号</th> |
| 124 | - <el-table-column prop="rebateNo" label="返利编号" width="140" align="center" /> | 140 | + <th>订单编号</th> |
| 125 | - <el-table-column prop="orderNo" label="订单编号" width="140" align="center" /> | 141 | + <th>经销商代码</th> |
| 126 | - <el-table-column prop="dealerCode" label="经销商代码" width="120" align="center" /> | 142 | + <th>经销商名称</th> |
| 127 | - <el-table-column prop="dealerName" label="经销商名称" min-width="150" /> | 143 | + <th>产品编码</th> |
| 128 | - <el-table-column prop="productCode" label="产品编码" width="120" align="center" /> | 144 | + <th>返利金额</th> |
| 129 | - <el-table-column prop="rebateAmount" label="返利金额" width="120" align="right"> | 145 | + <th>返利日期</th> |
| 130 | - <template #default="{ row }"> | 146 | + <th>操作类型</th> |
| 131 | - <span class="amount-text">{{ formatAmount(row.rebateAmount) }}</span> | 147 | + <th>计算状态</th> |
| 132 | - </template> | 148 | + <th>更新时间</th> |
| 133 | - </el-table-column> | 149 | + <th>操作</th> |
| 134 | - <el-table-column prop="rebateDate" label="返利日期" width="120" align="center" /> | 150 | + </tr> |
| 135 | - <el-table-column prop="operateTypeText" label="操作类型" width="100" align="center"> | 151 | + </thead> |
| 136 | - <template #default="{ row }"> | 152 | + <tbody> |
| 137 | - <el-tag :type="getOperateTypeTagType(row.operateType)" size="small"> | 153 | + <tr v-if="rebateList.length === 0"> |
| 138 | - {{ row.operateTypeText || getOperateTypeText(row.operateType) }} | 154 | + <td colspan="11" class="empty-data">暂无数据</td> |
| 139 | - </el-tag> | 155 | + </tr> |
| 140 | - </template> | 156 | + <tr v-for="row in rebateList" :key="row.rebateId"> |
| 141 | - </el-table-column> | 157 | + <td>{{ row.rebateNo }}</td> |
| 142 | - <el-table-column prop="calcFlagText" label="计算状态" width="100" align="center"> | 158 | + <td>{{ row.orderNo }}</td> |
| 143 | - <template #default="{ row }"> | 159 | + <td>{{ row.dealerCode }}</td> |
| 144 | - <el-tag :type="getCalcFlagTagType(row.calcFlag)" size="small"> | 160 | + <td>{{ row.dealerName }}</td> |
| 145 | - {{ row.calcFlagText || getCalcFlagText(row.calcFlag) }} | 161 | + <td>{{ row.productCode }}</td> |
| 146 | - </el-tag> | 162 | + <td class="amount-text">{{ formatAmount(row.rebateAmount) }}</td> |
| 147 | - </template> | 163 | + <td>{{ row.rebateDate }}</td> |
| 148 | - </el-table-column> | 164 | + <td> |
| 149 | - <el-table-column prop="updateTime" label="更新时间" width="150" align="center"> | 165 | + <span :class="getOperateTypeClass(row.operateType || 0)"> |
| 150 | - <template #default="{ row }"> | 166 | + {{ row.operateTypeText || getOperateTypeText(row.operateType || 0) }} |
| 151 | - {{ formatDate(row.updateTime) }} | 167 | + </span> |
| 152 | - </template> | 168 | + </td> |
| 153 | - </el-table-column> | 169 | + <td> |
| 154 | - <el-table-column label="操作" width="80" align="center"> | 170 | + <span :class="getCalcFlagClass(row.calcFlag || 0)"> |
| 155 | - <template #default="{ row }"> | 171 | + {{ row.calcFlagText || getCalcFlagText(row.calcFlag || 0) }} |
| 156 | - <el-button type="primary" size="small" link @click="handleViewDetail(row)"> | 172 | + </span> |
| 157 | - 详情 | 173 | + </td> |
| 158 | - </el-button> | 174 | + <td>{{ formatDate(row.updateTime || '') }}</td> |
| 159 | - </template> | 175 | + <td> |
| 160 | - </el-table-column> | 176 | + <button @click="handleViewDetail(row)" class="action-link">详情</button> |
| 161 | - </el-table> | 177 | + </td> |
| 178 | + </tr> | ||
| 179 | + </tbody> | ||
| 180 | + </table> | ||
| 181 | + </div> | ||
| 162 | 182 | ||
| 163 | <!-- 分页 --> | 183 | <!-- 分页 --> |
| 164 | <div class="pagination-wrapper"> | 184 | <div class="pagination-wrapper"> |
| 165 | <div class="pagination-info"> | 185 | <div class="pagination-info"> |
| 166 | 共 {{ pagination.total }} 条,{{ pagination.pageSize }}/页 | 186 | 共 {{ pagination.total }} 条,{{ pagination.pageSize }}/页 |
| 167 | </div> | 187 | </div> |
| 168 | - <el-pagination | 188 | + <div class="pagination-controls"> |
| 169 | - v-model:current-page="pagination.pageNum" | 189 | + <button @click="handlePrevPage" :disabled="pagination.pageNum <= 1" class="page-btn">‹</button> |
| 170 | - v-model:page-size="pagination.pageSize" | 190 | + <button |
| 171 | - :page-sizes="[10, 20, 50, 100]" | 191 | + v-for="page in getPageNumbers()" |
| 172 | - :total="pagination.total" | 192 | + :key="page" |
| 173 | - layout="sizes, prev, pager, next" | 193 | + @click="handlePageChange(page)" |
| 174 | - @size-change="handleSizeChange" | 194 | + :class="['page-btn', { active: page === pagination.pageNum }]" |
| 175 | - @current-change="handleCurrentChange" | 195 | + > |
| 176 | - small | 196 | + {{ page }} |
| 177 | - /> | 197 | + </button> |
| 198 | + <button @click="handleNextPage" :disabled="pagination.pageNum >= getTotalPages()" class="page-btn">›</button> | ||
| 199 | + </div> | ||
| 178 | <div class="pagination-jump"> | 200 | <div class="pagination-jump"> |
| 179 | 前往 | 201 | 前往 |
| 180 | - <el-input | 202 | + <input |
| 181 | v-model="jumpPage" | 203 | v-model="jumpPage" |
| 182 | - size="small" | 204 | + class="jump-input" |
| 183 | - style="width: 50px; margin: 0 8px;" | ||
| 184 | @keyup.enter="handleJumpPage" | 205 | @keyup.enter="handleJumpPage" |
| 185 | /> | 206 | /> |
| 186 | 页 | 207 | 页 |
| 187 | - </div> | 208 | + </div> |
| 188 | </div> | 209 | </div> |
| 189 | </div> | 210 | </div> |
| 190 | 211 | ||
| ... | @@ -435,6 +456,8 @@ const searchForm = reactive<RebateSearchParams>({ | ... | @@ -435,6 +456,8 @@ const searchForm = reactive<RebateSearchParams>({ |
| 435 | productCode: '', | 456 | productCode: '', |
| 436 | operateType: undefined, | 457 | operateType: undefined, |
| 437 | calcFlag: undefined, | 458 | calcFlag: undefined, |
| 459 | + auditStatus: undefined, | ||
| 460 | + dataSource: '', | ||
| 438 | rebateStartDate: '', | 461 | rebateStartDate: '', |
| 439 | rebateEndDate: '' | 462 | rebateEndDate: '' |
| 440 | }) | 463 | }) |
| ... | @@ -730,15 +753,17 @@ const handleReset = () => { | ... | @@ -730,15 +753,17 @@ const handleReset = () => { |
| 730 | Object.assign(searchForm, { | 753 | Object.assign(searchForm, { |
| 731 | pageNum: 1, | 754 | pageNum: 1, |
| 732 | pageSize: 10, | 755 | pageSize: 10, |
| 733 | - rebateNo: undefined, | 756 | + rebateNo: '', |
| 734 | - dealerName: undefined, | 757 | + dealerCode: '', |
| 735 | - productName: undefined, | 758 | + dealerName: '', |
| 736 | - rebateType: undefined, | 759 | + productCode: '', |
| 737 | - status: undefined, | 760 | + operateType: undefined, |
| 738 | - applyStartTime: undefined, | 761 | + calcFlag: undefined, |
| 739 | - applyEndTime: undefined | 762 | + auditStatus: undefined, |
| 763 | + dataSource: '', | ||
| 764 | + rebateStartDate: '', | ||
| 765 | + rebateEndDate: '' | ||
| 740 | }) | 766 | }) |
| 741 | - applyDateRange.value = null | ||
| 742 | pagination.pageNum = 1 | 767 | pagination.pageNum = 1 |
| 743 | getRebateList() | 768 | getRebateList() |
| 744 | } | 769 | } |
| ... | @@ -748,6 +773,104 @@ const handleRefresh = () => { | ... | @@ -748,6 +773,104 @@ const handleRefresh = () => { |
| 748 | getRebateList() | 773 | getRebateList() |
| 749 | } | 774 | } |
| 750 | 775 | ||
| 776 | +// 表格控制按钮 | ||
| 777 | +const handleTableSearch = () => { | ||
| 778 | + handleSearch() | ||
| 779 | +} | ||
| 780 | + | ||
| 781 | +const handleTableRefresh = () => { | ||
| 782 | + handleRefresh() | ||
| 783 | +} | ||
| 784 | + | ||
| 785 | +const handleTableExport = () => { | ||
| 786 | + handleExport() | ||
| 787 | +} | ||
| 788 | + | ||
| 789 | +const handleTableViewToggle = () => { | ||
| 790 | + // 切换视图模式 | ||
| 791 | + console.log('切换视图模式') | ||
| 792 | +} | ||
| 793 | + | ||
| 794 | +// 分页控制 | ||
| 795 | +const handlePrevPage = () => { | ||
| 796 | + if (pagination.pageNum > 1) { | ||
| 797 | + pagination.pageNum-- | ||
| 798 | + getRebateList() | ||
| 799 | + } | ||
| 800 | +} | ||
| 801 | + | ||
| 802 | +const handleNextPage = () => { | ||
| 803 | + if (pagination.pageNum < getTotalPages()) { | ||
| 804 | + pagination.pageNum++ | ||
| 805 | + getRebateList() | ||
| 806 | + } | ||
| 807 | +} | ||
| 808 | + | ||
| 809 | +const handlePageChange = (page: number | string) => { | ||
| 810 | + if (typeof page === 'number') { | ||
| 811 | + pagination.pageNum = page | ||
| 812 | + getRebateList() | ||
| 813 | + } | ||
| 814 | +} | ||
| 815 | + | ||
| 816 | +const getPageNumbers = () => { | ||
| 817 | + const totalPages = getTotalPages() | ||
| 818 | + const current = pagination.pageNum | ||
| 819 | + const pages = [] | ||
| 820 | + | ||
| 821 | + if (totalPages <= 7) { | ||
| 822 | + for (let i = 1; i <= totalPages; i++) { | ||
| 823 | + pages.push(i) | ||
| 824 | + } | ||
| 825 | + } else { | ||
| 826 | + if (current <= 4) { | ||
| 827 | + for (let i = 1; i <= 5; i++) { | ||
| 828 | + pages.push(i) | ||
| 829 | + } | ||
| 830 | + pages.push('...') | ||
| 831 | + pages.push(totalPages) | ||
| 832 | + } else if (current >= totalPages - 3) { | ||
| 833 | + pages.push(1) | ||
| 834 | + pages.push('...') | ||
| 835 | + for (let i = totalPages - 4; i <= totalPages; i++) { | ||
| 836 | + pages.push(i) | ||
| 837 | + } | ||
| 838 | + } else { | ||
| 839 | + pages.push(1) | ||
| 840 | + pages.push('...') | ||
| 841 | + for (let i = current - 1; i <= current + 1; i++) { | ||
| 842 | + pages.push(i) | ||
| 843 | + } | ||
| 844 | + pages.push('...') | ||
| 845 | + pages.push(totalPages) | ||
| 846 | + } | ||
| 847 | + } | ||
| 848 | + | ||
| 849 | + return pages | ||
| 850 | +} | ||
| 851 | + | ||
| 852 | +const getTotalPages = () => { | ||
| 853 | + return Math.ceil(pagination.total / pagination.pageSize) | ||
| 854 | +} | ||
| 855 | + | ||
| 856 | +// 状态样式类 | ||
| 857 | +const getOperateTypeClass = (type: number) => { | ||
| 858 | + const classes = { | ||
| 859 | + 1: 'status-tag success', | ||
| 860 | + 2: 'status-tag warning', | ||
| 861 | + 3: 'status-tag danger' | ||
| 862 | + } | ||
| 863 | + return classes[type as keyof typeof classes] || 'status-tag' | ||
| 864 | +} | ||
| 865 | + | ||
| 866 | +const getCalcFlagClass = (flag: number) => { | ||
| 867 | + const classes = { | ||
| 868 | + 0: 'status-tag warning', | ||
| 869 | + 1: 'status-tag success' | ||
| 870 | + } | ||
| 871 | + return classes[flag as keyof typeof classes] || 'status-tag' | ||
| 872 | +} | ||
| 873 | + | ||
| 751 | // 分页变化 | 874 | // 分页变化 |
| 752 | const handleSizeChange = (size: number) => { | 875 | const handleSizeChange = (size: number) => { |
| 753 | pagination.pageSize = size | 876 | pagination.pageSize = size |
| ... | @@ -784,11 +907,11 @@ const handleViewDetail = async (row: Rebate) => { | ... | @@ -784,11 +907,11 @@ const handleViewDetail = async (row: Rebate) => { |
| 784 | const response = await rebateApi.getRebateById(row.rebateId) | 907 | const response = await rebateApi.getRebateById(row.rebateId) |
| 785 | console.log('详情接口响应:', response) | 908 | console.log('详情接口响应:', response) |
| 786 | 909 | ||
| 787 | - // 根据request.ts拦截器的处理,response.data已经是实际数据 | 910 | + // 根据request.ts拦截器的处理,response直接就是数据对象 |
| 788 | - if (response && response.data) { | 911 | + if (response) { |
| 789 | - rebateDetail.value = response.data | 912 | + rebateDetail.value = response as unknown as Rebate |
| 790 | - detailDialogVisible.value = true | 913 | + detailDialogVisible.value = true |
| 791 | - console.log('详情数据:', response.data) | 914 | + console.log('详情数据:', response) |
| 792 | } else { | 915 | } else { |
| 793 | ElMessage.error('获取返利详情失败') | 916 | ElMessage.error('获取返利详情失败') |
| 794 | } | 917 | } |
| ... | @@ -1478,30 +1601,154 @@ onMounted(async () => { | ... | @@ -1478,30 +1601,154 @@ onMounted(async () => { |
| 1478 | border-radius: 4px; | 1601 | border-radius: 4px; |
| 1479 | box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1); | 1602 | box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1); |
| 1480 | 1603 | ||
| 1481 | - .search-form { | 1604 | + .search-row { |
| 1482 | - .search-row { | 1605 | + display: flex; |
| 1606 | + align-items: center; | ||
| 1607 | + gap: 24px; | ||
| 1608 | + flex-wrap: wrap; | ||
| 1609 | + margin-bottom: 12px; | ||
| 1610 | + | ||
| 1611 | + &:last-child { | ||
| 1612 | + margin-bottom: 0; | ||
| 1613 | + } | ||
| 1614 | + | ||
| 1615 | + .search-item { | ||
| 1483 | display: flex; | 1616 | display: flex; |
| 1484 | align-items: center; | 1617 | align-items: center; |
| 1485 | - gap: 24px; | 1618 | + gap: 8px; |
| 1486 | - flex-wrap: wrap; | ||
| 1487 | 1619 | ||
| 1488 | - .search-item { | 1620 | + label { |
| 1489 | - display: flex; | 1621 | + font-size: 14px; |
| 1490 | - align-items: center; | 1622 | + color: #333; |
| 1491 | - gap: 8px; | 1623 | + white-space: nowrap; |
| 1624 | + min-width: 80px; | ||
| 1625 | + } | ||
| 1492 | 1626 | ||
| 1493 | - .search-label { | 1627 | + .search-input { |
| 1494 | - font-size: 14px; | 1628 | + padding: 8px 12px; |
| 1495 | - color: #333; | 1629 | + border: 1px solid #ddd; |
| 1496 | - white-space: nowrap; | 1630 | + border-radius: 4px; |
| 1497 | - min-width: 70px; | 1631 | + font-size: 14px; |
| 1632 | + width: 160px; | ||
| 1633 | + transition: border-color 0.3s; | ||
| 1634 | + | ||
| 1635 | + &:focus { | ||
| 1636 | + outline: none; | ||
| 1637 | + border-color: #409eff; | ||
| 1498 | } | 1638 | } |
| 1499 | } | 1639 | } |
| 1500 | 1640 | ||
| 1501 | - .search-actions { | 1641 | + .search-select { |
| 1502 | - margin-left: auto; | 1642 | + padding: 8px 12px; |
| 1503 | - display: flex; | 1643 | + border: 1px solid #ddd; |
| 1504 | - gap: 8px; | 1644 | + border-radius: 4px; |
| 1645 | + font-size: 14px; | ||
| 1646 | + width: 120px; | ||
| 1647 | + background: white; | ||
| 1648 | + cursor: pointer; | ||
| 1649 | + transition: border-color 0.3s; | ||
| 1650 | + | ||
| 1651 | + &:focus { | ||
| 1652 | + outline: none; | ||
| 1653 | + border-color: #409eff; | ||
| 1654 | + } | ||
| 1655 | + } | ||
| 1656 | + } | ||
| 1657 | + | ||
| 1658 | + .search-actions { | ||
| 1659 | + margin-left: auto; | ||
| 1660 | + display: flex; | ||
| 1661 | + gap: 8px; | ||
| 1662 | + | ||
| 1663 | + .search-btn, .reset-btn { | ||
| 1664 | + padding: 8px 16px; | ||
| 1665 | + border: 1px solid #ddd; | ||
| 1666 | + border-radius: 4px; | ||
| 1667 | + font-size: 14px; | ||
| 1668 | + cursor: pointer; | ||
| 1669 | + transition: all 0.3s; | ||
| 1670 | + background: white; | ||
| 1671 | + | ||
| 1672 | + &:hover { | ||
| 1673 | + background: #f5f7fa; | ||
| 1674 | + } | ||
| 1675 | + | ||
| 1676 | + &:disabled { | ||
| 1677 | + opacity: 0.6; | ||
| 1678 | + cursor: not-allowed; | ||
| 1679 | + } | ||
| 1680 | + } | ||
| 1681 | + | ||
| 1682 | + .search-btn { | ||
| 1683 | + background: #409eff; | ||
| 1684 | + color: white; | ||
| 1685 | + border-color: #409eff; | ||
| 1686 | + | ||
| 1687 | + &:hover { | ||
| 1688 | + background: #66b1ff; | ||
| 1689 | + } | ||
| 1690 | + } | ||
| 1691 | + } | ||
| 1692 | + } | ||
| 1693 | +} | ||
| 1694 | + | ||
| 1695 | +.action-section { | ||
| 1696 | + background: white; | ||
| 1697 | + padding: 12px 20px; | ||
| 1698 | + margin-bottom: 16px; | ||
| 1699 | + border-radius: 4px; | ||
| 1700 | + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1); | ||
| 1701 | + | ||
| 1702 | + .action-buttons { | ||
| 1703 | + display: flex; | ||
| 1704 | + gap: 12px; | ||
| 1705 | + | ||
| 1706 | + .action-btn { | ||
| 1707 | + padding: 8px 16px; | ||
| 1708 | + border: 1px solid #ddd; | ||
| 1709 | + border-radius: 4px; | ||
| 1710 | + font-size: 14px; | ||
| 1711 | + cursor: pointer; | ||
| 1712 | + transition: all 0.3s; | ||
| 1713 | + background: white; | ||
| 1714 | + | ||
| 1715 | + &:hover { | ||
| 1716 | + background: #f5f7fa; | ||
| 1717 | + } | ||
| 1718 | + | ||
| 1719 | + &:disabled { | ||
| 1720 | + opacity: 0.6; | ||
| 1721 | + cursor: not-allowed; | ||
| 1722 | + } | ||
| 1723 | + | ||
| 1724 | + &.primary { | ||
| 1725 | + background: #409eff; | ||
| 1726 | + color: white; | ||
| 1727 | + border-color: #409eff; | ||
| 1728 | + | ||
| 1729 | + &:hover { | ||
| 1730 | + background: #66b1ff; | ||
| 1731 | + } | ||
| 1732 | + } | ||
| 1733 | + | ||
| 1734 | + &.secondary { | ||
| 1735 | + background: #67c23a; | ||
| 1736 | + color: white; | ||
| 1737 | + border-color: #67c23a; | ||
| 1738 | + | ||
| 1739 | + &:hover { | ||
| 1740 | + background: #85ce61; | ||
| 1741 | + } | ||
| 1742 | + } | ||
| 1743 | + | ||
| 1744 | + &.danger { | ||
| 1745 | + background: #f56c6c; | ||
| 1746 | + color: white; | ||
| 1747 | + border-color: #f56c6c; | ||
| 1748 | + | ||
| 1749 | + &:hover { | ||
| 1750 | + background: #f78989; | ||
| 1751 | + } | ||
| 1505 | } | 1752 | } |
| 1506 | } | 1753 | } |
| 1507 | } | 1754 | } |
| ... | @@ -1580,61 +1827,113 @@ onMounted(async () => { | ... | @@ -1580,61 +1827,113 @@ onMounted(async () => { |
| 1580 | 1827 | ||
| 1581 | .table-section { | 1828 | .table-section { |
| 1582 | background: white; | 1829 | background: white; |
| 1583 | - border-radius: 8px; | 1830 | + border-radius: 4px; |
| 1584 | - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); | 1831 | + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1); |
| 1585 | overflow: hidden; | 1832 | overflow: hidden; |
| 1586 | 1833 | ||
| 1587 | .table-header { | 1834 | .table-header { |
| 1588 | display: flex; | 1835 | display: flex; |
| 1589 | - justify-content: space-between; | 1836 | + justify-content: flex-end; |
| 1590 | align-items: center; | 1837 | align-items: center; |
| 1591 | - padding: 20px 20px 0 20px; | 1838 | + padding: 12px 20px; |
| 1592 | - margin-bottom: 16px; | 1839 | + border-bottom: 1px solid #f0f0f0; |
| 1840 | + background: #fafafa; | ||
| 1593 | 1841 | ||
| 1594 | - .table-title { | 1842 | + .table-controls { |
| 1595 | - font-size: 16px; | 1843 | + display: flex; |
| 1596 | - font-weight: 500; | 1844 | + gap: 8px; |
| 1597 | - color: #333; | 1845 | + |
| 1846 | + .control-btn { | ||
| 1847 | + padding: 6px 12px; | ||
| 1848 | + border: 1px solid #ddd; | ||
| 1849 | + border-radius: 4px; | ||
| 1850 | + background: white; | ||
| 1851 | + cursor: pointer; | ||
| 1852 | + font-size: 14px; | ||
| 1853 | + transition: all 0.3s; | ||
| 1854 | + | ||
| 1855 | + &:hover { | ||
| 1856 | + background: #f5f7fa; | ||
| 1857 | + border-color: #409eff; | ||
| 1858 | + } | ||
| 1859 | + } | ||
| 1598 | } | 1860 | } |
| 1861 | + } | ||
| 1599 | 1862 | ||
| 1600 | - .table-actions { | 1863 | + .table-container { |
| 1864 | + position: relative; | ||
| 1865 | + | ||
| 1866 | + .loading-overlay { | ||
| 1867 | + position: absolute; | ||
| 1868 | + top: 0; | ||
| 1869 | + left: 0; | ||
| 1870 | + right: 0; | ||
| 1871 | + bottom: 0; | ||
| 1872 | + background: rgba(255, 255, 255, 0.8); | ||
| 1601 | display: flex; | 1873 | display: flex; |
| 1602 | align-items: center; | 1874 | align-items: center; |
| 1603 | - gap: 12px; | 1875 | + justify-content: center; |
| 1876 | + z-index: 10; | ||
| 1604 | 1877 | ||
| 1605 | - .table-info { | 1878 | + .loading-spinner { |
| 1606 | - font-size: 12px; | 1879 | + font-size: 14px; |
| 1607 | - color: #999; | 1880 | + color: #666; |
| 1608 | } | 1881 | } |
| 1609 | } | 1882 | } |
| 1610 | - } | ||
| 1611 | 1883 | ||
| 1612 | - .el-table { | 1884 | + .data-table { |
| 1613 | - border: none; | 1885 | + width: 100%; |
| 1886 | + border-collapse: collapse; | ||
| 1887 | + font-size: 14px; | ||
| 1614 | 1888 | ||
| 1615 | - :deep(.el-table__header) { | 1889 | + thead { |
| 1616 | - th { | 1890 | + background: #fafafa; |
| 1617 | - background-color: #fafafa; | ||
| 1618 | - border: none; | ||
| 1619 | - font-weight: 500; | ||
| 1620 | - color: #333; | ||
| 1621 | - } | ||
| 1622 | - } | ||
| 1623 | 1891 | ||
| 1624 | - :deep(.el-table__body) { | 1892 | + th { |
| 1625 | - tr:hover > td { | 1893 | + padding: 12px 16px; |
| 1626 | - background-color: #f5f7fa; | 1894 | + text-align: left; |
| 1895 | + font-weight: 500; | ||
| 1896 | + color: #333; | ||
| 1897 | + border-bottom: 1px solid #e0e0e0; | ||
| 1898 | + white-space: nowrap; | ||
| 1899 | + } | ||
| 1627 | } | 1900 | } |
| 1628 | 1901 | ||
| 1629 | - td { | 1902 | + tbody { |
| 1630 | - border: none; | 1903 | + tr { |
| 1631 | - border-bottom: 1px solid #f0f0f0; | 1904 | + transition: background-color 0.3s; |
| 1905 | + | ||
| 1906 | + &:hover { | ||
| 1907 | + background: #f5f7fa; | ||
| 1908 | + } | ||
| 1909 | + | ||
| 1910 | + &:nth-child(even) { | ||
| 1911 | + background: #fafafa; | ||
| 1912 | + } | ||
| 1913 | + | ||
| 1914 | + &:nth-child(even):hover { | ||
| 1915 | + background: #f0f2f5; | ||
| 1916 | + } | ||
| 1917 | + | ||
| 1918 | + td { | ||
| 1919 | + padding: 12px 16px; | ||
| 1920 | + border-bottom: 1px solid #f0f0f0; | ||
| 1921 | + vertical-align: middle; | ||
| 1922 | + | ||
| 1923 | + &.empty-data { | ||
| 1924 | + text-align: center; | ||
| 1925 | + color: #999; | ||
| 1926 | + font-style: italic; | ||
| 1927 | + padding: 40px; | ||
| 1928 | + } | ||
| 1929 | + } | ||
| 1930 | + } | ||
| 1632 | } | 1931 | } |
| 1633 | } | 1932 | } |
| 1634 | } | 1933 | } |
| 1635 | 1934 | ||
| 1636 | .pagination-wrapper { | 1935 | .pagination-wrapper { |
| 1637 | - display: flex; | 1936 | + display: flex; |
| 1638 | justify-content: space-between; | 1937 | justify-content: space-between; |
| 1639 | align-items: center; | 1938 | align-items: center; |
| 1640 | padding: 12px 20px; | 1939 | padding: 12px 20px; |
| ... | @@ -1646,15 +1945,100 @@ onMounted(async () => { | ... | @@ -1646,15 +1945,100 @@ onMounted(async () => { |
| 1646 | color: #666; | 1945 | color: #666; |
| 1647 | } | 1946 | } |
| 1648 | 1947 | ||
| 1948 | + .pagination-controls { | ||
| 1949 | + display: flex; | ||
| 1950 | + gap: 4px; | ||
| 1951 | + align-items: center; | ||
| 1952 | + | ||
| 1953 | + .page-btn { | ||
| 1954 | + padding: 6px 12px; | ||
| 1955 | + border: 1px solid #ddd; | ||
| 1956 | + border-radius: 4px; | ||
| 1957 | + background: white; | ||
| 1958 | + cursor: pointer; | ||
| 1959 | + font-size: 14px; | ||
| 1960 | + transition: all 0.3s; | ||
| 1961 | + min-width: 32px; | ||
| 1962 | + | ||
| 1963 | + &:hover:not(:disabled) { | ||
| 1964 | + background: #f5f7fa; | ||
| 1965 | + border-color: #409eff; | ||
| 1966 | + } | ||
| 1967 | + | ||
| 1968 | + &:disabled { | ||
| 1969 | + opacity: 0.5; | ||
| 1970 | + cursor: not-allowed; | ||
| 1971 | + } | ||
| 1972 | + | ||
| 1973 | + &.active { | ||
| 1974 | + background: #409eff; | ||
| 1975 | + color: white; | ||
| 1976 | + border-color: #409eff; | ||
| 1977 | + } | ||
| 1978 | + } | ||
| 1979 | + } | ||
| 1980 | + | ||
| 1649 | .pagination-jump { | 1981 | .pagination-jump { |
| 1650 | display: flex; | 1982 | display: flex; |
| 1651 | align-items: center; | 1983 | align-items: center; |
| 1984 | + gap: 8px; | ||
| 1652 | font-size: 14px; | 1985 | font-size: 14px; |
| 1653 | color: #666; | 1986 | color: #666; |
| 1987 | + | ||
| 1988 | + .jump-input { | ||
| 1989 | + width: 50px; | ||
| 1990 | + padding: 4px 8px; | ||
| 1991 | + border: 1px solid #ddd; | ||
| 1992 | + border-radius: 4px; | ||
| 1993 | + text-align: center; | ||
| 1994 | + font-size: 14px; | ||
| 1995 | + | ||
| 1996 | + &:focus { | ||
| 1997 | + outline: none; | ||
| 1998 | + border-color: #409eff; | ||
| 1999 | + } | ||
| 2000 | + } | ||
| 1654 | } | 2001 | } |
| 1655 | } | 2002 | } |
| 1656 | } | 2003 | } |
| 1657 | 2004 | ||
| 2005 | +.status-tag { | ||
| 2006 | + padding: 2px 8px; | ||
| 2007 | + border-radius: 12px; | ||
| 2008 | + font-size: 12px; | ||
| 2009 | + font-weight: 500; | ||
| 2010 | + | ||
| 2011 | + &.success { | ||
| 2012 | + background: #f0f9ff; | ||
| 2013 | + color: #67c23a; | ||
| 2014 | + } | ||
| 2015 | + | ||
| 2016 | + &.warning { | ||
| 2017 | + background: #fdf6ec; | ||
| 2018 | + color: #e6a23c; | ||
| 2019 | + } | ||
| 2020 | + | ||
| 2021 | + &.danger { | ||
| 2022 | + background: #fef0f0; | ||
| 2023 | + color: #f56c6c; | ||
| 2024 | + } | ||
| 2025 | +} | ||
| 2026 | + | ||
| 2027 | +.action-link { | ||
| 2028 | + color: #409eff; | ||
| 2029 | + text-decoration: none; | ||
| 2030 | + cursor: pointer; | ||
| 2031 | + font-size: 14px; | ||
| 2032 | + background: none; | ||
| 2033 | + border: none; | ||
| 2034 | + padding: 0; | ||
| 2035 | + | ||
| 2036 | + &:hover { | ||
| 2037 | + color: #66b1ff; | ||
| 2038 | + text-decoration: underline; | ||
| 2039 | + } | ||
| 2040 | +} | ||
| 2041 | + | ||
| 1658 | .amount-text { | 2042 | .amount-text { |
| 1659 | color: #f56c6c; | 2043 | color: #f56c6c; |
| 1660 | font-weight: 500; | 2044 | font-weight: 500; | ... | ... |
-
Please register or login to post a comment