trainList.vue 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. <template>
  2. <view class="container">
  3. <!-- 搜索区域 -->
  4. <view class="search-box">
  5. <input
  6. class="search-input"
  7. v-model="courseName"
  8. placeholder="搜索课程名称"
  9. @confirm="searchTrain"
  10. />
  11. <button class="search-btn" @click="searchTrain">查询</button>
  12. </view>
  13. <!-- 培训列表 -->
  14. <scroll-view scroll-y class="train-list" @scrolltolower="loadMore">
  15. <view v-if="loading" class="loading">加载中...</view>
  16. <view v-else-if="trainList.length === 0" class="empty">
  17. <text>暂无培训记录</text>
  18. </view>
  19. <view v-else>
  20. <view v-for="(item, index) in trainList" :key="index" class="train-item">
  21. <view class="card-header">
  22. <text class="title">{{ item.train_name || '未命名培训' }}</text>
  23. </view>
  24. <view class="card-body">
  25. <view class="info-line">
  26. <text class="label">负责人:</text>
  27. <text class="value">{{ item.responsible_people || '-' }}</text>
  28. </view>
  29. <view class="info-line">
  30. <text class="label">课程名称:</text>
  31. <text class="value">{{ item.course_name || '-' }}</text>
  32. </view>
  33. <view class="info-line">
  34. <text class="label">讲师:</text>
  35. <text class="value">{{ item.teacher_name || '-' }}</text>
  36. </view>
  37. <view class="info-line">
  38. <text class="label">地点:</text>
  39. <text class="value">{{ item.location_name || '-' }}</text>
  40. </view>
  41. <!-- 评分展示 -->
  42. <!-- <view class="score-section" v-if="item.project_org || item.course_score || item.teacher_score">
  43. <view class="score-line">
  44. <text class="label">项目组织:</text>
  45. <text class="stars">{{ renderStars(item.project_org) }}</text>
  46. </view>
  47. <view class="score-line">
  48. <text class="label">课程评分:</text>
  49. <text class="stars">{{ renderStars(item.course_score) }}</text>
  50. </view>
  51. <view class="score-line">
  52. <text class="label">讲师评分:</text>
  53. <text class="stars">{{ renderStars(item.teacher_score) }}</text>
  54. </view>
  55. </view>-->
  56. <!-- 操作按钮 -->
  57. <view class="action-buttons">
  58. <button
  59. class="btn btn-view"
  60. @click="viewDetail(item)"
  61. >
  62. 查看
  63. </button>
  64. <button
  65. class="btn btn-assess"
  66. @click="assessTrain(item)"
  67. >
  68. 评估
  69. </button>
  70. </view>
  71. </view>
  72. </view>
  73. <view v-if="hasMore" class="load-more">加载更多...</view>
  74. <view v-else-if="trainList.length > 0" class="no-more">没有更多了</view>
  75. </view>
  76. </scroll-view>
  77. </view>
  78. </template>
  79. <script setup>
  80. import { ref, onMounted } from 'vue';
  81. import { onLoad } from '@dcloudio/uni-app';
  82. import { getMyTrainList } from '@/api/train.js';
  83. import { useUserStore } from '@/store/user.js';
  84. const userStore = useUserStore();
  85. const trainList = ref([]);
  86. const courseName = ref('');
  87. const loading = ref(false);
  88. const hasMore = ref(true);
  89. const pageNum = ref(1);
  90. const pageSize = ref(20);
  91. onLoad(() => {
  92. loadTrainList();
  93. });
  94. // 渲染星星评分
  95. function renderStars(score) {
  96. if (!score || score === 0) {
  97. return '☆ ☆ ☆ ☆ ☆';
  98. }
  99. const filledStars = '★ '.repeat(parseInt(score));
  100. const emptyStars = '☆ '.repeat(5 - parseInt(score));
  101. return filledStars + emptyStars;
  102. }
  103. // 查询培训
  104. function searchTrain() {
  105. pageNum.value = 1;
  106. trainList.value = [];
  107. hasMore.value = true;
  108. loadTrainList();
  109. }
  110. // 加载培训列表
  111. async function loadTrainList() {
  112. if (loading.value || !hasMore.value) return;
  113. loading.value = true;
  114. try {
  115. const res = await getMyTrainList({
  116. userId: userStore.user.useId,
  117. course_name: courseName.value,
  118. page: pageNum.value,
  119. pageSize: pageSize.value
  120. });
  121. if (res && res.returnCode === '0' && res.returnParams) {
  122. const data = res.returnParams;
  123. const newList = data.list || [];
  124. trainList.value = [...trainList.value, ...newList];
  125. // 判断是否还有更多数据
  126. if (newList.length < pageSize.value) {
  127. hasMore.value = false;
  128. } else {
  129. pageNum.value++;
  130. }
  131. }
  132. } catch (error) {
  133. uni.showToast({
  134. title: '加载失败',
  135. icon: 'none'
  136. });
  137. console.error('加载培训列表失败:', error);
  138. } finally {
  139. loading.value = false;
  140. }
  141. }
  142. // 加载更多
  143. function loadMore() {
  144. if (!loading.value && hasMore.value) {
  145. loadTrainList();
  146. }
  147. }
  148. // 查看详情
  149. function viewDetail(item) {
  150. console.log('查看培训详情 - item:', item);
  151. console.log('assess_id:', item.assess_id);
  152. console.log('universalid:', item.universalid);
  153. uni.navigateTo({
  154. url: `/pages/mine/train/trainDetail?universalid=${item.universalid || ''}&imple_id=${item.imple_id}&course_id=${item.course_id}&trainees_id=${item.trainees_id}&assess_id=${item.assess_id || ''}`
  155. });
  156. }
  157. // 评估培训
  158. function assessTrain(item) {
  159. uni.navigateTo({
  160. url: `/pages/mine/train/trainAssess?universalid=${item.universalid || ''}&imple_id=${item.imple_id}&course_id=${item.course_id}&trainees_id=${item.trainees_id}&assess_id=${item.assess_id || ''}&train_name=${encodeURIComponent(item.train_name || '')}&course_name=${encodeURIComponent(item.course_name || '')}`
  161. });
  162. }
  163. </script>
  164. <style lang="scss" scoped>
  165. .container {
  166. min-height: 100vh;
  167. background-color: #f5f5f5;
  168. display: flex;
  169. flex-direction: column;
  170. }
  171. .search-box {
  172. background: #fff;
  173. padding: 20rpx 30rpx;
  174. display: flex;
  175. align-items: center;
  176. gap: 15rpx;
  177. border-bottom: 1rpx solid #eee;
  178. .search-input {
  179. flex: 1;
  180. height: 70rpx;
  181. line-height: 70rpx;
  182. padding: 0 25rpx;
  183. background: #f8f8f8;
  184. border-radius: 8rpx;
  185. font-size: 28rpx;
  186. }
  187. .search-btn {
  188. width: 150rpx;
  189. height: 70rpx;
  190. line-height: 70rpx;
  191. background: #1890ff;
  192. color: #fff;
  193. border-radius: 8rpx;
  194. font-size: 28rpx;
  195. padding: 0;
  196. margin: 0;
  197. &::after {
  198. border: none;
  199. }
  200. }
  201. }
  202. .train-list {
  203. flex: 1;
  204. height: calc(100vh - 150rpx);
  205. padding: 20rpx 24rpx;
  206. box-sizing: border-box;
  207. width: 100%;
  208. }
  209. .loading, .empty, .load-more, .no-more {
  210. text-align: center;
  211. padding: 40rpx 0;
  212. color: #999;
  213. font-size: 28rpx;
  214. }
  215. .train-item {
  216. background: #fff;
  217. border-radius: 12rpx;
  218. padding: 20rpx;
  219. margin-bottom: 16rpx;
  220. box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
  221. box-sizing: border-box;
  222. width: 100%;
  223. .card-header {
  224. margin-bottom: 16rpx;
  225. padding-bottom: 12rpx;
  226. border-bottom: 1rpx solid #f0f0f0;
  227. .title {
  228. font-size: 32rpx;
  229. font-weight: 600;
  230. color: #333;
  231. }
  232. }
  233. .card-body {
  234. .info-line {
  235. display: flex;
  236. align-items: center;
  237. font-size: 26rpx;
  238. line-height: 1.8;
  239. margin-bottom: 8rpx;
  240. .label {
  241. color: #999;
  242. margin-right: 8rpx;
  243. min-width: 140rpx;
  244. }
  245. .value {
  246. color: #333;
  247. flex: 1;
  248. }
  249. }
  250. .score-section {
  251. margin-top: 12rpx;
  252. padding-top: 12rpx;
  253. border-top: 1rpx dashed #f0f0f0;
  254. .score-line {
  255. display: flex;
  256. align-items: center;
  257. font-size: 26rpx;
  258. line-height: 1.8;
  259. margin-bottom: 6rpx;
  260. .label {
  261. color: #999;
  262. margin-right: 8rpx;
  263. min-width: 140rpx;
  264. }
  265. .stars {
  266. color: #FF9912;
  267. font-size: 24rpx;
  268. }
  269. }
  270. }
  271. .action-buttons {
  272. display: flex;
  273. justify-content: flex-end;
  274. gap: 16rpx;
  275. margin-top: 16rpx;
  276. padding-top: 12rpx;
  277. border-top: 1rpx solid #f0f0f0;
  278. .btn {
  279. flex: 0 0 auto;
  280. padding: 12rpx 28rpx;
  281. font-size: 26rpx;
  282. border-radius: 6rpx;
  283. min-width: 120rpx;
  284. height: auto;
  285. line-height: 1.5;
  286. &::after {
  287. border: none;
  288. }
  289. &.btn-view {
  290. background: #fff;
  291. color: #1890ff;
  292. border: 1rpx solid #1890ff;
  293. &:active {
  294. background: #f0f7ff;
  295. }
  296. }
  297. &.btn-assess {
  298. background: #1890ff;
  299. color: #fff;
  300. border: 1rpx solid #1890ff;
  301. &:active {
  302. background: #096dd9;
  303. }
  304. }
  305. }
  306. }
  307. }
  308. }
  309. </style>