subordinates.vue 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. <template>
  2. <div class="me-subordinates">
  3. <div class="subordinates-header">
  4. <h2 class="subordinates-title">我的下级</h2>
  5. </div>
  6. <div class="subordinates-content">
  7. <!-- 过滤/搜索区域 + 汇总指标 -->
  8. <div class="filter-section">
  9. <div class="filter-left">
  10. <el-date-picker
  11. v-model="searchForm.dateRange"
  12. class="filter-item date-picker"
  13. type="daterange"
  14. unlink-panels
  15. start-placeholder="开始日期"
  16. end-placeholder="结束日期"
  17. range-separator="~"
  18. value-format="yyyy-MM-dd"
  19. />
  20. <el-input
  21. v-model="searchForm.name"
  22. placeholder="请输入名称"
  23. clearable
  24. class="filter-item name-input"
  25. @keyup.enter="handleSearch"
  26. />
  27. <el-button type="primary" :icon="Search" size="small" class="search-btn" @click="handleSearch">
  28. 搜索
  29. </el-button>
  30. </div>
  31. <!-- 汇总指标 -->
  32. <div class="summary-item">
  33. <span class="summary-label">分拥余额:</span>
  34. <span class="summary-value">
  35. {{ totalCommission }}
  36. </span>
  37. </div>
  38. </div>
  39. <!-- 数据表格 -->
  40. <div class="table-section">
  41. <el-table
  42. :data="tableData"
  43. style="width: 100%"
  44. :header-cell-style="{ background: '#f5f7fa', color: '#606266', fontWeight: '500' }"
  45. >
  46. <el-table-column prop="id" label="ID" width="80" align="center" />
  47. <el-table-column prop="userName" label="用户名称" min-width="120" align="center" />
  48. <el-table-column prop="email" label="邮箱" min-width="180" align="center" />
  49. <el-table-column prop="commissionAmount" label="已得分佣金额" width="140" align="center" />
  50. <el-table-column prop="registerTime" label="注册时间" width="180" align="center" />
  51. <el-table-column prop="status" label="状态" width="100" align="center">
  52. <template #default="{ row }">
  53. <span class="status-text">{{ row.status }}</span>
  54. </template>
  55. </el-table-column>
  56. <el-table-column prop="operation" label="操作" width="100" align="center">
  57. <template #default>
  58. <span class="status-text">详情</span>
  59. </template>
  60. </el-table-column>
  61. </el-table>
  62. </div>
  63. <!-- 分页控件 -->
  64. <div class="pagination-section">
  65. <el-pagination
  66. :current-page="currentPage"
  67. :page-size="pageSize"
  68. :page-sizes="pageSizes"
  69. :total="total"
  70. layout="total, sizes, prev, pager, next, jumper"
  71. background
  72. @size-change="handlePageSizeChange"
  73. @current-change="handleCurrentPageChange"
  74. />
  75. </div>
  76. </div>
  77. </div>
  78. </template>
  79. <script setup lang="ts">
  80. import { reactive, ref } from 'vue'
  81. import { Search } from '@element-plus/icons-vue'
  82. defineOptions({ name: 'MeSubordinates' })
  83. type TableRow = {
  84. id: number | string
  85. userName: string
  86. email: string
  87. commissionAmount: number
  88. registerTime: string
  89. status: string
  90. }
  91. const searchForm = reactive<{ dateRange: string[]; name: string }>({
  92. dateRange: ['2025-10-01', '2025-10-01'],
  93. name: ''
  94. })
  95. const totalCommission = ref(30)
  96. const tableData = ref<TableRow[]>([
  97. { id: '-', userName: '汇总', email: '-', commissionAmount: 30, registerTime: '-', status: '-' },
  98. { id: 1, userName: '张三', email: 'XXX.qq.com', commissionAmount: 10, registerTime: '2025-10-01 14:32:11', status: '正常' },
  99. { id: 10, userName: '李四', email: 'XXX.qq.com', commissionAmount: 20, registerTime: '2025-10-01 14:32:11', status: '正常' }
  100. ])
  101. const currentPage = ref(1)
  102. const pageSize = ref(10)
  103. const pageSizes = [10, 20, 30, 50]
  104. const total = ref(1)
  105. const loadData = () => {
  106. // TODO: 接入实际 API 获取数据
  107. console.log('搜索条件:', { ...searchForm })
  108. }
  109. const handleSearch = () => {
  110. currentPage.value = 1
  111. loadData()
  112. }
  113. const handlePageSizeChange = (val: number) => {
  114. pageSize.value = val
  115. currentPage.value = 1
  116. loadData()
  117. }
  118. const handleCurrentPageChange = (val: number) => {
  119. currentPage.value = val
  120. loadData()
  121. }
  122. </script>
  123. <style scoped lang="scss">
  124. .me-subordinates {
  125. width: 100%;
  126. .subordinates-header {
  127. margin-bottom: 20px;
  128. padding-bottom: 15px;
  129. border-bottom: 1px solid #e4e7ed;
  130. .subordinates-title {
  131. margin: 0;
  132. font-size: 20px;
  133. font-weight: 500;
  134. color: #303133;
  135. }
  136. }
  137. .subordinates-content {
  138. padding: 20px 0;
  139. color: #606266;
  140. }
  141. .filter-section {
  142. display: flex;
  143. justify-content: space-between;
  144. align-items: center;
  145. flex-wrap: wrap;
  146. gap: 16px;
  147. margin-bottom: 20px;
  148. padding: 20px 24px;
  149. background: linear-gradient(135deg, #f5f7fa 0%, #ffffff 100%);
  150. border-radius: 12px;
  151. border: 1px solid #e4e7ed;
  152. box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
  153. .filter-left {
  154. display: flex;
  155. flex-wrap: wrap;
  156. gap: 12px;
  157. flex: 1;
  158. min-width: 300px;
  159. }
  160. .filter-item {
  161. flex: 1;
  162. min-width: 200px;
  163. }
  164. .date-picker {
  165. max-width: 360px;
  166. }
  167. .name-input {
  168. max-width: 320px;
  169. }
  170. .search-btn {
  171. height: 32px;
  172. padding: 0 20px;
  173. font-weight: 500;
  174. box-shadow: 0 2px 4px rgba(64, 158, 255, 0.2);
  175. transition: all 0.3s ease;
  176. &:hover {
  177. box-shadow: 0 4px 8px rgba(64, 158, 255, 0.3);
  178. transform: translateY(-1px);
  179. }
  180. &:active {
  181. transform: translateY(0);
  182. }
  183. }
  184. .summary-item {
  185. display: flex;
  186. align-items: center;
  187. padding: 8px 10px;
  188. border-radius: 10px;
  189. box-shadow: 0 2px 6px rgba(104, 151, 238, 0.15);
  190. white-space: nowrap;
  191. flex-shrink: 0;
  192. .summary-label {
  193. color: #8c6e1f;
  194. font-size: 14px;
  195. font-weight: 500;
  196. }
  197. .summary-value {
  198. position: relative;
  199. color: #d48806;
  200. font-weight: 600;
  201. font-size: 20px;
  202. letter-spacing: 0.5px;
  203. .summary-badge {
  204. position: absolute;
  205. top: -10px;
  206. right: -20px;
  207. display: inline-flex;
  208. align-items: center;
  209. justify-content: center;
  210. min-width: 20px;
  211. height: 20px;
  212. padding: 0 5px;
  213. background: linear-gradient(135deg, #ff9800 0%, #f57c00 100%);
  214. color: #fff;
  215. border-radius: 10px;
  216. font-size: 11px;
  217. font-weight: 600;
  218. line-height: 1;
  219. box-shadow: 0 2px 4px rgba(255, 152, 0, 0.4);
  220. border: 2px solid #fff;
  221. }
  222. }
  223. }
  224. }
  225. .table-section {
  226. margin-bottom: 20px;
  227. background: #fff;
  228. border-radius: 12px;
  229. overflow: hidden;
  230. box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
  231. ::v-deep .el-table {
  232. border: none;
  233. &::before {
  234. display: none;
  235. }
  236. .el-table__header-wrapper {
  237. .el-table__header {
  238. th {
  239. border-bottom: 2px solid #f0f2f5;
  240. border-right: none;
  241. }
  242. }
  243. }
  244. .el-table__body-wrapper {
  245. .el-table__body {
  246. tr {
  247. td {
  248. border-bottom: 1px solid #f5f7fa;
  249. border-right: none;
  250. }
  251. &:hover {
  252. background-color: #fafbfc;
  253. }
  254. &:last-child {
  255. td {
  256. border-bottom: none;
  257. }
  258. }
  259. }
  260. }
  261. }
  262. .status-text {
  263. color: #409eff;
  264. font-weight: 500;
  265. }
  266. }
  267. }
  268. .pagination-section {
  269. display: flex;
  270. justify-content: flex-end;
  271. align-items: center;
  272. padding: 16px 20px;
  273. background: #fff;
  274. border-radius: 12px;
  275. box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
  276. ::v-deep .el-pagination {
  277. .el-pagination__total {
  278. color: #606266;
  279. font-size: 14px;
  280. font-weight: 500;
  281. }
  282. }
  283. }
  284. }
  285. @media (max-width: 768px) {
  286. .me-subordinates {
  287. .filter-section {
  288. flex-direction: column;
  289. align-items: stretch;
  290. .filter-left {
  291. width: 100%;
  292. }
  293. .filter-item,
  294. .date-picker,
  295. .name-input {
  296. width: 100%;
  297. max-width: none;
  298. }
  299. .search-btn {
  300. width: 100%;
  301. margin-top: 8px;
  302. }
  303. .summary-item {
  304. width: 100%;
  305. justify-content: center;
  306. }
  307. }
  308. .pagination-section {
  309. justify-content: center;
  310. ::v-deep .el-pagination {
  311. display: flex;
  312. flex-wrap: wrap;
  313. justify-content: center;
  314. gap: 8px;
  315. }
  316. }
  317. }
  318. }
  319. </style>