team-system.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616
  1. <template>
  2. <view class="page-team-system">
  3. <!-- 头部:体系信息 -->
  4. <view class="system-header" v-if="systemInfo">
  5. <view class="system-info">
  6. <view class="system-icon">🏢</view>
  7. <view class="system-detail">
  8. <text class="system-name">{{ systemInfo.name || '我的供应商体系' }}</text>
  9. <text class="system-desc">服务商团队管理</text>
  10. </view>
  11. </view>
  12. <view class="system-status" :class="systemInfo.status">
  13. <text>{{ systemInfo.status === 'active' ? '活跃' : '未激活' }}</text>
  14. </view>
  15. </view>
  16. <!-- 层级关系 -->
  17. <view class="section" v-if="hierarchyInfo">
  18. <view class="section-header">
  19. <text class="section-title">层级关系</text>
  20. </view>
  21. <view class="hierarchy-grid">
  22. <!-- 上级 -->
  23. <view class="hierarchy-item" v-if="hierarchyInfo.parent">
  24. <view class="hierarchy-label">上级服务商</view>
  25. <view class="hierarchy-person">
  26. <image v-if="hierarchyInfo.parent.avatar" class="avatar-img" :src="hierarchyInfo.parent.avatar" mode="aspectFill"></image>
  27. <text v-else class="avatar-placeholder">👤</text>
  28. <view class="person-info">
  29. <text class="person-name">{{ hierarchyInfo.parent.nickname || '服务商' }}</text>
  30. <text class="person-level">上级</text>
  31. </view>
  32. </view>
  33. </view>
  34. <!-- 下级 -->
  35. <view class="hierarchy-item" v-else>
  36. <view class="hierarchy-label">下级服务商</view>
  37. <view class="hierarchy-empty">
  38. <text>暂无下级</text>
  39. </view>
  40. </view>
  41. <!-- 团队规模 -->
  42. <view class="hierarchy-stat">
  43. <text class="stat-value">{{ hierarchyInfo.totalChildren || 0 }}</text>
  44. <text class="stat-label">团队成员</text>
  45. </view>
  46. </view>
  47. </view>
  48. <!-- 团队统计 -->
  49. <view class="section">
  50. <view class="section-header">
  51. <text class="section-title">团队数据</text>
  52. </view>
  53. <view class="stats-grid">
  54. <view class="stat-card">
  55. <text class="stat-num">{{ teamStats.memberCount || 0 }}</text>
  56. <text class="stat-name">团队成员</text>
  57. </view>
  58. <view class="stat-card">
  59. <text class="stat-num">{{ teamStats.customerCount || 0 }}</text>
  60. <text class="stat-name">客户归属</text>
  61. </view>
  62. <view class="stat-card">
  63. <text class="stat-num">{{ teamStats.orderCount || 0 }}</text>
  64. <text class="stat-name">关联订单</text>
  65. </view>
  66. <view class="stat-card">
  67. <text class="stat-num">{{ teamStats.amount || 0 }}</text>
  68. <text class="stat-name">关联金额(元)</text>
  69. </view>
  70. </view>
  71. </view>
  72. <!-- 团队成员列表 -->
  73. <view class="section">
  74. <view class="section-header">
  75. <text class="section-title">团队成员</text>
  76. <text class="section-action" @click="goToMembers">查看全部</text>
  77. </view>
  78. <view class="member-list">
  79. <view class="member-item" v-for="member in teamMembers.slice(0, 5)" :key="member.userId">
  80. <image v-if="member.avatar" class="member-avatar" :src="member.avatar" mode="aspectFill"></image>
  81. <text v-else class="member-avatar-placeholder">👤</text>
  82. <view class="member-info">
  83. <text class="member-name">{{ member.nickname || '微信用户' }}</text>
  84. <text class="member-role">{{ member.role || '成员' }}</text>
  85. </view>
  86. <view class="member-stats">
  87. <text class="member-count">{{ member.customerCount || 0 }}</text>
  88. <text class="member-label">客户</text>
  89. </view>
  90. </view>
  91. <view class="empty-tip" v-if="teamMembers.length === 0">
  92. <text>暂无团队成员</text>
  93. </view>
  94. </view>
  95. </view>
  96. <!-- 团队客户列表 -->
  97. <view class="section">
  98. <view class="section-header">
  99. <text class="section-title">团队客户</text>
  100. <text class="section-action" @click="goToCustomers">查看全部</text>
  101. </view>
  102. <view class="customer-list">
  103. <view class="customer-item" v-for="customer in teamCustomers.slice(0, 5)" :key="customer.id">
  104. <view class="customer-avatar">
  105. <text class="customer-icon">👥</text>
  106. </view>
  107. <view class="customer-info">
  108. <text class="customer-name">{{ customer.familyName || '未知家庭' }}</text>
  109. <text class="customer-meta">{{ customer.source === 'manual' ? '人工绑定' : customer.source === 'order' ? '下单自动' : '推荐码' }}</text>
  110. </view>
  111. <view class="customer-stats">
  112. <text class="customer-orders">{{ customer.orderCount || 0 }}</text>
  113. <text class="customer-label">订单</text>
  114. </view>
  115. </view>
  116. <view class="empty-tip" v-if="teamCustomers.length === 0">
  117. <text>暂无客户数据</text>
  118. </view>
  119. </view>
  120. </view>
  121. <!-- 操作按钮 -->
  122. <view class="action-bar">
  123. <button class="btn-manage" @click="goToManage">团队管理</button>
  124. <button class="btn-customers" @click="goToCustomers">客户列表</button>
  125. </view>
  126. </view>
  127. </template>
  128. <script>
  129. export default {
  130. data() {
  131. return {
  132. systemInfo: null,
  133. hierarchyInfo: null,
  134. teamStats: {
  135. memberCount: 0,
  136. customerCount: 0,
  137. orderCount: 0,
  138. amount: 0
  139. },
  140. teamMembers: [],
  141. teamCustomers: []
  142. }
  143. },
  144. onLoad() {
  145. this.loadData()
  146. },
  147. onShow() {
  148. this.loadData()
  149. },
  150. methods: {
  151. async loadData() {
  152. try {
  153. // 并发获取所有数据
  154. const [systemRes, hierarchyRes, membersRes, customersRes] = await Promise.all([
  155. this.fetchSystemInfo(),
  156. this.fetchHierarchyInfo(),
  157. this.fetchTeamMembers(),
  158. this.fetchTeamCustomers()
  159. ])
  160. if (systemRes) {
  161. this.systemInfo = systemRes
  162. }
  163. if (hierarchyRes) {
  164. this.hierarchyInfo = hierarchyRes
  165. }
  166. if (membersRes) {
  167. this.teamMembers = membersRes
  168. }
  169. if (customersRes) {
  170. this.teamCustomers = customersRes
  171. }
  172. // 更新统计
  173. this.updateStats()
  174. } catch (error) {
  175. console.error('加载团队数据失败:', error)
  176. }
  177. },
  178. // 获取体系信息
  179. async fetchSystemInfo() {
  180. try {
  181. const res = await this.$http.get('/api/admin/supply-system/my-system', {})
  182. return res.data || null
  183. } catch (e) {
  184. return null
  185. }
  186. },
  187. // 获取层级关系
  188. async fetchHierarchyInfo() {
  189. try {
  190. const res = await this.$http.get('/api/admin/supply-hierarchy/my', {})
  191. return res.data || null
  192. } catch (e) {
  193. return null
  194. }
  195. },
  196. // 获取团队成员
  197. async fetchTeamMembers() {
  198. try {
  199. const res = await this.$http.get('/api/admin/supply-team/members', {
  200. systemId: this.systemInfo?.id
  201. })
  202. return res.data || []
  203. } catch (e) {
  204. return []
  205. }
  206. },
  207. // 获取团队客户
  208. async fetchTeamCustomers() {
  209. try {
  210. const res = await this.$http.get('/api/admin/supply-team/customers', {
  211. systemId: this.systemInfo?.id,
  212. parentId: null,
  213. page: 1,
  214. size: 50
  215. })
  216. return res.data || []
  217. } catch (e) {
  218. return []
  219. }
  220. },
  221. // 更新统计数据
  222. updateStats() {
  223. this.teamStats.memberCount = this.teamMembers.length
  224. this.teamStats.customerCount = this.teamCustomers.length
  225. },
  226. // 跳转管理页面
  227. goToManage() {
  228. uni.navigateTo({
  229. url: '/pages/vendor/team-manage/team-manage'
  230. })
  231. },
  232. // 跳转客户列表
  233. goToCustomers() {
  234. uni.navigateTo({
  235. url: '/pages/vendor/team-customers/team-customers'
  236. })
  237. }
  238. }
  239. }
  240. </script>
  241. <style>
  242. .page-team-system {
  243. background-color: #FFF7ED;
  244. min-height: 100vh;
  245. padding-bottom: 100rpx;
  246. }
  247. /* 体系头部 */
  248. .system-header {
  249. background-color: #fff;
  250. padding: 30rpx;
  251. margin-bottom: 24rpx;
  252. display: flex;
  253. justify-content: space-between;
  254. align-items: center;
  255. box-shadow: 0 2rpx 12rpx rgba(249,115,22,0.08);
  256. }
  257. .system-info {
  258. display: flex;
  259. align-items: center;
  260. }
  261. .system-icon {
  262. font-size: 64rpx;
  263. margin-right: 20rpx;
  264. }
  265. .system-name {
  266. font-size: 32rpx;
  267. font-weight: bold;
  268. color: #333;
  269. }
  270. .system-desc {
  271. font-size: 24rpx;
  272. color: #94A3B8;
  273. margin-top: 4rpx;
  274. }
  275. .system-status {
  276. padding: 8rpx 20rpx;
  277. border-radius: 20rpx;
  278. font-size: 24rpx;
  279. }
  280. .system-status.active {
  281. background-color: #E6F7EE;
  282. color: #65C29A;
  283. }
  284. .system-status.inactive {
  285. background-color: #FFF7EE;
  286. color: #F97316;
  287. }
  288. /* 区块 */
  289. .section {
  290. background-color: #fff;
  291. border-radius: 20rpx;
  292. padding: 24rpx;
  293. margin: 0 30rpx 24rpx;
  294. box-shadow: 0 2rpx 12rpx rgba(249,115,22,0.08);
  295. }
  296. .section-header {
  297. display: flex;
  298. justify-content: space-between;
  299. align-items: center;
  300. margin-bottom: 24rpx;
  301. }
  302. .section-title {
  303. font-size: 28rpx;
  304. font-weight: bold;
  305. color: #333;
  306. }
  307. .section-action {
  308. font-size: 24rpx;
  309. color: #F97316;
  310. }
  311. /* 层级关系 */
  312. .hierarchy-grid {
  313. display: flex;
  314. flex-wrap: wrap;
  315. }
  316. .hierarchy-item {
  317. flex: 1;
  318. min-width: 48%;
  319. padding: 20rpx;
  320. background-color: #FFF7ED;
  321. border-radius: 12rpx;
  322. margin-bottom: 16rpx;
  323. }
  324. .hierarchy-label {
  325. font-size: 24rpx;
  326. color: #666;
  327. margin-bottom: 12rpx;
  328. }
  329. .hierarchy-person {
  330. display: flex;
  331. align-items: center;
  332. }
  333. .avatar-img {
  334. width: 64rpx;
  335. height: 64rpx;
  336. border-radius: 50%;
  337. margin-right: 16rpx;
  338. }
  339. .avatar-placeholder {
  340. width: 64rpx;
  341. height: 64rpx;
  342. border-radius: 50%;
  343. background-color: #F5FAFE;
  344. display: flex;
  345. align-items: center;
  346. justify-content: center;
  347. font-size: 32rpx;
  348. margin-right: 16rpx;
  349. }
  350. .person-info {
  351. flex: 1;
  352. }
  353. .person-name {
  354. font-size: 26rpx;
  355. color: #333;
  356. display: block;
  357. }
  358. .person-level {
  359. font-size: 20rpx;
  360. color: #94A3B8;
  361. }
  362. .hierarchy-empty {
  363. font-size: 24rpx;
  364. color: #94A3B8;
  365. }
  366. .hierarchy-stat {
  367. flex: 1;
  368. min-width: 48%;
  369. padding: 20rpx;
  370. background-color: #F5FAFE;
  371. border-radius: 12rpx;
  372. text-align: center;
  373. }
  374. .stat-value {
  375. font-size: 40rpx;
  376. font-weight: bold;
  377. color: #2F5496;
  378. display: block;
  379. }
  380. .stat-label {
  381. font-size: 22rpx;
  382. color: #666;
  383. }
  384. /* 统计网格 */
  385. .stats-grid {
  386. display: flex;
  387. flex-wrap: wrap;
  388. }
  389. .stat-card {
  390. width: 50%;
  391. padding: 20rpx;
  392. text-align: center;
  393. border-right: 1rpx solid #FED7AA;
  394. border-bottom: 1rpx solid #FED7AA;
  395. }
  396. .stat-num {
  397. font-size: 36rpx;
  398. font-weight: bold;
  399. color: #F97316;
  400. display: block;
  401. }
  402. .stat-name {
  403. font-size: 22rpx;
  404. color: #666;
  405. }
  406. /* 成员列表 */
  407. .member-item {
  408. display: flex;
  409. align-items: center;
  410. padding: 16rpx 0;
  411. border-bottom: 1rpx solid #FED7AA;
  412. }
  413. .member-item:last-child {
  414. border-bottom: none;
  415. }
  416. .member-avatar {
  417. width: 64rpx;
  418. height: 64rpx;
  419. border-radius: 50%;
  420. margin-right: 16rpx;
  421. }
  422. .member-avatar-placeholder {
  423. width: 64rpx;
  424. height: 64rpx;
  425. border-radius: 50%;
  426. background-color: #F5FAFE;
  427. display: flex;
  428. align-items: center;
  429. justify-content: center;
  430. font-size: 32rpx;
  431. margin-right: 16rpx;
  432. }
  433. .member-info {
  434. flex: 1;
  435. }
  436. .member-name {
  437. font-size: 26rpx;
  438. color: #333;
  439. display: block;
  440. }
  441. .member-role {
  442. font-size: 20rpx;
  443. color: #94A3B8;
  444. }
  445. .member-stats {
  446. text-align: right;
  447. }
  448. .member-count {
  449. font-size: 28rpx;
  450. font-weight: bold;
  451. color: #F97316;
  452. display: block;
  453. }
  454. .member-label {
  455. font-size: 20rpx;
  456. color: #666;
  457. }
  458. .empty-tip {
  459. text-align: center;
  460. padding: 40rpx;
  461. color: #94A3B8;
  462. font-size: 26rpx;
  463. }
  464. /* 客户列表 */
  465. .customer-item {
  466. display: flex;
  467. align-items: center;
  468. padding: 16rpx 0;
  469. border-bottom: 1rpx solid #FED7AA;
  470. }
  471. .customer-item:last-child {
  472. border-bottom: none;
  473. }
  474. .customer-avatar {
  475. width: 64rpx;
  476. height: 64rpx;
  477. border-radius: 50%;
  478. background-color: #F5FAFE;
  479. display: flex;
  480. align-items: center;
  481. justify-content: center;
  482. margin-right: 16rpx;
  483. }
  484. .customer-icon {
  485. font-size: 32rpx;
  486. }
  487. .customer-info {
  488. flex: 1;
  489. }
  490. .customer-name {
  491. font-size: 26rpx;
  492. color: #333;
  493. display: block;
  494. }
  495. .customer-meta {
  496. font-size: 20rpx;
  497. color: #94A3B8;
  498. }
  499. .customer-stats {
  500. text-align: right;
  501. }
  502. .customer-orders {
  503. font-size: 28rpx;
  504. font-weight: bold;
  505. color: #F97316;
  506. display: block;
  507. }
  508. .customer-label {
  509. font-size: 20rpx;
  510. color: #666;
  511. }
  512. /* 操作按钮 */
  513. .action-bar {
  514. position: fixed;
  515. bottom: 0;
  516. left: 0;
  517. right: 0;
  518. background-color: #fff;
  519. padding: 20rpx 30rpx;
  520. display: flex;
  521. gap: 20rpx;
  522. box-shadow: 0 -2rpx 12rpx rgba(0,0,0,0.05);
  523. }
  524. .btn-manage, .btn-customers {
  525. flex: 1;
  526. height: 80rpx;
  527. line-height: 80rpx;
  528. border-radius: 40rpx;
  529. font-size: 28rpx;
  530. border: none;
  531. }
  532. .btn-manage {
  533. background-color: #F97316;
  534. color: #fff;
  535. }
  536. .btn-customers {
  537. background-color: #fff;
  538. color: #F97316;
  539. border: 2rpx solid #F97316;
  540. }
  541. </style>