展品维保小程序前端代码接口
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

641 lines
19 KiB

2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
1 month ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
1 month ago
1 month ago
2 months ago
2 months ago
1 month ago
2 months ago
1 month ago
  1. <template>
  2. <view class="container">
  3. <view class="header">
  4. <!-- 大Tab维修记录/保养记录 -->
  5. <view class="main-tabs">
  6. <view
  7. class="tab-item"
  8. :class="{ active: activeMainTab === 'repair' }"
  9. @click="switchMainTab('repair')"
  10. >
  11. <text class="tab-text">维修记录</text>
  12. <view class="tab-underline" v-if="activeMainTab === 'repair'"></view>
  13. </view>
  14. <view
  15. class="tab-item"
  16. :class="{ active: activeMainTab === 'maintain' }"
  17. @click="switchMainTab('maintain')"
  18. >
  19. <text class="tab-text">保养记录</text>
  20. <view class="tab-underline" v-if="activeMainTab === 'maintain'"></view>
  21. </view>
  22. </view>
  23. <!-- 筛选按钮区域 -->
  24. <view class="picker-buttons">
  25. <view class="picker-btn" :class="{ active: selectedTime }" @click="showTimePicker" >
  26. <text class="btn-text">{{ selectedTime || '时间' }}</text>
  27. <text class="arrow-icon"></text>
  28. </view>
  29. <view class="picker-btn" :class="{ active: selectedPerson }" @click="showPersonPicker" >
  30. <text class="btn-text">{{ selectedPerson.label || '人员' }}</text>
  31. <text class="arrow-icon"></text>
  32. </view>
  33. </view>
  34. </view>
  35. <!-- 日期选择器 -->
  36. <uv-datetime-picker
  37. confirm-color="#C70019"
  38. ref="timePicker"
  39. mode="date"
  40. @confirm="onTimeConfirm"
  41. ></uv-datetime-picker>
  42. <!-- 人员选择器 -->
  43. <uv-picker
  44. ref="personPicker"
  45. :columns="personColumns"
  46. @confirm="onPersonConfirm"
  47. @cancel="onPersonCancel"
  48. keyName="label"
  49. title="选择人员"
  50. confirmColor="#C70019"
  51. ></uv-picker>
  52. <!-- 内容区域 -->
  53. <view class="content-area">
  54. <!-- 记录item -->
  55. <!-- 加载动画容器 -->
  56. <view v-if="list.length">
  57. <view class="record-item" v-for="(record, index) in list" :key="index">
  58. <!-- 维修记录 -->
  59. <template v-if="activeMainTab === 'repair'">
  60. <!-- 基本信息 -->
  61. <view class="info-row">
  62. <text class="label">维修人</text>
  63. <text class="value">{{ record.repairName }}</text>
  64. </view>
  65. <view class="info-row">
  66. <text class="label">联系方式</text>
  67. <text class="value">{{ record.phone }}</text>
  68. </view>
  69. <view class="info-row">
  70. <text class="label">维修日期</text>
  71. <text class="value">{{ record.repairDate }}</text>
  72. </view>
  73. <view class="info-row">
  74. <text class="label">处理内容</text>
  75. </view>
  76. <!-- 处理内容文本区域 -->
  77. <view class="content-text">
  78. <text>{{ record.content }}</text>
  79. </view>
  80. <!-- 上传图片 -->
  81. <view class="info-row" v-if="!collapsedStates[index]">
  82. <text class="label">上传图片</text>
  83. </view>
  84. <view class="image-container" v-if="!collapsedStates[index] && record.image">
  85. <image class="uploaded-image" v-for="(value, index) in record.image.split(',')" :key="index" :src="value" mode="aspectFill"></image>
  86. </view>
  87. <!-- 是否产生费用 -->
  88. <view class="info-row" v-if="!collapsedStates[index]">
  89. <text class="label">是否产生费用</text>
  90. <text class="value red-text">{{ record.isExpend === '1' || record.isExpend === 1 ? '是' : '否' }}</text>
  91. </view>
  92. </template>
  93. <!-- 保养记录 -->
  94. <template v-else>
  95. <!-- 基本信息 -->
  96. <view class="info-row">
  97. <text class="label">保养人</text>
  98. <text class="value">{{ record.maintenanceName }}</text>
  99. </view>
  100. <view class="info-row">
  101. <text class="label">保养日期</text>
  102. <text class="value">{{ record.maintenanceDate }}</text>
  103. </view>
  104. <view class="info-row">
  105. <text class="label">保养前状态</text>
  106. </view>
  107. <!-- 保养前状态文本区域 -->
  108. <view class="content-text">
  109. <text>{{ record.stateFrontText }}</text>
  110. </view>
  111. <!-- 保养前图片 -->
  112. <view class="image-container" v-if="!collapsedStates[index] && record.stateFrontImage">
  113. <image
  114. class="uploaded-image"
  115. v-for="(img, imgIndex) in record.stateFrontImage.split(',')"
  116. :key="imgIndex"
  117. :src="img"
  118. mode="aspectFill"
  119. ></image>
  120. </view>
  121. <view class="info-row" v-if="!collapsedStates[index]">
  122. <text class="label">保养后状态</text>
  123. </view>
  124. <!-- 保养后状态文本区域 -->
  125. <view class="content-text" v-if="!collapsedStates[index]">
  126. <text>{{ record.stateBackText }}</text>
  127. </view>
  128. <!-- 保养后图片 -->
  129. <view class="image-container" v-if="!collapsedStates[index] && record.stateBackImage">
  130. <image
  131. class="uploaded-image"
  132. v-for="(img, imgIndex) in record.stateBackImage.split(',')"
  133. :key="'after-' + imgIndex"
  134. :src="img"
  135. mode="aspectFill"
  136. ></image>
  137. </view>
  138. <!-- 是否产生费用 -->
  139. <view class="info-row" v-if="!collapsedStates[index]">
  140. <text class="label">是否产生费用</text>
  141. <text class="value red-text">{{ record.isExpend === '1'||record.isExpend === 1 ? '是' : '否' }}</text>
  142. </view>
  143. </template>
  144. <!-- 产生费用 -->
  145. <view class="info-row" v-if="!collapsedStates[index]">
  146. <text class="label">产生费用</text>
  147. <text class="value red-text">{{ record.amount }}</text>
  148. </view>
  149. <!-- 费用详情表格 -->
  150. <view class="cost-table" v-if="!collapsedStates[index] && record.exhibitMaintenanceExpenses.length">
  151. <view class="table-header">
  152. <text class="header-cell">费用名称</text>
  153. <text class="header-cell">数量</text>
  154. <text class="header-cell">金额</text>
  155. </view>
  156. <view class="table-row" v-for="(item, costIndex) in record.exhibitMaintenanceExpenses" :key="costIndex">
  157. <text class="cell">{{ item.title }}</text>
  158. <text class="cell">{{ item.num }}</text>
  159. <text class="cell">{{ item.amount }}</text>
  160. </view>
  161. <view class="table-row" v-for="(item, costIndex) in record.exhibitRepairExpenseList" :key="costIndex">
  162. <text class="cell">{{ item.title }}</text>
  163. <text class="cell">{{ item.num }}</text>
  164. <text class="cell">{{ item.amount }}</text>
  165. </view>
  166. </view>
  167. <!-- 维修记录特有字段 -->
  168. <template v-if="activeMainTab === 'repair'">
  169. <!-- 问题是否解决 -->
  170. <view class="info-row" >
  171. <text class="label">问题是否解决</text>
  172. <text class="value red-text">{{ record.isFix_dictText }}</text>
  173. </view>
  174. <!-- 备注 -->
  175. <view class="info-row" v-if="!collapsedStates[index]">
  176. <text class="label">备注</text>
  177. </view>
  178. <!-- 备注文本区域 -->
  179. <view class="content-text" v-if="!collapsedStates[index]">
  180. <text>{{ record.remark }}</text>
  181. </view>
  182. </template>
  183. <!-- 保养记录特有字段 -->
  184. <template v-else>
  185. <!-- 附件信息 -->
  186. <view class="info-row" v-if="!collapsedStates[index]">
  187. <text class="label">附件信息</text>
  188. </view>
  189. <!-- 附件信息文本区域 -->
  190. <view class="content-text" v-if="!collapsedStates[index]">
  191. <text>{{ record.remarkText }}</text>
  192. </view>
  193. <!-- 附件图片 -->
  194. <view class="image-container" v-if="!collapsedStates[index] && record.remarkImage">
  195. <image class="uploaded-image" v-for="(value, index) in record.remarkImage.split(',')" :src="value" :key="index" mode="aspectFill"></image>
  196. </view>
  197. <!-- 保养备注 -->
  198. <view class="info-row" v-if="!collapsedStates[index]">
  199. <text class="label">备注</text>
  200. </view>
  201. <!-- 保养备注文本区域 -->
  202. <view class="content-text" v-if="!collapsedStates[index]">
  203. <text>{{ record.remark }}</text>
  204. </view>
  205. </template>
  206. <!-- 修改入口按钮 -->
  207. <view class="edit-btn" @click="goToEdit(record, index)">
  208. <text class="edit-text">修改</text>
  209. </view>
  210. <!-- 收起按钮 -->
  211. <view class="collapse-btn" @click="toggleCollapse(index)">
  212. <text class="collapse-text">{{ collapsedStates[index] ? '查看全部' : '收起' }}</text>
  213. <text class="collapse-icon">{{ collapsedStates[index] ? '▼' : '▲' }}</text>
  214. </view>
  215. </view>
  216. <view v-if="isLoading" style="padding:400rpx 0; ">
  217. <uv-loading-icon text="正在加载中 先喝杯茶吧"></uv-loading-icon>
  218. </view>
  219. </view>
  220. <view v-else-if="!isLoading && !list.length ">
  221. <uv-empty icon="/static/暂无搜索结果.png" />
  222. </view>
  223. <view v-else style="padding:400rpx 0; ">
  224. <uv-loading-icon text="正在加载中 先喝杯茶吧"></uv-loading-icon>
  225. </view>
  226. </view>
  227. </view>
  228. </template>
  229. <script>
  230. import ListMixin from '@/mixins/list'
  231. export default {
  232. mixins: [ListMixin],
  233. data() {
  234. return {
  235. showpieceId: '',
  236. mixinListApi: 'exhibit.queryRepairList',
  237. activeMainTab: 'repair', // 当前激活的主Tab
  238. activeFilter: 0, // 当前激活的筛选器
  239. collapsedStates: [], // 控制每个记录项的展开/收起状态
  240. afterUpdateDataFn(list) {
  241. // 改为新增加的数组部分 为true 原来的 状态保留
  242. this.collapsedStates.push(...new Array(8).fill(true))
  243. },
  244. filterOptions: [
  245. { name: '时间' },
  246. { name: '人员' }
  247. ],
  248. selectedTime: '', // 选中的时间
  249. selectedPerson: '', // 选中的人员
  250. timeColumns: [
  251. ['今天', '昨天', '本周', '本月', '上月', '自定义']
  252. ],
  253. personColumns: [ ]
  254. }
  255. },
  256. computed: {
  257. contentText() {
  258. const tabText = this.activeMainTab === 'repair' ? '维修' : '保养'
  259. const filterText = this.filterOptions[this.activeFilter].name
  260. let selectedText = ''
  261. if (this.activeFilter === 0 && this.selectedTime) {
  262. selectedText = ` - ${this.selectedTime}`
  263. } else if (this.activeFilter === 1 && this.selectedPerson) {
  264. selectedText = ` - ${this.selectedPerson}`
  265. }
  266. return `${tabText}记录 - 按${filterText}筛选${selectedText}`
  267. }
  268. },
  269. methods: {
  270. mixinSetParams() {
  271. const params = { }
  272. if (this.activeMainTab === 'repair' && this.selectedTime) {
  273. params.repairDate = this.selectedTime
  274. } else if (this.activeMainTab === 'maintain' && this.selectedTime) {
  275. params.maintainDate = this.selectedTime
  276. }
  277. if (this.selectedPerson) {
  278. params.id = this.selectedPerson.id
  279. }
  280. return {
  281. showpieceId: this.showpieceId,
  282. ...params
  283. }
  284. },
  285. async switchMainTab(tab) {
  286. this.activeMainTab = tab
  287. this.mixinListApi = this.activeMainTab === 'repair' ? 'exhibit.queryRepairList' : 'exhibit.queryMaintenanceList'
  288. this.onFilterChange()
  289. this.initPage()
  290. this.list = []
  291. this.getList(true)
  292. // this.initCollapsedStates()
  293. },
  294. onFilterChange() {
  295. // this.activeFilter = index
  296. // 切换筛选器时清空选择
  297. this.selectedTime = ''
  298. this.selectedPerson = ''
  299. },
  300. showTimePicker() {
  301. this.$refs.timePicker.open()
  302. },
  303. showPersonPicker() {
  304. this.$refs.personPicker.open()
  305. },
  306. onTimeConfirm(e) {
  307. this.selectedTime = this.$utils.formatTime(e.value)
  308. this.initPage()
  309. this.getList(true)
  310. },
  311. onTimeCancel() {
  312. console.log('取消选择时间')
  313. },
  314. onPersonConfirm(value) {
  315. this.selectedPerson = value.value[0]
  316. console.log('选择的人员:', value)
  317. this.initPage()
  318. this.getList(true)
  319. },
  320. onPersonCancel() {
  321. console.log('取消选择人员')
  322. },
  323. toggleCollapse(index) {
  324. this.$set(this.collapsedStates, index, !this.collapsedStates[index])
  325. console.log('收起/展开费用详情', this.collapsedStates[index])
  326. },
  327. goToEdit(record) {
  328. // 根据当前tab类型导航到对应的修改页面
  329. const pagePath = this.activeMainTab === 'repair' ? '/subPages/home/repairChange' : '/subPages/home/maintainChange'
  330. uni.navigateTo({
  331. url: `${pagePath}?id=${record.id}&showpieceId=${this.showpieceId}`
  332. })
  333. }
  334. },
  335. async onLoad(args){
  336. this.showpieceId = args.id
  337. const userRes = await this.$api.config.queryUserList()
  338. this.personColumns = [[...userRes.result.records.map(item => ({
  339. label: item.nickName,
  340. id: item.id
  341. }))]]
  342. }
  343. }
  344. </script>
  345. <style lang="scss" scoped>
  346. .container {
  347. background-color: #f5f5f5;
  348. min-height: 100vh;
  349. }
  350. .header{
  351. padding: 16rpx 39rpx 23rpx;
  352. background: #fff;
  353. }
  354. .main-tabs {
  355. display: flex;
  356. margin-bottom: 48rpx;
  357. // border-bottom: 2rpx solid #f0f0f0;
  358. .tab-item {
  359. flex: 1;
  360. position: relative;
  361. padding: 13rpx 0;
  362. text-align: center;
  363. .tab-text {
  364. font-size: 28rpx;
  365. color: $secondary-text-color;
  366. font-weight: 400;
  367. transition: all 0.3s ease;
  368. }
  369. &.active {
  370. .tab-text {
  371. color: $primary-color;
  372. // font-weight: 600;
  373. }
  374. }
  375. .tab-underline {
  376. position: absolute;
  377. bottom: 0;
  378. left: 50%;
  379. transform: translateX(-50%);
  380. width: 60rpx;
  381. height: 4rpx;
  382. background-color: $primary-color;
  383. border-radius: 2rpx;
  384. }
  385. }
  386. }
  387. .filter-section {
  388. margin-bottom: 32rpx;
  389. }
  390. .picker-buttons {
  391. // margin-bottom: 32rpx;
  392. display: flex;
  393. gap: 55rpx;
  394. .picker-btn {
  395. display: flex;
  396. align-items: center;
  397. justify-content: flex-start;
  398. padding: 0;
  399. background-color: transparent;
  400. color: $primary-text-color;
  401. &.active {
  402. color: $primary-color;
  403. }
  404. .btn-text {
  405. font-size: 28rpx;
  406. // color: $primary-color;
  407. margin-right: 10rpx;
  408. }
  409. .arrow-icon {
  410. font-size: 20rpx;
  411. // color: $primary-color;
  412. }
  413. }
  414. }
  415. .content-area {
  416. padding: 22rpx 29rpx;
  417. .loading-icon {
  418. margin-top: 60rpx;
  419. }
  420. .record-item {
  421. background: #fff;
  422. border-radius: 16rpx;
  423. padding: 29rpx;
  424. margin-bottom: 37rpx;
  425. border-radius: 15rpx;
  426. box-shadow: 0 3rpx 6rpx 0rpx rgba(0,0,0,0.16);
  427. .info-row {
  428. display: flex;
  429. justify-content: space-between;
  430. align-items: flex-start;
  431. margin-bottom: 38rpx;
  432. .label {
  433. font-size: 28rpx;
  434. color: $primary-text-color;
  435. // font-weight: 400;
  436. flex-shrink: 0;
  437. }
  438. .value {
  439. font-size: 28rpx;
  440. color: $primary-text-color;
  441. text-align: right;
  442. flex: 1;
  443. margin-left: 32rpx;
  444. &.red-text {
  445. color: $primary-color;
  446. }
  447. }
  448. }
  449. .content-text {
  450. background: #f5f5f5;
  451. border-radius: 15rpx;
  452. padding: 24rpx;
  453. margin-bottom: 24rpx;
  454. min-height: 120rpx;
  455. text {
  456. font-size: 28rpx;
  457. color: $primary-text-color;
  458. line-height: 1.5;
  459. }
  460. }
  461. .image-container {
  462. margin-bottom: 29rpx;
  463. .uploaded-image {
  464. width: 157rpx;
  465. height: 157rpx;
  466. // border-radius: 8rpx;
  467. }
  468. }
  469. .cost-table {
  470. margin-bottom: 24rpx;
  471. .table-header {
  472. display: flex;
  473. // background: #f8f8f8;
  474. // border-radius: 8rpx 8rpx 0 0;
  475. padding: 30rpx 0;
  476. justify-content: space-between;
  477. .header-cell {
  478. flex: 1;
  479. font-size: 30rpx;
  480. color: $secondary-text-color;
  481. &:nth-child(1) {
  482. text-align: left;
  483. }
  484. &:nth-child(2) {
  485. text-align: center;
  486. }
  487. &:nth-child(3) {
  488. text-align: right;
  489. }
  490. // font-weight: 500;
  491. }
  492. }
  493. .table-row {
  494. display: flex;
  495. border-bottom: 1rpx solid #f0f0f0;
  496. padding: 30rpx 0;
  497. // justify-content: space-between;
  498. .cell {
  499. flex: 1;
  500. font-size: 30rpx;
  501. color: $primary-text-color;
  502. &:nth-child(1) {
  503. text-align: left;
  504. }
  505. &:nth-child(2) {
  506. text-align: center;
  507. }
  508. &:nth-child(3) {
  509. text-align: right;
  510. }
  511. }
  512. // .cell:first-child {
  513. // text-align: center;
  514. // }
  515. }
  516. }
  517. .view-all-btn {
  518. display: flex;
  519. justify-content: center;
  520. align-items: center;
  521. padding: 16rpx 0;
  522. margin-top: 16rpx;
  523. .view-all-text {
  524. font-size: 26rpx;
  525. color: $primary-color;
  526. margin-right: 8rpx;
  527. }
  528. .view-all-icon {
  529. font-size: 20rpx;
  530. color: $primary-color;
  531. }
  532. }
  533. .edit-btn {
  534. display: flex;
  535. justify-content: center;
  536. align-items: center;
  537. padding: 20rpx 0;
  538. margin-top: 16rpx;
  539. background-color: $primary-color;
  540. border-radius: 8rpx;
  541. .edit-text {
  542. font-size: 28rpx;
  543. color: #fff;
  544. font-weight: 500;
  545. }
  546. }
  547. .collapse-btn {
  548. display: flex;
  549. justify-content: center;
  550. align-items: center;
  551. padding: 16rpx 0;
  552. margin-top: 16rpx;
  553. .collapse-text {
  554. font-size: 26rpx;
  555. color: $primary-color;
  556. margin-right: 8rpx;
  557. }
  558. .collapse-icon {
  559. font-size: 20rpx;
  560. color: $primary-color;
  561. }
  562. }
  563. }
  564. }
  565. </style>