index.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. <template>
  2. <view>
  3. <uni-nav-bar fixed :border="false" title="编辑卡片" :statusBar="true" @clickLeft="back" @clickRight="handleSubmit">
  4. <template v-slot:left>
  5. <view class="nav-bar-left">取消</view>
  6. </template>
  7. <!-- #ifndef MP-WEIXIN -->
  8. <template v-slot:right>
  9. <view class="nav-bar-save">保存</view>
  10. </template>
  11. <!-- #endif -->
  12. </uni-nav-bar>
  13. <view class="container">
  14. <view class="tips">拖动可调整应用位置</view>
  15. <view class="box-title">显示在健康监测页</view>
  16. <view>
  17. <!-- gap: rowSpace + 'rpx '+ columnSpace + 'rpx ' -->
  18. <movable-area class="box-con" :style="{height: boxHeight + 'px',marginBottom: `-${rowSpace}rpx`}" id="drag">
  19. <movable-view
  20. v-for="(item, index) in currentList"
  21. :key="index"
  22. :x="item.x"
  23. :y="item.y"
  24. damping="40"
  25. disabled
  26. :animation="item.animation"
  27. :direction="direction"
  28. :data-index="index"
  29. :class="{'active': active == index}"
  30. :style="{width: columnWidth + 'px', height: rpxTopx(rowHeight) + 'px'}"
  31. @touchstart="touchstart"
  32. @touchmove.stop="touchmove"
  33. @touchend="touchend"
  34. >
  35. <view class="box-conitem" :style="{width: columnWidth + 'px', height: rpxTopx(rowHeight) + 'px',marginBottom: `${rowSpace}rpx`}">
  36. <image class="editicon" src="@/static/images/pages_watch/icons/remove_icon.png" mode="aspectFill" @click.stop="close(index,item)"></image>
  37. <text>{{item.type}}</text>
  38. <image :src="item.icon" mode="aspectFill"></image>
  39. </view>
  40. </movable-view>
  41. </movable-area>
  42. </view>
  43. <view class="box-title" style="margin-top: 40rpx;">可添加的健康卡片</view>
  44. <!-- :style="{gap: rowSpace + 'rpx '+ columnSpace + 'rpx ', }" -->
  45. <view class="box-con" :style="{marginBottom: `-${rowSpace}rpx`}">
  46. <view class="box-conitem" v-for="(item,index) in list" :key="item.key" :style="{width: columnWidth + 'px', height: rpxTopx(rowHeight) + 'px',marginBottom: `${rowSpace}rpx`}">
  47. <image class="editicon" src="@/static/images/pages_watch/icons/increase_icon.png" mode="aspectFill" @click="handleAdd(item,index)"></image>
  48. <text>{{item.type}}</text>
  49. <image :src="item.icon" mode="aspectFill"></image>
  50. </view>
  51. </view>
  52. <!-- #ifndef APP-PLUS || H5 -->
  53. <button class="footer-save-btn" :loading="btnLoading" :disabled="btnLoading" @click="handleSubmit">保存</button>
  54. <!-- #endif -->
  55. </view>
  56. </view>
  57. </template>
  58. <script>
  59. import { monitorDataType } from "@/api/pages_watch/healthMonitoring.js"
  60. import { editUser } from "@/api/pages_watch/user.js"
  61. export default {
  62. data() {
  63. return {
  64. btnLoading: false,
  65. columnWidth: 0, //列宽,单位px
  66. rowHeight: 128, //行高,单位rpx
  67. rowNum: 1, //行数
  68. boxHeight: 10, //可拖动区域的高度,单位px
  69. windowWidth: 750, //系统获取到的窗口宽度,单位px
  70. columnNum: 2, // 列数
  71. rowSpace: 22, //行间距,单位rpx
  72. columnSpace: 22, //列间距,单位rpx
  73. xMoveUnit: 0, //沿x轴移动时的单位距离,单位px
  74. yMoveUnit: 0, //沿y轴移动时的单位距离,单位px
  75. topY: 0, // 距离顶部的距离
  76. topX: 0, // 距离左侧偏移位置
  77. deviationX: 0,
  78. deviationY: 0 ,// 偏移量
  79. active: -1, // 当前激活的item
  80. index: 0, // 当前激活的item的原index
  81. direction: "all",
  82. monitorDataTypeOrder: "", // 当前登录用户设置显示的健康监测卡片
  83. currentList: [
  84. ],
  85. list: [
  86. // {
  87. // name: "血糖",
  88. // icon: require("@/static/images/pages_watch/icons/blood_sugar_icon.png")
  89. // },
  90. // {
  91. // name: "血压",
  92. // icon: require("@/static/images/pages_watch/icons/blood_pressure_icon.png")
  93. // },
  94. // {
  95. // name: "心率",
  96. // icon: require("@/static/images/pages_watch/icons/heart_rate_icon.png")
  97. // },{
  98. // name: "血氧",
  99. // icon: require("@/static/images/pages_watch/icons/blood_oxygen_icon.png")
  100. // },
  101. // {
  102. // name: "体温",
  103. // icon: require("@/static/images/pages_watch/icons/temperature_icon.png")
  104. // },
  105. // {
  106. // name: "呼吸",
  107. // icon: require("@/static/images/pages_watch/icons/breathe_icon.png")
  108. // },
  109. // {
  110. // name: "血脂",
  111. // icon: require("@/static/images/pages_watch/icons/blood_fat_icon.png")
  112. // },{
  113. // name: "尿酸",
  114. // icon: require("@/static/images/pages_watch/icons/uric_acid_icon.png")
  115. // },{
  116. // name: "血管硬化",
  117. // icon: require("@/static/images/pages_watch/icons/angiosclerosis_icon.png")
  118. // },{
  119. // name: "血酮",
  120. // icon: require("@/static/images/pages_watch/icons/blood_ketone_icon.png")
  121. // },{
  122. // name: "睡眠",
  123. // icon: require("@/static/images/pages_watch/icons/sleep_icon.png")
  124. // },{
  125. // name: "房颤",
  126. // icon: require("@/static/images/pages_watch/icons/atrial_fibrillation_icon.png")
  127. // },{
  128. // name: "舌诊",
  129. // icon: require("@/static/images/pages_watch/icons/tongue_diagnosis_icon.png")
  130. // }
  131. ]
  132. }
  133. },
  134. onLoad() {
  135. const res = uni.getSystemInfoSync();
  136. this.windowWidth = res.windowWidth
  137. if(uni.getStorageSync("userWatchInfo")) {
  138. const userInfo = JSON.parse(uni.getStorageSync("userWatchInfo"))
  139. this.monitorDataTypeOrder = userInfo.monitorDataTypeOrder
  140. this.getAllList()
  141. }
  142. },
  143. methods: {
  144. back() {
  145. uni.navigateBack({
  146. delta: 1
  147. })
  148. },
  149. // 保存
  150. handleSubmit() {
  151. const sortList = this.currentList.slice().sort((a, b) => a.SortNumber - b.SortNumber);
  152. const monitorDataTypeOrder = sortList.map(item=>item.id)
  153. this.btnLoading = true
  154. editUser({monitorDataTypeOrder:monitorDataTypeOrder.join(',')}).then(res=>{
  155. this.btnLoading = false
  156. if(res.code == 200) {
  157. uni.showToast({
  158. title: "保存成功",
  159. icon: "none"
  160. })
  161. uni.navigateBack()
  162. } else {
  163. uni.showToast({
  164. title: res.msg,
  165. icon: "none"
  166. })
  167. }
  168. }).catch(()=>{
  169. this.btnLoading = false
  170. })
  171. },
  172. /* prx转换成px,返回值还是不带px单位的Number */
  173. rpxTopx(v) {
  174. return this.windowWidth * v / 750
  175. },
  176. handleAdd(item,index) {
  177. this.list.splice(index,1)
  178. this.currentList.push(item)
  179. this.onUpdateCurrentList()
  180. },
  181. // 关闭按钮
  182. close(index,item) {
  183. this.currentList.forEach((item, i) => {
  184. if (this.currentList[i].SortNumber > this.currentList[index].SortNumber) {
  185. item.SortNumber--
  186. }
  187. })
  188. this.moveUpdateCurrentList(-1)
  189. this.currentList[index].isShow = 0
  190. this.currentList.splice(index,1)
  191. this.list.push(item)
  192. },
  193. getAllList() {
  194. monitorDataType().then(res=>{
  195. if(res.code == 200) {
  196. if(this.monitorDataTypeOrder) {
  197. const order = this.monitorDataTypeOrder.split(',')
  198. let list = res.data || []
  199. this.list = list.filter(item=> order.indexOf(item.id) == -1)
  200. const indexOrder = this.monitorDataTypeOrder.split(","); // 指定的索引顺序
  201. this.currentList = list.filter(item => order.includes(item.id));
  202. this.currentList.sort((a, b) => indexOrder.indexOf(a.id) - indexOrder.indexOf(b.id));
  203. } else {
  204. this.list = res.data || []
  205. this.currentList = []
  206. }
  207. const query = uni.createSelectorQuery().in(this);
  208. query.select('#drag').boundingClientRect(data => {
  209. this.columnWidth = (data.width - (this.columnNum - 1) * this.rpxTopx(this.columnSpace)) / this.columnNum
  210. this.onUpdateCurrentList();
  211. }).exec();
  212. }
  213. })
  214. },
  215. onUpdateCurrentList(list = this.currentList) {
  216. this.rowNum = Math.ceil(this.currentList.length / this.columnNum);
  217. this.boxHeight = this.rowNum * this.rpxTopx(this.rowHeight) + (this.rowNum - 1) * this.rpxTopx(this.rowSpace);
  218. this.xMoveUnit = this.columnWidth + this.rpxTopx(this.columnSpace);
  219. this.yMoveUnit = this.rpxTopx(this.rowHeight) + this.rpxTopx(this.rowSpace);
  220. let arr = []
  221. for (const key in list) {
  222. let x = this.xMoveUnit * (key % this.columnNum); //单位px
  223. let y = Math.floor(key / this.columnNum) * this.yMoveUnit; //单位px
  224. arr.push({
  225. ...list[key],
  226. isShow: 1,
  227. index: Number(key),
  228. SortNumber: Number(key),
  229. y,
  230. x,
  231. animation: true
  232. })
  233. }
  234. this.currentList = arr
  235. },
  236. // 根据排序进行重新计算位置
  237. moveUpdateCurrentList(index) {
  238. for (const i in this.currentList) {
  239. let key
  240. if (this.currentList[i].SortNumber || this.currentList[i].SortNumber === 0) {
  241. key = this.currentList[i].SortNumber
  242. } else {
  243. key = Number(i)
  244. }
  245. // debugger
  246. this.currentList[i].y = Math.floor(key / this.columnNum) * this.yMoveUnit; //单位px
  247. if (index == key) {
  248. continue
  249. } else {
  250. this.currentList[i].x = this.xMoveUnit * (key % this.columnNum); //单位px
  251. }
  252. }
  253. },
  254. touchstart(e) {
  255. // 计算 x y 轴点击位置
  256. var query = uni.createSelectorQuery().in(this)
  257. query.select('#drag').boundingClientRect()
  258. query.exec((res) => {
  259. this.topY = res[0].top
  260. this.topX = res[0].left
  261. const mp = e.mp || e
  262. let touchY = mp.touches[0].clientY - res[0].top
  263. let touchX = mp.touches[0].clientX - res[0].left
  264. // this.deviationY = touchY % this.height
  265. // this.deviationX = touchX % (this.windowWidth * 0.2)
  266. this.active = Number(e.currentTarget.dataset.index)
  267. this.index = Number(e.currentTarget.dataset.index)
  268. })
  269. },
  270. touchmove(e) {
  271. if (this.active < 0) return
  272. const mp = e.mp || e
  273. let temY = mp.touches[0].clientY - this.topY
  274. let temX = mp.touches[0].clientX - this.topX
  275. let touchY = temY - 30
  276. let touchX = temX - this.windowWidth * 0.1
  277. this.currentList[this.active].y = touchY
  278. this.currentList[this.active].x = touchX
  279. this.currentList[this.active].animation = false
  280. this.currentList.every((res, index) => {
  281. let absX = Math.abs(touchX - res.x)
  282. let absY = Math.abs(touchY - res.y)
  283. // 设置元素定点距离多少进行重排
  284. if (res.isShow == 0){
  285. return true
  286. }
  287. if (0 < absX && absX <= 20 && absY > 0 && absY <= 40 && this.active != index) {
  288. // debugger
  289. let temNumber = this.currentList[index].SortNumber
  290. this.currentList.every((_res, _index) => {
  291. // 判断从大像小移还是从小向大移
  292. if (this.currentList[this.active].SortNumber < this.currentList[index].SortNumber) {
  293. // 移动元素比目标元素所在位置小,之间元素排序--
  294. if (this.currentList[_index].SortNumber > this.currentList[this.active].SortNumber && this.currentList[
  295. _index].SortNumber <= this.currentList[index].SortNumber) {
  296. _res.SortNumber--
  297. }
  298. } else {
  299. // 反之++
  300. if (this.currentList[_index].SortNumber < this.currentList[this.active].SortNumber && this.currentList[
  301. _index].SortNumber >= this.currentList[index].SortNumber) {
  302. _res.SortNumber++
  303. }
  304. }
  305. return true
  306. }, this)
  307. this.currentList[this.active].SortNumber = temNumber
  308. this.moveUpdateCurrentList(temNumber)
  309. return false
  310. } else {
  311. return true
  312. }
  313. }, this)
  314. },
  315. touchend(e) {
  316. if (this.currentList[this.active]) {
  317. this.currentList[this.active].animation = true
  318. }
  319. this.moveUpdateCurrentList(-1)
  320. this.active = -1
  321. },
  322. }
  323. }
  324. </script>
  325. <style lang="scss" scoped>
  326. @mixin u-flex($flexD, $alignI, $justifyC) {
  327. display: flex;
  328. flex-direction: $flexD;
  329. align-items: $alignI;
  330. justify-content: $justifyC;
  331. }
  332. .nav-bar-save {
  333. width: 100rpx;
  334. height: 52rpx;
  335. background: #FF7700;
  336. border-radius: 26rpx 26rpx 26rpx 26rpx;
  337. font-family: PingFang SC, PingFang SC;
  338. font-weight: 400;
  339. font-size: 26rpx;
  340. color: #FFFFFF;
  341. text-align: center;
  342. line-height: 52rpx;
  343. }
  344. .footer-save-btn {
  345. width: 702rpx;
  346. height: 98rpx;
  347. margin-top: 40rpx;
  348. background: #FF7700 !important;
  349. border-radius: 16rpx 16rpx 16rpx 16rpx;
  350. font-family: PingFang SC, PingFang SC;
  351. font-weight: 500;
  352. font-size: 32rpx;
  353. color: #FFFFFF !important;
  354. line-height: 98rpx;
  355. &::after {
  356. border: none;
  357. }
  358. }
  359. .nav-bar-left {
  360. margin-left: 8rpx;
  361. font-family: PingFang SC, PingFang SC;
  362. font-weight: 400;
  363. font-size: 32rpx;
  364. color: #FF7700;
  365. }
  366. .container {
  367. padding: 0 24rpx 24rpx 24rpx;
  368. font-family: PingFang SC, PingFang SC;
  369. font-weight: 500;
  370. font-size: 32rpx;
  371. }
  372. .tips {
  373. padding: 16rpx 0;
  374. text-align: center;
  375. font-size: 26rpx;
  376. font-weight: 400;
  377. color: #CCCCCC;
  378. }
  379. .box {
  380. &-title {
  381. height: 80rpx;
  382. line-height: 80rpx;
  383. margin-bottom: 20rpx;
  384. }
  385. &-con {
  386. width: 100%;
  387. position: relative;
  388. @include u-flex(row, center, space-between);
  389. flex-wrap: wrap;
  390. // gap: 22rpx;
  391. }
  392. &-conitem {
  393. // width: 340rpx;
  394. // width: 50%;
  395. // flex: 1;
  396. // height: 128rpx;
  397. padding: 0 34rpx 0 24rpx;
  398. box-sizing: border-box;
  399. background: #FFFFFF;
  400. border-radius: 16rpx 16rpx 16rpx 16rpx;
  401. @include u-flex(row, center, space-between);
  402. position: relative;
  403. image {
  404. height: 72rpx;
  405. width: 72rpx;
  406. }
  407. .editicon {
  408. height: 32rpx;
  409. width: 32rpx;
  410. position: absolute;
  411. right: 0;
  412. top: 0;
  413. }
  414. }
  415. }
  416. .active {
  417. box-shadow: 0 0 40rpx #DDDDDD;
  418. z-index: 99;
  419. }
  420. </style>