stationSwitcher.vue 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. <template>
  2. <div class="layout-navbars-breadcrumb-station"
  3. :class="{'is-open': state.open, 'is-empty': !state.loading && state.stationList.length === 0}"
  4. title="切换当前查看的站点,页面数据范围会跟随变化">
  5. <el-select-v2
  6. v-model="state.currentStationId"
  7. :options="state.options"
  8. :loading="state.loading"
  9. :loading-text="'正在加载站点…'"
  10. :no-data-text="'没有匹配的站点'"
  11. :disabled="!state.loading && state.stationList.length === 0"
  12. :placeholder="placeholder"
  13. filterable
  14. popper-class="station-select-popper"
  15. class="station-select"
  16. @change="handleStationChange"
  17. @visible-change="state.open = $event">
  18. <template #prefix>
  19. <SvgIcon name="ele-Location" :size="15"/>
  20. </template>
  21. <template #header>
  22. <span>站点范围</span>
  23. <span>共 {{ state.stationList.length }} 个站点</span>
  24. </template>
  25. </el-select-v2>
  26. </div>
  27. </template>
  28. <script setup lang="ts" name="layoutBreadcrumbStationSwitcher">
  29. import {reactive, computed, onMounted} from 'vue';
  30. import mittBus from '/@/utils/mitt';
  31. import {Session} from '/@/utils/storage';
  32. import {$body} from '/@/utils/request';
  33. const state = reactive({
  34. stationList: [] as Array<any>,
  35. currentStationId: null as any,
  36. options: [] as Array<any>,
  37. loading: true,
  38. open: false
  39. });
  40. const placeholder = computed(() => {
  41. if (state.loading) return '加载站点中';
  42. if (state.stationList.length === 0) return '暂无站点';
  43. return '选择站点';
  44. });
  45. //站点切换
  46. const handleStationChange = (stationId: string) => {
  47. let station = state.stationList.find((k: any) => k.stationId === stationId);
  48. if (station) {
  49. Session.set("currentStationId", stationId);
  50. mittBus.emit('stationChangeRefresh', stationId);
  51. }
  52. }
  53. const loadStationList = () => {
  54. $body(`/washStation/list`, {pageNum: 1, pageSize: 1024}).then((res: any) => {
  55. let {list} = res;
  56. state.stationList = list || [];
  57. state.options = state.stationList.map((k: any) => {
  58. return {value: k.stationId, label: k.stationName}
  59. });
  60. if (state.stationList.length > 0) {
  61. //优先恢复上次选择的站点,其次默认第一个
  62. let savedId = Session.get('currentStationId');
  63. let current = state.stationList.find((k: any) => k.stationId === savedId) || state.stationList[0];
  64. state.currentStationId = current.stationId;
  65. setTimeout(() => {
  66. Session.set("currentStationId", current.stationId);
  67. mittBus.emit('stationChangeRefresh', current.stationId);
  68. }, 200)
  69. }
  70. }).catch(e => {
  71. //console.error(e)
  72. }).finally(() => {
  73. state.loading = false;
  74. })
  75. }
  76. // 页面加载时
  77. onMounted(() => {
  78. loadStationList();
  79. });
  80. </script>
  81. <style scoped lang="scss">
  82. .layout-navbars-breadcrumb-station {
  83. display: flex;
  84. align-items: center;
  85. height: 50px;
  86. padding: 0 12px 0 4px;
  87. position: relative;
  88. //与面包屑之间的分隔:范围(哪个站)与位置(哪个页)是两个语境
  89. &::after {
  90. content: '';
  91. position: absolute;
  92. right: 0;
  93. top: 50%;
  94. transform: translateY(-50%);
  95. width: 1px;
  96. height: 20px;
  97. background: var(--el-border-color-lighter);
  98. }
  99. .station-select {
  100. width: 180px;
  101. //去框融入顶栏:静态时无边框无底色,悬停/聚焦时才浮起一层浅底
  102. :deep(.el-select__wrapper) {
  103. height: 34px;
  104. min-height: 34px;
  105. padding: 0 8px;
  106. border-radius: 8px;
  107. background: transparent;
  108. box-shadow: none;
  109. transition: background-color .2s cubic-bezier(.4, 0, .2, 1);
  110. &.is-hovering {
  111. background: var(--next-color-user-hover);
  112. }
  113. &.is-focused {
  114. background: var(--next-color-user-hover);
  115. }
  116. &.is-disabled {
  117. background: transparent;
  118. }
  119. }
  120. :deep(.el-select__prefix) {
  121. display: flex;
  122. align-items: center;
  123. margin-right: 6px;
  124. color: var(--next-bg-topBarColor);
  125. transition: color .2s cubic-bezier(.4, 0, .2, 1);
  126. }
  127. :deep(.el-select__placeholder),
  128. :deep(.el-select__input) {
  129. color: var(--next-bg-topBarColor);
  130. font-size: 14px;
  131. font-weight: 500;
  132. transition: color .2s cubic-bezier(.4, 0, .2, 1);
  133. }
  134. //未选中任何站点时的占位:退一档
  135. :deep(.el-select__placeholder.is-transparent) {
  136. color: var(--el-text-color-placeholder);
  137. font-weight: 400;
  138. }
  139. :deep(.el-select__caret) {
  140. color: var(--next-bg-topBarColor);
  141. opacity: 0.6;
  142. transform-origin: center;
  143. transition: transform .2s cubic-bezier(.4, 0, .2, 1), opacity .2s cubic-bezier(.4, 0, .2, 1), color .2s;
  144. }
  145. }
  146. //悬停时文字与图标转为品牌色,呼应 Ghost 按钮规则(禁用态除外)
  147. &:not(.is-empty) .station-select:hover {
  148. :deep(.el-select__wrapper .el-select__placeholder),
  149. :deep(.el-select__wrapper .el-select__input),
  150. :deep(.el-select__wrapper .el-select__prefix),
  151. :deep(.el-select__wrapper .el-select__caret) {
  152. color: var(--el-color-primary);
  153. opacity: 1;
  154. }
  155. }
  156. //展开时箭头翻转
  157. &.is-open .station-select {
  158. :deep(.el-select__caret) {
  159. transform: rotate(180deg);
  160. opacity: 1;
  161. color: var(--el-color-primary);
  162. }
  163. }
  164. //禁用态(无站点可用)
  165. &:deep(.el-select__wrapper.is-disabled .el-select__placeholder),
  166. &:deep(.el-select__wrapper.is-disabled .el-select__prefix),
  167. &:deep(.el-select__wrapper.is-disabled .el-select__caret) {
  168. color: var(--el-text-color-disabled);
  169. opacity: 1;
  170. }
  171. }
  172. </style>
  173. <style lang="scss">
  174. // 下拉面板(teleported,需全局样式)
  175. .station-select-popper {
  176. .el-select-dropdown {
  177. padding: 6px;
  178. border: none;
  179. border-radius: 12px;
  180. box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.08), 0 4px 6px -4px rgba(0, 0, 0, 0.06);
  181. }
  182. .el-select-dropdown__header {
  183. display: flex;
  184. justify-content: space-between;
  185. padding: 4px 10px 6px;
  186. font-size: 12px;
  187. font-weight: 500;
  188. color: var(--el-text-color-secondary);
  189. span:last-child {
  190. font-weight: 400;
  191. color: var(--el-text-color-placeholder);
  192. }
  193. }
  194. .el-select-dropdown__list {
  195. padding: 2px;
  196. }
  197. .el-select-dropdown__item {
  198. height: 34px;
  199. margin: 2px 0;
  200. padding: 0 10px;
  201. border-radius: 8px;
  202. font-size: 14px;
  203. }
  204. .el-select-dropdown__item.is-hovering {
  205. background: var(--el-fill-color-light);
  206. }
  207. //当前站点:品牌色 + 加粗标记「你在这里」
  208. .el-select-dropdown__item.is-selected {
  209. color: var(--el-color-primary);
  210. font-weight: 600;
  211. }
  212. .el-select-dropdown__empty {
  213. padding: 14px 10px;
  214. font-size: 13px;
  215. color: var(--el-text-color-placeholder);
  216. }
  217. }
  218. </style>