module-submenu.vue 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. <template>
  2. <div class="app-container submenu-container">
  3. <div class="page-header">
  4. <h2>{{ moduleTitle }}</h2>
  5. <!-- <p>请选择具体功能</p> -->
  6. </div>
  7. <div class="submenu-grid">
  8. <div
  9. v-for="item in menuItems"
  10. :key="item.path"
  11. class="submenu-card"
  12. @click="handleItemClick(item)"
  13. >
  14. <div class="submenu-icon">
  15. <svg-icon v-if="item.meta && item.meta.icon" :icon-class="item.meta.icon" class="icon" />
  16. <i v-else class="el-icon-document icon"></i>
  17. </div>
  18. <div class="submenu-title">{{ formatMenuTitle(item.meta ? item.meta.title : item.name) }}</div>
  19. </div>
  20. </div>
  21. </div>
  22. </template>
  23. <script>
  24. import { mapGetters } from 'vuex'
  25. export default {
  26. name: 'ModuleSubmenu',
  27. data() {
  28. return {
  29. moduleTitle: '',
  30. menuItems: []
  31. }
  32. },
  33. computed: {
  34. ...mapGetters(['sidebarRouters'])
  35. },
  36. created() {
  37. this.loadMenuItems()
  38. },
  39. watch: {
  40. '$route.query': {
  41. handler() {
  42. this.loadMenuItems()
  43. },
  44. deep: true
  45. }
  46. },
  47. methods: {
  48. loadMenuItems() {
  49. const { modulePath, moduleTitle } = this.$route.query
  50. this.moduleTitle = moduleTitle || '功能菜单'
  51. // 查找对应的菜单项
  52. this.menuItems = this.findMenuItems(modulePath)
  53. },
  54. findMenuItems(modulePath) {
  55. // 从系统菜单查找
  56. if (this.sidebarRouters && this.sidebarRouters.length > 0) {
  57. for (const route of this.sidebarRouters) {
  58. if (route.hidden) {
  59. continue
  60. }
  61. if (route.path === modulePath && route.children) {
  62. // 返回系统模块的子菜单,确保路径完整
  63. const items = route.children
  64. .filter(child => !child.hidden)
  65. .map(child => {
  66. // 如果路径是相对路径,需要拼接父路径
  67. const parentPath = route.path === '/' ? '' : route.path.replace(/\/$/, '')
  68. const fullPath = child.path.startsWith('/') ? child.path : `${parentPath}/${child.path}`
  69. return {
  70. ...child,
  71. path: fullPath
  72. }
  73. })
  74. return this.appendLocalMenuItems(modulePath, items)
  75. }
  76. }
  77. }
  78. return this.appendLocalMenuItems(modulePath, [])
  79. },
  80. appendLocalMenuItems(modulePath, items) {
  81. const result = [...items]
  82. if (modulePath === '/order' && !result.some(item => item.path === '/order/shopreport')) {
  83. result.push({
  84. path: '/order/shopreport',
  85. name: 'ShopOperationReport',
  86. meta: {
  87. title: '店铺运营报表',
  88. icon: 'excel'
  89. }
  90. })
  91. }
  92. if (modulePath === '/supply' && !result.some(item => item.path === '/supply/monitor')) {
  93. const monitorItem = {
  94. path: '/supply/monitor',
  95. name: 'SupplyMonitorEnhancement',
  96. meta: {
  97. title: '供应商查询对比与付款计划',
  98. icon: 'chart'
  99. }
  100. }
  101. const weightsIndex = result.findIndex(item => item.path === '/supply/weights' || (item.meta && item.meta.title === '权重设置'))
  102. if (weightsIndex >= 0) {
  103. result.splice(weightsIndex, 0, monitorItem)
  104. } else {
  105. result.push(monitorItem)
  106. }
  107. }
  108. return result
  109. },
  110. handleItemClick(item) {
  111. this.$router.push(item.path)
  112. },
  113. formatMenuTitle(title) {
  114. return title === '半成品模块' ? '产品模块' : title
  115. }
  116. }
  117. }
  118. </script>
  119. <style scoped lang="scss">
  120. .submenu-container {
  121. .page-header {
  122. text-align: center;
  123. margin-bottom: 48px;
  124. padding: 32px 24px;
  125. background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  126. border-radius: 16px;
  127. box-shadow: 0 8px 32px 0 rgba(102, 126, 234, 0.2);
  128. h2 {
  129. font-size: 32px;
  130. font-weight: 700;
  131. color: #ffffff;
  132. margin-bottom: 12px;
  133. text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  134. }
  135. p {
  136. font-size: 16px;
  137. color: rgba(255, 255, 255, 0.9);
  138. font-weight: 300;
  139. }
  140. }
  141. .submenu-grid {
  142. display: grid;
  143. grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  144. gap: 24px;
  145. .submenu-card {
  146. background: #ffffff;
  147. border-radius: 12px;
  148. padding: 32px 24px;
  149. text-align: center;
  150. cursor: pointer;
  151. transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  152. border: 2px solid transparent;
  153. box-shadow: 0 4px 12px 0 rgba(0, 0, 0, 0.08);
  154. position: relative;
  155. overflow: hidden;
  156. &::before {
  157. content: '';
  158. position: absolute;
  159. top: 0;
  160. left: 0;
  161. right: 0;
  162. height: 4px;
  163. background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
  164. transform: scaleX(0);
  165. transition: transform 0.3s ease;
  166. }
  167. &:hover {
  168. background: #ffffff;
  169. border-color: transparent;
  170. transform: translateY(-8px);
  171. box-shadow: 0 12px 32px 0 rgba(102, 126, 234, 0.25);
  172. &::before {
  173. transform: scaleX(1);
  174. }
  175. .submenu-icon {
  176. background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  177. transform: scale(1.1) rotate(5deg);
  178. box-shadow: 0 8px 24px rgba(102, 126, 234, 0.3);
  179. ::v-deep(.svg-icon),
  180. .icon {
  181. color: #ffffff;
  182. }
  183. }
  184. .submenu-title {
  185. color: #667eea;
  186. }
  187. }
  188. .submenu-icon {
  189. display: inline-flex;
  190. align-items: center;
  191. justify-content: center;
  192. width: 72px;
  193. height: 72px;
  194. border-radius: 16px;
  195. background: linear-gradient(135deg, #f5f7fa 0%, #e4e7ed 100%);
  196. margin: 0 auto 20px;
  197. transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  198. box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
  199. ::v-deep(.svg-icon) {
  200. font-size: 36px;
  201. color: #606266;
  202. transition: color 0.3s ease;
  203. }
  204. .icon {
  205. font-size: 36px;
  206. color: #606266;
  207. transition: color 0.3s ease;
  208. }
  209. }
  210. .submenu-title {
  211. font-size: 17px;
  212. font-weight: 600;
  213. color: #303133;
  214. transition: color 0.3s ease;
  215. line-height: 1.5;
  216. }
  217. }
  218. }
  219. // 响应式布局
  220. @media (max-width: 1200px) {
  221. .submenu-grid {
  222. grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  223. gap: 20px;
  224. }
  225. }
  226. @media (max-width: 768px) {
  227. .page-header {
  228. margin-bottom: 32px;
  229. padding: 24px 16px;
  230. h2 {
  231. font-size: 24px;
  232. }
  233. p {
  234. font-size: 14px;
  235. }
  236. }
  237. .submenu-grid {
  238. grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  239. gap: 16px;
  240. .submenu-card {
  241. padding: 28px 20px;
  242. .submenu-icon {
  243. width: 60px;
  244. height: 60px;
  245. margin-bottom: 16px;
  246. ::v-deep(.svg-icon),
  247. .icon {
  248. font-size: 30px;
  249. }
  250. }
  251. .submenu-title {
  252. font-size: 15px;
  253. }
  254. }
  255. }
  256. }
  257. }
  258. </style>