navigation.vue 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. <template>
  2. <view class="nav-container">
  3. <!-- 快捷功能 -->
  4. <view class="quick-functions-box">
  5. <view class="qfn-header">
  6. <text class="qfn-title">常用功能</text>
  7. <view class="qfn-actions">
  8. <u-icon name="edit-pen" size="36" color="#999" @click="toggleDelMode" v-if="!isDelMode"></u-icon>
  9. <text class="qfn-done" @click="toggleDelMode" v-if="isDelMode">完成</text>
  10. <u-icon name="plus" size="36" color="#999" style="margin-left:16rpx" @click="openAddPopup"></u-icon>
  11. </view>
  12. </view>
  13. <scroll-view scroll-x class="qfn-scroll" v-if="quickFunctions.length">
  14. <view class="qfn-item" v-for="(it, i) in quickFunctions" :key="it.id || i" @click="handleQuick(it, i)">
  15. <view class="qfn-icon" :class="isDelMode ? 'qfn-del' : ''">
  16. <!-- <u-icon :name="it.icon||'grid'" size="44" color="#157A2C"></u-icon> -->
  17. <span :class="'iconfont ' + it.icon"></span>
  18. </view>
  19. <text class="qfn-name">{{ it.name }}</text>
  20. <u-icon v-if="isDelMode" name="minus-circle-fill" size="32" color="#ff4949"
  21. class="qfn-badge"></u-icon>
  22. </view>
  23. </scroll-view>
  24. <view class="qfn-empty" v-else @click="openAddPopup">
  25. <u-icon name="plus-circle" size="40" color="#ccc"></u-icon>
  26. <text class="qfn-empty-tip">添加常用功能</text>
  27. </view>
  28. </view>
  29. <!-- 原有导航 -->
  30. <view class="nav">
  31. <view class="nav-item" v-for="(item, index) in workList" @click="toNav(item.link_url)">
  32. <span :class="item.class"></span>
  33. <i class="badge" v-if="item.badge">{{ item.badge }}</i>
  34. <label>{{ item.title }}</label>
  35. </view>
  36. </view>
  37. <!-- 选择菜单弹窗 -->
  38. <u-popup :show="showAddPopup" mode="bottom" :round="16" @close="showAddPopup = false" closeable>
  39. <view class="add-popup">
  40. <view class="add-popup-head">选择菜单</view>
  41. <scroll-view scroll-y class="add-popup-body">
  42. <view class="add-popup-row" v-for="(m, index) in availableMenus" :key="m.id" @click="addQuick(m, index)">
  43. <view class="add-popup-left">
  44. <text class="add-popup-name">{{ m.name }}</text>
  45. </view>
  46. <u-icon v-if="checkedIds[m.id]" name="checkbox-mark" size="36" color="#157A2C"></u-icon>
  47. <u-icon v-else name="plus-circle" size="36" color="#ccc"></u-icon>
  48. </view>
  49. <view v-if="!availableMenus.length" class="add-popup-none">暂无菜单</view>
  50. </scroll-view>
  51. </view>
  52. </u-popup>
  53. <u-toast ref="uToast"></u-toast>
  54. </view>
  55. </template>
  56. <script>
  57. import { todoNuber } from '@/api/myTicket'
  58. import { userResourceListAPI, userResourceSaveAPI, userResourceDeleteAPI } from '@/api/home'
  59. export default {
  60. props: { workOrder: { default: {} } },
  61. data() {
  62. return {
  63. workList: [
  64. { class: 'iconfont icon-bianji', title: '工单待办', link_url: '/pages/home/myTicket/myTicket?orderType=1', badge: 0 },
  65. { class: 'iconfont icon-bianji', title: '工单已办', link_url: '/pages/home/myTicket/myTicket?orderType=2', badge: 0 },
  66. { class: 'iconfont icon-kuneipandian', title: '已发流程', link_url: '/pages/home/wt/send/send', badge: 0 },
  67. { class: 'iconfont icon-kuneipandian', title: '流程待办', link_url: '/pages/home/wt/todo/todo', badge: 0 },
  68. { class: 'iconfont icon-kuneipandian', title: '流程已办', link_url: '/pages/home/wt/done/done' }
  69. ],
  70. quickFunctions: [],
  71. isDelMode: false,
  72. showAddPopup: false,
  73. availableMenus: [],
  74. }
  75. },
  76. watch: {
  77. workOrder: { handler(v) { this.workList[0].badge = v.total }, deep: true }
  78. },
  79. computed: {
  80. // 已勾选的菜单 resourceId 映射(resourceId 即菜单 tree 中的 id)
  81. checkedIds: function() {
  82. var map = {}
  83. var qf = this.quickFunctions || []
  84. for (var i = 0; i < qf.length; i++) {
  85. map[qf[i].resourceId] = true
  86. }
  87. return map
  88. }
  89. },
  90. created() {
  91. this.gettodoNuber()
  92. this.loadQuick()
  93. this.loadMenus()
  94. },
  95. methods: {
  96. toNav(url) { uni.navigateTo({ url }) },
  97. gettodoNuber() { todoNuber().then(r => { this.workList[3].badge = r.count }) },
  98. // ===== 快捷功能 =====
  99. async loadQuick() {
  100. try { this.quickFunctions = await userResourceListAPI() } catch (e) {}
  101. },
  102. loadMenus() {
  103. try {
  104. var raw = uni.getStorageSync('treeList')
  105. if (!raw) { this.availableMenus = []; return }
  106. var tree = typeof raw === 'string' ? JSON.parse(raw) : raw
  107. if (Array.isArray(tree) && tree.length === 1 && tree[0].children) {
  108. tree = tree[0].children
  109. }
  110. var arr = []
  111. var walk = function (list) {
  112. (list || []).forEach(function (it) {
  113. if (it.menuType === 2) return
  114. if (it.path && it.path.indexOf('/pages/') === 0) {
  115. arr.push({ id: it.id, name: it.name || it.title || '', url: it.path, icon: it.icon || 'grid' })
  116. }
  117. if (it.children && it.children.length) { walk(it.children) }
  118. })
  119. }
  120. walk(tree)
  121. this.availableMenus = arr
  122. } catch (e) { this.availableMenus = [] }
  123. },
  124. toggleDelMode() { this.isDelMode = !this.isDelMode },
  125. openAddPopup() {
  126. console.log('availableMenus~~', this.availableMenus)
  127. if (!this.availableMenus.length) this.loadMenus()
  128. this.showAddPopup = true
  129. },
  130. async addQuick(m, i) {
  131. // m.id = menu tree ID, quickFunctions[] 中对应 resourceId
  132. if (this.checkedIds[m.id]) {
  133. // 找到对应的用户资源记录 ID 来删除
  134. var found = this.quickFunctions.find(function(it) { return String(it.resourceId) === String(m.id) })
  135. if (found) {
  136. await userResourceDeleteAPI([found.id])
  137. }
  138. this.quickFunctions = this.quickFunctions.filter(function(it) { return String(it.resourceId) !== String(m.id) })
  139. } else {
  140. if (this.quickFunctions.length >= 12) return uni.showToast({ title: '最多12个', icon: 'none' })
  141. await userResourceSaveAPI({ icon: '', pic: '', resourceId: m.id, sort: i, useScope: 2 })
  142. // 保存后重新拉取列表,确保拿到服务端返回的 record id
  143. await this.loadQuick()
  144. }
  145. },
  146. async handleQuick(it, i) {
  147. if (this.isDelMode) {
  148. await userResourceDeleteAPI([it.id])
  149. this.quickFunctions.splice(i, 1)
  150. return
  151. }
  152. if (it.url) { uni.navigateTo({ url: it.url }) }
  153. },
  154. }
  155. }
  156. </script>
  157. <style lang="scss" scoped>
  158. /* 快捷功能 */
  159. .quick-functions-box {
  160. background: #fff;
  161. border-radius: 16rpx;
  162. margin-bottom: 16rpx;
  163. padding: 20rpx;
  164. }
  165. .qfn-header {
  166. display: flex;
  167. justify-content: space-between;
  168. align-items: center;
  169. margin-bottom: 16rpx;
  170. }
  171. .qfn-title {
  172. font-size: 28rpx;
  173. font-weight: bold;
  174. color: #333;
  175. }
  176. .qfn-actions {
  177. display: flex;
  178. align-items: center;
  179. }
  180. .qfn-done {
  181. font-size: 26rpx;
  182. color: #ff4949;
  183. padding: 4rpx 16rpx;
  184. }
  185. .qfn-scroll {
  186. white-space: nowrap;
  187. width: 100%;
  188. }
  189. .qfn-item {
  190. display: inline-flex;
  191. flex-direction: column;
  192. align-items: center;
  193. width: 140rpx;
  194. margin-right: 20rpx;
  195. position: relative;
  196. }
  197. .qfn-item:last-child {
  198. margin-right: 0;
  199. }
  200. .qfn-icon {
  201. width: 88rpx;
  202. height: 88rpx;
  203. border-radius: 50%;
  204. background: #f5f7fa;
  205. display: flex;
  206. align-items: center;
  207. justify-content: center;
  208. margin-bottom: 10rpx;
  209. box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, .06);
  210. }
  211. .qfn-icon span {
  212. font-size: 44rpx;
  213. color: #157A2C;
  214. }
  215. .qfn-del {
  216. border: 2rpx dashed #ff4949;
  217. }
  218. .qfn-name {
  219. font-size: 26rpx;
  220. color: #333;
  221. text-align: center;
  222. overflow: hidden;
  223. text-overflow: ellipsis;
  224. white-space: nowrap;
  225. width: 100%;
  226. }
  227. .qfn-badge {
  228. position: absolute;
  229. top: -4rpx;
  230. right: -4rpx;
  231. }
  232. .qfn-empty {
  233. display: flex;
  234. flex-direction: column;
  235. align-items: center;
  236. padding: 30rpx 0;
  237. }
  238. .qfn-empty-tip {
  239. font-size: 24rpx;
  240. color: #ccc;
  241. margin-top: 10rpx;
  242. }
  243. /* 弹窗 */
  244. .add-popup {
  245. height: 60vh;
  246. padding: 30rpx;
  247. display: flex;
  248. flex-direction: column;
  249. }
  250. .add-popup-head {
  251. font-size: 30rpx;
  252. font-weight: bold;
  253. color: #333;
  254. text-align: center;
  255. margin-bottom: 20rpx;
  256. }
  257. .add-popup-body {
  258. flex: 1;
  259. min-height: 0;
  260. }
  261. .add-popup-row {
  262. display: flex;
  263. justify-content: space-between;
  264. align-items: center;
  265. padding: 24rpx 0;
  266. border-bottom: 1rpx solid #f0f0f0;
  267. }
  268. .add-popup-left {
  269. display: flex;
  270. align-items: center;
  271. flex: 1;
  272. }
  273. .add-popup-name {
  274. font-size: 28rpx;
  275. color: #333;
  276. margin-left: 20rpx;
  277. }
  278. .add-popup-none {
  279. text-align: center;
  280. padding: 60rpx 0;
  281. color: #999;
  282. font-size: 28rpx;
  283. }
  284. /* 原有导航 */
  285. .nav {
  286. display: flex;
  287. width: 100%;
  288. padding: 32rpx 0;
  289. margin-bottom: 24rpx;
  290. align-items: center;
  291. justify-content: space-around;
  292. background-color: #fff;
  293. font-size: $uni-font-size-sm;
  294. color: $uni-text-color;
  295. border-radius: 16rpx;
  296. }
  297. .nav-item {
  298. position: relative;
  299. display: flex;
  300. flex-direction: column;
  301. align-items: center;
  302. margin-top: 5rpx;
  303. flex: 1;
  304. }
  305. .nav-item span {
  306. padding: 18rpx;
  307. color: #fff;
  308. margin-bottom: 14rpx;
  309. border-radius: 50%;
  310. }
  311. .badge {
  312. position: absolute;
  313. top: -8rpx;
  314. left: 100rpx;
  315. font-size: 24rpx;
  316. padding: 0 10rpx;
  317. border-radius: 38rpx;
  318. background: #ff4949;
  319. color: #fff;
  320. font-style: normal;
  321. }
  322. .nav-item:nth-child(1) span {
  323. background-color: $uni-color-success;
  324. }
  325. .nav-item:nth-child(2) span {
  326. background-color: $uni-color-warning;
  327. }
  328. .nav-item:nth-child(3) span {
  329. background-color: $uni-color-primary;
  330. }
  331. .nav-item:nth-child(4) span {
  332. background-color: $uni-color-error;
  333. }
  334. .nav-item:nth-child(5) span {
  335. background-color: $uni-color-success;
  336. }
  337. </style>