PageHeader.vue 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. <!-- 顶部信息栏 -->
  2. <template>
  3. <header class="page-header">
  4. <div class="header-left">
  5. <button v-if="showBack" class="action-btn back-btn" @click="handleBack">
  6. <i class="fas fa-arrow-left mr-1" /> 返回
  7. </button>
  8. <div class="user-info">
  9. <i class="fas fa-user text-blue-500 mr-2" />
  10. <span class="font-medium">{{ operatorName }}</span>
  11. <!-- <span class="font-medium">操作员:{{ operatorName }} (ID: {{ operatorId }})</span> -->
  12. </div>
  13. </div>
  14. <div v-if="title" class="header-center">
  15. <h1 class="page-header-title">{{ title }}</h1>
  16. </div>
  17. <div class="header-right">
  18. <!-- 自定义插槽,用于插入额外按钮(如领料车) -->
  19. <slot name="actions" />
  20. <!-- 设置下拉菜单 -->
  21. <div ref="settingsDropdown" class="settings-dropdown">
  22. <button class="action-btn settings-btn" @click="toggleSettings">
  23. <i class="fas fa-cog" />
  24. </button>
  25. <div v-if="showSettings" class="dropdown-menu">
  26. <div class="dropdown-item" @click="goToFingerprintEnroll">
  27. <i class="fas fa-fingerprint mr-2" />
  28. <span>指纹录入</span>
  29. </div>
  30. </div>
  31. </div>
  32. <button class="action-btn logout-btn" @click="handleLogout">
  33. <i class="fas fa-sign-out-alt mr-1" /> 登出
  34. </button>
  35. </div>
  36. </header>
  37. </template>
  38. <script setup>
  39. import { useRouter } from 'vue-router';
  40. import { ref, defineProps, defineEmits, onMounted, onUnmounted } from 'vue';
  41. const props = defineProps({
  42. showBack: {
  43. type: Boolean,
  44. default: true,
  45. },
  46. isGoHome: {
  47. type: Boolean,
  48. default: true,
  49. },
  50. isCustomBack: {
  51. type: Boolean,
  52. default: false,
  53. },
  54. title: {
  55. type: String,
  56. default: '',
  57. },
  58. });
  59. const emit = defineEmits(['back', 'logout']);
  60. const router = useRouter();
  61. const operatorName = ref('管理员');
  62. const operatorId = ref('lw001');
  63. const showSettings = ref(false);
  64. const settingsDropdown = ref(null);
  65. onMounted(() => {
  66. const loginInfo = JSON.parse(localStorage.getItem('#LoginInfo'));
  67. if (loginInfo) {
  68. operatorName.value = loginInfo.userName;
  69. operatorId.value = loginInfo.userId;
  70. }
  71. // 添加点击外部关闭下拉菜单的事件监听
  72. document.addEventListener('click', handleClickOutside);
  73. });
  74. onUnmounted(() => {
  75. // 移除事件监听
  76. document.removeEventListener('click', handleClickOutside);
  77. });
  78. const handleBack = () => {
  79. if (props.isCustomBack) {
  80. emit('back');
  81. return;
  82. }
  83. if (props.isGoHome) {
  84. router.push('/home');
  85. } else {
  86. router.back();
  87. }
  88. emit('back');
  89. };
  90. const handleLogout = () => {
  91. emit('logout');
  92. localStorage.removeItem('#LoginInfo');
  93. localStorage.removeItem('#token');
  94. localStorage.removeItem('#accountId');
  95. // 检查localStorage中的isOut值,如果为true则在登录页面添加isOut参数
  96. const isOut = localStorage.getItem('isOut') === 'true';
  97. if (isOut) {
  98. router.push({ path: '/fingerprint-login', query: { isOut: 'true' } });
  99. } else {
  100. router.push('/fingerprint-login');
  101. }
  102. };
  103. // 切换设置下拉菜单
  104. const toggleSettings = event => {
  105. event.stopPropagation();
  106. showSettings.value = !showSettings.value;
  107. };
  108. // 点击外部关闭下拉菜单
  109. const handleClickOutside = event => {
  110. if (settingsDropdown.value && !settingsDropdown.value.contains(event.target)) {
  111. showSettings.value = false;
  112. }
  113. };
  114. // 跳转到指纹录入页面
  115. const goToFingerprintEnroll = () => {
  116. showSettings.value = false;
  117. router.push('/fingerprint-enroll');
  118. };
  119. </script>
  120. <style scoped>
  121. .page-header {
  122. display: flex;
  123. align-items: center;
  124. justify-content: space-between;
  125. padding-top: 1rem;
  126. padding-bottom: 1rem;
  127. padding-left: 1.5rem;
  128. padding-right: 1.5rem;
  129. background-color: white;
  130. box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
  131. position: sticky;
  132. top: 0;
  133. z-index: 100;
  134. }
  135. .header-left {
  136. display: flex;
  137. align-items: center;
  138. flex: 1;
  139. }
  140. .header-left > * {
  141. margin-right: 1rem;
  142. }
  143. .header-left > *:last-child {
  144. margin-right: 0;
  145. }
  146. .header-center {
  147. display: flex;
  148. align-items: center;
  149. justify-content: center;
  150. flex: 1;
  151. }
  152. .page-header-title {
  153. font-size: 1.5rem;
  154. font-weight: 700;
  155. color: #111827;
  156. margin: 0;
  157. }
  158. .header-right {
  159. display: flex;
  160. align-items: center;
  161. flex: 1;
  162. justify-content: flex-end;
  163. }
  164. .header-right > * {
  165. margin-left: 0.75rem;
  166. }
  167. .header-right > *:first-child {
  168. margin-left: 0;
  169. }
  170. .user-info {
  171. display: flex;
  172. align-items: center;
  173. }
  174. .action-btn {
  175. padding-top: 0.65rem;
  176. padding-bottom: 0.65rem;
  177. padding-left: 1rem;
  178. padding-right: 1rem;
  179. border-radius: 0.5rem;
  180. font-weight: 500;
  181. transition: all 0.2s;
  182. display: inline-flex;
  183. align-items: center;
  184. border: none;
  185. cursor: pointer;
  186. font-size: 14px;
  187. }
  188. .back-btn {
  189. background-color: #f3f4f6;
  190. color: #374151;
  191. }
  192. .back-btn:hover {
  193. background-color: #e5e7eb;
  194. }
  195. .logout-btn {
  196. background-color: #fee2e2;
  197. color: #dc2626;
  198. }
  199. .logout-btn:hover {
  200. background-color: #fecaca;
  201. }
  202. /* 设置下拉菜单样式 */
  203. .settings-dropdown {
  204. position: relative;
  205. }
  206. .settings-btn {
  207. background-color: #f3f4f6;
  208. color: #374151;
  209. padding-top: 0.8rem;
  210. padding-bottom: 0.8rem;
  211. padding-left: 0.8rem;
  212. padding-right: 0.8rem;
  213. /* margin-right: 0.75rem; */
  214. }
  215. .settings-btn:hover {
  216. background-color: #e5e7eb;
  217. }
  218. .settings-btn i {
  219. font-size: 24px;
  220. }
  221. .dropdown-menu {
  222. position: absolute;
  223. top: calc(100% + 0.5rem);
  224. right: 0;
  225. background-color: white;
  226. border-radius: 0.5rem;
  227. box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
  228. min-width: 200px;
  229. z-index: 1000;
  230. overflow: hidden;
  231. animation: dropdownFadeIn 0.2s ease;
  232. }
  233. @keyframes dropdownFadeIn {
  234. from {
  235. opacity: 0;
  236. transform: translateY(-10px);
  237. }
  238. to {
  239. opacity: 1;
  240. transform: translateY(0);
  241. }
  242. }
  243. .dropdown-item {
  244. padding-top: 1.2rem;
  245. padding-bottom: 1.2rem;
  246. padding-left: 1.5rem;
  247. padding-right: 1.5rem;
  248. display: flex;
  249. align-items: center;
  250. cursor: pointer;
  251. transition: background-color 0.2s;
  252. color: #374151;
  253. font-size: 1.3rem;
  254. font-weight: 600;
  255. }
  256. .dropdown-item:hover {
  257. background-color: #f3f4f6;
  258. }
  259. .dropdown-item i {
  260. color: #3b82f6;
  261. font-size: 1.2rem;
  262. margin-right: 0.75rem;
  263. }
  264. </style>