展品维保小程序前端代码接口
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.

606 lines
17 KiB

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