index.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549
  1. <template>
  2. <view class="container">
  3. <template v-if="!isLogin">
  4. <!-- 未登录状态 -->
  5. <view class="no-login">
  6. <image src="/static/images/no-login.png" mode="aspectFit" class="no-login-image"></image>
  7. <text class="no-login-text">登录后查看更多内容</text>
  8. <button class="login-btn" @click="goToLogin">去登录</button>
  9. </view>
  10. </template>
  11. <template v-else>
  12. <!-- Visit list -->
  13. <scroll-view scroll-y class="visit-list" @scrolltolower="onReachBottom">
  14. <template v-if="taskList.length > 0">
  15. <view class="visit-item" v-for="(item, index) in taskList" :key="index" @click="goToDetail(item)">
  16. <view class="visit-date">
  17. <text class="date-num">{{item.date}}</text>
  18. <text class="date-month">{{item.year}}</text>
  19. </view>
  20. <view class="visit-content">
  21. <view class="visit-header">
  22. <view class="visitor-info">
  23. <text class="visitor-label">对接人 </text>
  24. <text class="visitor-name">{{item.employeeName}}</text>
  25. </view>
  26. <text class="visit-status" :class="getStatusClass(item.status)">{{item.status}}</text>
  27. </view>
  28. <view class="visit-desc">{{item.visitReason}}</view>
  29. <view class="visit-time">
  30. <u-icon name="clock" size="12" color="#999"></u-icon>
  31. <text>{{item.time}}</text>
  32. </view>
  33. </view>
  34. </view>
  35. <!-- Loading more -->
  36. <u-loadmore :status="loadMoreStatus" />
  37. </template>
  38. <template v-else>
  39. <!-- 暂无数据 -->
  40. <view class="flex-items-plus">
  41. <image src="../../static/images/empty.png" class="empty "></image>
  42. </view>
  43. <view class="font28 font-gray flex-items-plus">
  44. 数据为空
  45. </view>
  46. </template>
  47. </scroll-view>
  48. <!-- Floating button -->
  49. <view class="floating-button" @click="showApplyPopup">
  50. <text class="floating-text">申请<br>访问</text>
  51. </view>
  52. </template>
  53. <!-- Application method popup -->
  54. <uni-popup ref="popup" type="center" border-radius="16rpx">
  55. <view class="popup-content">
  56. <view class="popup-title">
  57. 选择申请方式
  58. <text class="close-icon" @click="closePopup">×</text>
  59. </view>
  60. <view class="apply-methods">
  61. <view class="method-item" @click="handleApply('self')">
  62. <view class="method-icon green-bg">
  63. <image src="/static/images/apply.png" mode="aspectFit"></image>
  64. </view>
  65. <text class="font-bold">发起申请</text>
  66. </view>
  67. <view class="method-item" @click="handleApply('proxy')">
  68. <view class="method-icon orange-bg">
  69. <image src="/static/images/behalf.png" mode="aspectFit"></image>
  70. </view>
  71. <text class="font-bold">代客申请</text>
  72. </view>
  73. </view>
  74. </view>
  75. </uni-popup>
  76. </view>
  77. </template>
  78. <script>
  79. import {
  80. orderList,
  81. userLogin
  82. } from '@/config/api.js';
  83. import {
  84. mapGetters
  85. } from 'vuex';
  86. import {
  87. shareImg
  88. } from '@/common/config.js'
  89. export default {
  90. data() {
  91. return {
  92. taskList: [],
  93. loadMoreStatus: 'loadmore', // loadmore, loading, nomore
  94. params: {
  95. current: 1,
  96. size: 10,
  97. },
  98. hasMore: true
  99. }
  100. },
  101. computed: {
  102. ...mapGetters(['isLogin'])
  103. },
  104. onLoad(options) {
  105. this.getList();
  106. },
  107. // 微信小程序分享配置
  108. onShareAppMessage() {
  109. return {
  110. title: `邀请您加入知己访客`,
  111. path: `/pages/index/index`,
  112. imageUrl: shareImg // 分享图片,需要添加
  113. }
  114. },
  115. onShareTimeline(res) {
  116. let that = this;
  117. let shareInfo = store.state.vuex_shareInfo;
  118. let query = shareInfo.query;
  119. //携带当前页面资源ID参数
  120. let currentPage = getCurrentPages()[getCurrentPages().length - 1];
  121. let options = currentPage.options;
  122. if (JSON.stringify(options) != '{}' && options.id) {
  123. query += `&id=${options.id}`;
  124. }
  125. return {
  126. title: shareInfo.title,
  127. query: query,
  128. imageUrl: shareImg,
  129. success(res) {
  130. uni.showToast({
  131. title: '分享成功'
  132. })
  133. },
  134. fail(res) {
  135. uni.showToast({
  136. title: '分享失败',
  137. icon: 'none'
  138. })
  139. },
  140. }
  141. },
  142. onShow() {
  143. // #ifdef H5
  144. this.login()
  145. // #endif
  146. },
  147. onPullDownRefresh() {
  148. this.refresh()
  149. uni.stopPullDownRefresh();
  150. },
  151. methods: {
  152. // Refresh list
  153. refresh() {
  154. this.taskList = []
  155. this.params.current = 1
  156. this.hasMore = true
  157. this.loadMoreStatus = 'loadmore'
  158. this.getList()
  159. },
  160. // Handle reaching bottom of scroll
  161. onReachBottom() {
  162. if (!this.hasMore || this.loadMoreStatus === 'loading') return
  163. this.params.current++
  164. this.getList(true)
  165. },
  166. // Show apply popup
  167. showApplyPopup() {
  168. console.log('显示申请弹窗');
  169. this.$refs.popup.open()
  170. },
  171. // Close popup
  172. closePopup() {
  173. this.$refs.popup.close()
  174. },
  175. // Handle apply method selection
  176. handleApply(type) {
  177. if (!this.isLogin) {
  178. uni.navigateTo({
  179. url: '/pagesA/public/login'
  180. });
  181. return
  182. }
  183. console.log('选择申请方式:', type);
  184. this.$refs.popup.close()
  185. if (type === 'self') {
  186. uni.navigateTo({
  187. url: '/pagesA/task/edit?type=1'
  188. })
  189. } else {
  190. uni.navigateTo({
  191. url: '/pagesA/task/edit?type=2'
  192. })
  193. }
  194. },
  195. // Login method
  196. login() {
  197. let params = {
  198. username: "USER082927",
  199. grant_type: "web",
  200. memberId: "1957060037088083973"
  201. }
  202. userLogin(params).then((loginData) => {
  203. uni.setStorageSync('access_token', loginData.access_token);
  204. uni.setStorageSync('refresh_token', loginData.refresh_token);
  205. uni.setStorageSync('user', loginData);
  206. this.$store.commit('isLogin', true);
  207. this.$store.commit('refresh_token', loginData.refresh_token);
  208. })
  209. },
  210. // Get visit list with mock data
  211. getList(loadMore = false) {
  212. if (!loadMore) {
  213. uni.showLoading({
  214. title: '加载中...'
  215. });
  216. }
  217. this.loadMoreStatus = 'loading'
  218. // 模拟接口延迟
  219. setTimeout(() => {
  220. // 模拟数据
  221. const mockData = Array(10).fill(0).map((_, index) => {
  222. const currentIndex = (this.params.current - 1) * 10 + index;
  223. const date = new Date();
  224. date.setDate(date.getDate() - currentIndex); // 每条数据日期递减
  225. const statusList = ['待访问', '待审核', '已拒绝'];
  226. const descList = [
  227. '我是访问事由我是访问事由我是访问事由...',
  228. '需要进行业务对接商谈...',
  229. '产品展示与技术交流...',
  230. '项目合作洽谈...'
  231. ];
  232. return {
  233. id: currentIndex,
  234. employeeName: `范海洋${currentIndex + 1}`,
  235. createTime: date,
  236. status: statusList[Math.floor(Math.random() * statusList.length)],
  237. visitReason: descList[Math.floor(Math.random() * descList.length)]
  238. };
  239. });
  240. const list = mockData.map(item => ({
  241. ...item,
  242. date: this.$u.timeFormat(item.createTime, 'DD'),
  243. year: this.$u.timeFormat(item.createTime, 'YYYY/MM'),
  244. time: this.$u.timeFormat(item.createTime, 'hh:mm')
  245. }));
  246. if (loadMore) {
  247. this.taskList = [...this.taskList, ...list];
  248. } else {
  249. this.taskList = list;
  250. }
  251. // 模拟总共有5页数据
  252. this.hasMore = this.params.current < 5;
  253. this.loadMoreStatus = this.hasMore ? 'loadmore' : 'nomore';
  254. if (!loadMore) {
  255. uni.hideLoading();
  256. }
  257. }, 500); // 增加500ms延迟模拟网络请求
  258. },
  259. // Get status class for styling
  260. getStatusClass(status) {
  261. const statusMap = {
  262. '待访问': 'status-pending-visit',
  263. '待审核': 'status-pending-review',
  264. '已拒绝': 'status-rejected'
  265. }
  266. return statusMap[status] || 'status-default'
  267. },
  268. // 跳转到详情页面
  269. goToDetail(item) {
  270. uni.navigateTo({
  271. url: `/pagesA/task/detail?id=${item.id}&status=${item.status}`
  272. });
  273. },
  274. // 跳转到登录页面
  275. goToLogin() {
  276. uni.navigateTo({
  277. url: '/pagesA/public/login'
  278. });
  279. },
  280. },
  281. }
  282. </script>
  283. <style lang="scss" scoped>
  284. .container {
  285. min-height: 100vh;
  286. background-color: #f5f5f5;
  287. .no-login {
  288. display: flex;
  289. flex-direction: column;
  290. align-items: center;
  291. justify-content: center;
  292. padding-top: 30vh;
  293. .no-login-image {
  294. width: 240rpx;
  295. height: 240rpx;
  296. margin-bottom: 40rpx;
  297. }
  298. .no-login-text {
  299. font-size: 32rpx;
  300. color: #999;
  301. margin-bottom: 60rpx;
  302. }
  303. .login-btn {
  304. width: 320rpx;
  305. height: 88rpx;
  306. background: #FF6B00;
  307. border-radius: 44rpx;
  308. color: #fff;
  309. font-size: 32rpx;
  310. font-weight: 500;
  311. display: flex;
  312. align-items: center;
  313. justify-content: center;
  314. }
  315. }
  316. }
  317. .visit-list {
  318. height: 100vh;
  319. }
  320. .visit-item {
  321. background-color: #fff;
  322. margin: 16rpx 20rpx;
  323. border-radius: 16rpx;
  324. padding: 24rpx;
  325. display: flex;
  326. align-items: flex-start;
  327. box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
  328. }
  329. .visit-date {
  330. display: flex;
  331. flex-direction: column;
  332. align-items: center;
  333. margin-right: 24rpx;
  334. min-width: 80rpx;
  335. .date-num {
  336. font-size: 48rpx;
  337. font-weight: bold;
  338. color: #333;
  339. line-height: 1;
  340. }
  341. .date-month {
  342. font-size: 24rpx;
  343. color: #999;
  344. margin-top: 4rpx;
  345. }
  346. }
  347. .visit-content {
  348. flex: 1;
  349. .visit-header {
  350. display: flex;
  351. justify-content: space-between;
  352. align-items: center;
  353. margin-bottom: 12rpx;
  354. .visitor-info {
  355. display: flex;
  356. align-items: center;
  357. .visitor-label {
  358. margin-right: 10rpx;
  359. font-size: 32rpx;
  360. color: #333;
  361. font-weight: normal;
  362. }
  363. .visitor-name {
  364. font-size: 36rpx;
  365. color: #333;
  366. font-weight: bold;
  367. }
  368. }
  369. .visit-status {
  370. font-size: 28rpx;
  371. padding: 4rpx 12rpx;
  372. border-radius: 12rpx;
  373. &.status-pending-visit {
  374. color: #4CAF50;
  375. background-color: rgba(76, 175, 80, 0.1);
  376. }
  377. &.status-pending-review {
  378. color: #2196F3;
  379. background-color: rgba(33, 150, 243, 0.1);
  380. }
  381. &.status-rejected {
  382. color: #F44336;
  383. background-color: rgba(244, 67, 54, 0.1);
  384. }
  385. }
  386. }
  387. .visit-desc {
  388. color: #666;
  389. font-size: 28rpx;
  390. line-height: 1.4;
  391. margin-bottom: 12rpx;
  392. }
  393. .visit-time {
  394. display: flex;
  395. align-items: center;
  396. gap: 8rpx;
  397. text {
  398. color: #999;
  399. font-size: 24rpx;
  400. }
  401. }
  402. }
  403. .floating-button {
  404. position: fixed;
  405. bottom: 120rpx;
  406. right: 40rpx;
  407. width: 120rpx;
  408. height: 120rpx;
  409. background-color: #FF6B00;
  410. border-radius: 16rpx;
  411. display: flex;
  412. align-items: center;
  413. justify-content: center;
  414. box-shadow: 0 8rpx 20rpx rgba(255, 107, 0, 0.3);
  415. z-index: 999;
  416. .floating-text {
  417. color: #fff;
  418. font-size: 28rpx;
  419. text-align: center;
  420. line-height: 1.2;
  421. }
  422. }
  423. .popup-content {
  424. width: 600rpx;
  425. padding: 70rpx 40rpx;
  426. background-color: #fff;
  427. border-radius: 16rpx;
  428. .popup-title {
  429. font-size: 36rpx;
  430. color: #333;
  431. text-align: center;
  432. position: relative;
  433. margin-bottom: 60rpx;
  434. font-weight: 500;
  435. .close-icon {
  436. position: absolute;
  437. right: 0;
  438. top: 50%;
  439. transform: translateY(-50%);
  440. font-size: 50rpx;
  441. color: #999;
  442. width: 60rpx;
  443. height: 60rpx;
  444. display: flex;
  445. align-items: center;
  446. justify-content: center;
  447. }
  448. }
  449. .apply-methods {
  450. display: flex;
  451. justify-content: space-around;
  452. padding: 0 20rpx;
  453. .method-item {
  454. display: flex;
  455. flex-direction: column;
  456. align-items: center;
  457. gap: 30rpx;
  458. cursor: pointer;
  459. .method-icon {
  460. width: 120rpx;
  461. height: 120rpx;
  462. border-radius: 20rpx;
  463. display: flex;
  464. align-items: center;
  465. justify-content: center;
  466. image {
  467. width: 80rpx;
  468. height: 80rpx;
  469. }
  470. &.green-bg {
  471. background-color: #39833b;
  472. }
  473. &.orange-bg {
  474. background-color: #FF6B00;
  475. }
  476. }
  477. text {
  478. font-size: 32rpx;
  479. color: #333;
  480. font-weight: 500;
  481. }
  482. }
  483. }
  484. }
  485. </style>