Browse Source

feat: 新增基本布局

sunxiao 4 months ago
parent
commit
31bbb4f4df

+ 1 - 0
package.json

@@ -35,6 +35,7 @@
     "@vitejs/plugin-vue": "5.0.5",
     "naive-ui": "^2.40.1",
     "sass": "1.77.5",
+    "tailwindcss": "^3.4.14",
     "unplugin-auto-import": "0.17.6",
     "unplugin-vue-setup-extend-plus": "1.0.1",
     "vite": "5.3.2",

+ 4 - 0
src/assets/images/header/icon-call-off.svg

@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40" fill="none">
+  <rect width="40" height="40" rx="20" fill="#DB4445"/>
+  <path d="M19.7016 16.0879C15.7137 16.0879 11.9653 16.926 10.0957 18.7956C9.24843 19.6429 8.82478 20.6652 8.88004 21.8993C8.91688 22.6453 9.14712 23.3084 9.57998 23.7412C9.90233 24.0728 10.3536 24.257 10.8786 24.1741L14.2954 23.5939C14.8112 23.511 15.1703 23.3544 15.4006 23.115C15.7045 22.8203 15.7966 22.3782 15.7966 21.798V20.8678C15.7966 20.7204 15.8611 20.6099 15.9532 20.5178C16.0453 20.4073 16.1834 20.3612 16.2847 20.3336C16.911 20.1863 18.182 20.0481 19.7016 20.0481C21.2212 20.0481 22.4829 20.1586 23.1184 20.3428C23.2105 20.3705 23.3395 20.4257 23.4408 20.5178C23.5236 20.6099 23.5789 20.7112 23.5881 20.8586L23.5973 21.798C23.6065 22.3782 23.6986 22.8203 23.9933 23.115C24.2328 23.3544 24.592 23.511 25.1077 23.5939L28.4785 24.1649C29.0219 24.257 29.4824 24.0636 29.8416 23.7136C30.2744 23.29 30.5139 22.6361 30.5323 21.8901C30.5599 20.6467 30.081 19.6245 29.2522 18.7956C27.3826 16.926 23.6894 16.0879 19.7016 16.0879Z" fill="white"/>
+</svg>

+ 4 - 0
src/assets/images/header/icon-call-on.svg

@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
+  <rect width="20" height="20" rx="10" fill="#34C759"/>
+  <path d="M7.88739 12.0186C9.21821 13.3494 10.8345 14.3763 12.1561 14.3763C12.7502 14.3763 13.2705 14.1691 13.6896 13.7086C13.9336 13.4369 14.0856 13.1191 14.0856 12.806C14.0856 12.5758 13.9981 12.3547 13.7817 12.1982L12.368 11.1943C12.1515 11.0469 11.9719 10.9732 11.8062 10.9732C11.599 10.9732 11.4102 11.093 11.1983 11.3002L10.8714 11.6225C10.8207 11.6732 10.7563 11.6962 10.6964 11.6962C10.6273 11.6962 10.5582 11.6686 10.5122 11.6456C10.2267 11.4936 9.73857 11.0746 9.28268 10.6233C8.8314 10.172 8.41235 9.68387 8.26499 9.39836C8.24197 9.34771 8.21434 9.28324 8.21434 9.21417C8.21434 9.1543 8.23276 9.09444 8.28341 9.04379L8.61036 8.70763C8.81298 8.4958 8.93731 8.3116 8.93731 8.09978C8.93731 7.934 8.85903 7.75441 8.70707 7.53798L7.71701 6.14269C7.55584 5.92165 7.3302 5.82495 7.08153 5.82495C6.77761 5.82495 6.46447 5.9631 6.19278 6.22558C5.74611 6.65384 5.5481 7.1834 5.5481 7.76822C5.5481 9.08984 6.55657 10.6923 7.88739 12.0186Z" fill="white"/>
+</svg>

+ 5 - 0
src/assets/images/header/icon-clean-btn.svg

@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
+  <path d="M5.83341 4.58337L1.66675 10L5.83341 15.4167H18.3334V4.58337H5.83341Z" stroke="#5092E1" stroke-linecap="round" stroke-linejoin="round"/>
+  <path d="M8.75 7.91663L12.9167 12.0833" stroke="#5092E1" stroke-linecap="round" stroke-linejoin="round"/>
+  <path d="M12.9167 7.91663L8.75 12.0833" stroke="#5092E1" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>

+ 6 - 0
src/assets/images/header/icon-phone-offline.svg

@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18" fill="none">
+  <path d="M5.99842 3.2572C6.2709 3.2572 6.52192 3.40496 6.65418 3.64317L7.57158 5.29573C7.6917 5.5121 7.69736 5.77382 7.58666 5.99518L6.70286 7.76282C6.70286 7.76282 6.95898 9.07959 8.03088 10.1515C9.10278 11.2234 10.4152 11.4751 10.4152 11.4751L12.1825 10.5914C12.404 10.4807 12.6659 10.4864 12.8824 10.6067L14.5396 11.5281C14.7776 11.6605 14.9252 11.9113 14.9252 12.1836V14.0862C14.9252 15.0551 14.0252 15.7549 13.1072 15.4451C11.2217 14.8089 8.29496 13.5976 6.43991 11.7425C4.58482 9.88742 3.3735 6.96065 2.7373 5.07519C2.42754 4.15715 3.12732 3.2572 4.0962 3.2572H5.99842Z" fill="#BCBCBC" stroke="#BCBCBC" stroke-linejoin="round"/>
+  <path opacity="0.01" d="M15.375 2.625H10.5V7.5H15.375V2.625Z" fill="#BCBCBC"/>
+  <path d="M14.625 3.375L11.25 6.75" stroke="#BCBCBC" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+  <path d="M11.25 3.375L14.625 6.75" stroke="#BCBCBC" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>

+ 7 - 0
src/assets/images/header/icon-phone-online.svg

@@ -0,0 +1,7 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18" fill="none">
+  <path d="M5.39076 3.67291C5.66323 3.67291 5.91426 3.82067 6.04648 4.05888L6.96392 5.71144C7.08403 5.92781 7.08966 6.18953 6.979 6.41089L6.0952 8.17849C6.0952 8.17849 6.35132 9.4953 7.42322 10.5672C8.49512 11.6391 9.80751 11.8908 9.80751 11.8908L11.5748 11.0072C11.7964 10.8964 12.0583 10.9021 12.2747 11.0225L13.9319 11.9438C14.1699 12.0761 14.3175 12.327 14.3175 12.5993V14.5019C14.3175 15.4708 13.4176 16.1706 12.4995 15.8608C10.6141 15.2246 7.6873 14.0133 5.83225 12.1582C3.97716 10.3031 2.76583 7.37636 2.12963 5.4909C1.81987 4.57286 2.51966 3.67291 3.48853 3.67291H5.39076Z" fill="#34C759" stroke="#34C759" stroke-linejoin="round"/>
+  <path d="M15.75 3.75H9" stroke="#34C759" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+  <path d="M13.5 2.25L15.75 3.75" stroke="#34C759" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+  <path d="M9.75 6.75H15.75" stroke="#34C759" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+  <path d="M9.75 6.75L11.25 8.25" stroke="#34C759" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>

+ 11 - 0
src/assets/images/header/icon-tel-keyboard.svg

@@ -0,0 +1,11 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
+  <path d="M4.16667 5.83333C5.08712 5.83333 5.83333 5.08712 5.83333 4.16667C5.83333 3.24619 5.08712 2.5 4.16667 2.5C3.24619 2.5 2.5 3.24619 2.5 4.16667C2.5 5.08712 3.24619 5.83333 4.16667 5.83333Z" fill="#86909C"/>
+  <path d="M9.99992 5.83333C10.9204 5.83333 11.6666 5.08712 11.6666 4.16667C11.6666 3.24619 10.9204 2.5 9.99992 2.5C9.07946 2.5 8.33325 3.24619 8.33325 4.16667C8.33325 5.08712 9.07946 5.83333 9.99992 5.83333Z" fill="#86909C"/>
+  <path d="M15.8334 5.83333C16.7539 5.83333 17.5001 5.08712 17.5001 4.16667C17.5001 3.24619 16.7539 2.5 15.8334 2.5C14.913 2.5 14.1667 3.24619 14.1667 4.16667C14.1667 5.08712 14.913 5.83333 15.8334 5.83333Z" fill="#86909C"/>
+  <path d="M4.16667 11.6666C5.08712 11.6666 5.83333 10.9204 5.83333 9.99998C5.83333 9.07952 5.08712 8.33331 4.16667 8.33331C3.24619 8.33331 2.5 9.07952 2.5 9.99998C2.5 10.9204 3.24619 11.6666 4.16667 11.6666Z" fill="#86909C"/>
+  <path d="M9.99992 11.6666C10.9204 11.6666 11.6666 10.9204 11.6666 9.99998C11.6666 9.07952 10.9204 8.33331 9.99992 8.33331C9.07946 8.33331 8.33325 9.07952 8.33325 9.99998C8.33325 10.9204 9.07946 11.6666 9.99992 11.6666Z" fill="#86909C"/>
+  <path d="M15.8334 11.6666C16.7539 11.6666 17.5001 10.9204 17.5001 9.99998C17.5001 9.07952 16.7539 8.33331 15.8334 8.33331C14.913 8.33331 14.1667 9.07952 14.1667 9.99998C14.1667 10.9204 14.913 11.6666 15.8334 11.6666Z" fill="#86909C"/>
+  <path d="M4.16667 17.5C5.08712 17.5 5.83333 16.7538 5.83333 15.8334C5.83333 14.9129 5.08712 14.1667 4.16667 14.1667C3.24619 14.1667 2.5 14.9129 2.5 15.8334C2.5 16.7538 3.24619 17.5 4.16667 17.5Z" fill="#86909C"/>
+  <path d="M9.99992 17.5C10.9204 17.5 11.6666 16.7538 11.6666 15.8334C11.6666 14.9129 10.9204 14.1667 9.99992 14.1667C9.07946 14.1667 8.33325 14.9129 8.33325 15.8334C8.33325 16.7538 9.07946 17.5 9.99992 17.5Z" fill="#86909C"/>
+  <path d="M15.8334 17.5C16.7539 17.5 17.5001 16.7538 17.5001 15.8334C17.5001 14.9129 16.7539 14.1667 15.8334 14.1667C14.913 14.1667 14.1667 14.9129 14.1667 15.8334C14.1667 16.7538 14.913 17.5 15.8334 17.5Z" fill="#86909C"/>
+</svg>

BIN
src/assets/images/login/bg-login-text.png


BIN
src/assets/images/login/bg-login.png


+ 11 - 0
src/assets/images/logo/logo-text.svg

@@ -0,0 +1,11 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="197" height="20" viewBox="0 0 197 20" fill="none">
+<path d="M174.643 20.0078C175.163 18.5296 175.57 17.0877 175.863 15.682C176.158 14.2617 176.402 12.6386 176.594 10.8126L177.656 0.704468H184.476L182.447 20.0078H179.933L180.509 14.5298H178.607C178.269 16.5008 177.837 18.3268 177.31 20.0078H174.643ZM180.79 11.8561L181.089 9.00839H179.297L179.107 10.8126L178.998 11.8561H180.79ZM181.37 6.33462L181.669 3.48693H179.877L179.578 6.33462H181.37ZM185.802 0.769682H196.337L194.887 7.70411H189.532L189.8 5.16076H192.641L192.97 3.48693H188.249L187.7 8.70406H196.159C195.263 11.2691 194.142 13.6169 192.796 15.7472L194.971 20.0078H192.239L191.206 17.9862C190.616 18.7398 190.036 19.4137 189.463 20.0078H183.78L185.802 0.769682ZM186.547 19.6818C187.703 18.5224 188.772 17.2978 189.752 16.008L187.499 11.2474H187.433L186.547 19.6818ZM191.354 13.6603C191.827 12.9067 192.268 12.1024 192.679 11.2474H190.275L191.354 13.6603Z" fill="#1D2129"/>
+<path d="M158.355 17.5504H168.279L168.498 15.4635H158.575L158.355 17.5504ZM154.188 11.029C156.893 10.6667 159.162 10.2174 160.997 9.68123C160.222 9.14503 159.534 8.5581 158.93 7.92045C157.753 8.58708 156.52 9.0146 155.233 9.20299L155.479 6.85529C157.095 6.31908 158.376 5.5655 159.324 4.59453H171.214L171.324 3.55111H158.253L158.095 5.05103H155.254L155.686 0.942548H163.62L163.718 0.0078125H166.603L166.505 0.942548H174.44L174.008 5.05103H172.237C170.961 6.935 169.312 8.47839 167.292 9.68123C169.013 10.2174 171.188 10.6667 173.817 11.029L173.549 13.5723C172.744 13.4709 172.084 13.3767 171.568 13.2897L170.878 19.8546H155.272L155.962 13.2897C155.428 13.3767 154.748 13.4709 153.921 13.5723L154.188 11.029ZM170.858 13.1811C168.182 12.7173 165.88 12.0869 163.953 11.2898C161.902 12.0869 159.483 12.7173 156.694 13.1811H170.858ZM161.884 6.85529C162.618 7.49294 163.414 8.03639 164.27 8.48564C165.22 8.03639 166.13 7.49294 166.998 6.85529H161.884Z" fill="#1D2129"/>
+<path d="M149.101 19.9198H133.319L134.359 10.029H150.14L149.101 19.9198ZM132.841 5.96403H137.628L136.752 3.89892H134.151L134.436 1.18167H141.737L141.86 0.0078125H144.745L144.622 1.18167H151.923L151.637 3.89892H149.014L147.704 5.96403H152.513L152.227 8.68128H132.555L132.841 5.96403ZM146.551 17.3548L146.678 16.1374H136.536L136.409 17.3548H146.551ZM146.921 13.8332L147.051 12.5941H136.909L136.779 13.8332H146.921ZM140.688 5.96403H144.644L145.954 3.89892H139.812L140.688 5.96403Z" fill="#1D2129"/>
+<path d="M128.734 19.8763H116.887L117.687 12.268H129.534L128.734 19.8763ZM110.585 6.63784H115.393L114.452 15.5939L116.232 14.8765L115.892 18.1155L111.147 20.0067L112.248 9.529H110.281L110.585 6.63784ZM115.28 0.442505L116.292 5.15965H113.319L112.307 0.442505H115.28ZM117.28 8.4421H119.816L120.477 6.72479H118.291L118.524 4.50751H121.3L121.845 3.0728H118.282L118.535 0.659885H131.104L130.85 3.0728H124.686L124.142 4.50751H129.563L129.149 8.4421H130.723L130.465 10.8985H117.022L117.28 8.4421ZM126.173 17.4199L126.454 14.7461H120.224L119.943 17.4199H126.173ZM126.482 8.4421L126.663 6.72479H123.319L122.657 8.4421H126.482Z" fill="#1D2129"/>
+<path d="M89.3249 8.22433H98.8549L97.6166 20.0063H94.8625L95.0773 17.963H91.0554L90.8407 20.0063H88.0866L89.3249 8.22433ZM89.2716 4.57234H90.2989L93.1028 0.355164H96.4033L93.5994 4.57234H96.4191L95.9582 2.0942H98.7342L99.664 7.1809H88.9974L89.2716 4.57234ZM95.3217 15.637L95.4542 14.3762H91.4324L91.2999 15.637H95.3217ZM95.6987 12.0502L95.8381 10.7242H91.8162L91.6769 12.0502H95.6987ZM100.087 10.2242H102.907L102.637 12.7893C104.378 12.3111 106.191 11.6299 108.076 10.7459L107.775 13.6154C106.056 14.4414 104.248 15.0718 102.352 15.5066L102.192 17.0282H107.634L107.34 19.8324H99.0775L100.087 10.2242ZM101.115 0.442116H103.935L103.656 3.09415C105.35 2.6449 107.16 1.99276 109.086 1.13773L108.794 3.9202C107.105 4.73175 105.299 5.34766 103.375 5.76793L103.257 6.89831H108.699L108.411 9.6373H100.149L101.115 0.442116Z" fill="#1D2129"/>
+<path d="M85.3545 10.8558L84.4109 19.8336H66.881L67.8246 10.8558H85.3545ZM69.0994 0.182434H71.8972L71.5112 0.943265H77.1724L76.9256 3.29097H73.8437L73.7363 4.31266H77.2773L77.0259 6.70384H73.7691C74.1989 7.05165 74.6646 7.33424 75.1664 7.55162C75.6681 7.769 76.2384 7.9574 76.8774 8.11681L76.6078 10.6819C75.5378 10.4645 74.6349 10.1819 73.8991 9.83411C73.1795 9.47181 72.5283 8.94286 71.9457 8.24724C71.2023 8.94286 70.4327 9.47181 69.6369 9.83411C68.8572 10.1819 67.9021 10.4645 66.7718 10.6819L67.0414 8.11681C67.7139 7.9574 68.3239 7.769 68.8713 7.55162C69.4188 7.33424 69.9439 7.05165 70.4467 6.70384H67.1899L67.4413 4.31266H70.9385L71.0459 3.29097H67.5268L69.0994 0.182434ZM81.8184 17.4642L81.9304 16.399H70.0835L69.9715 17.4642H81.8184ZM82.152 14.2904L82.264 13.2252H70.4171L70.3051 14.2904H82.152ZM78.5608 0.834574H86.9324L85.9728 9.96454H77.6013L78.5608 0.834574ZM83.5261 7.24729L83.9146 3.55183H81.0075L80.6191 7.24729H83.5261Z" fill="#1D2129"/>
+<path d="M50.3767 16.3117H53.3494L53.9952 19.9419C54.5801 18.5362 55.0235 17.1595 55.3254 15.8117C55.642 14.464 55.8916 12.9206 56.0744 11.1815L57.1574 0.877689C60.028 0.877689 62.8019 0.689293 65.479 0.3125L65.1865 3.09497C63.514 3.34133 61.6451 3.51524 59.5798 3.61668L59.308 6.2035H65.3626L65.0701 8.98597H63.6056L62.4473 20.0071H59.7369L60.8953 8.98597H59.0155L58.7847 11.1815C58.4588 14.2828 57.8217 17.2247 56.8735 20.0071H51.0375L50.3767 16.3117ZM44.444 13.0727H45.7773L46.7392 3.92101H45.6245L45.8941 1.35593H47.0088L47.1162 0.334238H49.8266L49.7192 1.35593H52.5825L52.6899 0.334238H55.4003L55.2929 1.35593H56.4076L56.138 3.92101H55.0233L54.0614 13.0727H55.3073L55.0331 15.6813H44.1698L44.444 13.0727ZM45.4588 16.3117H48.4314L46.9938 20.0071H44.0212L45.4588 16.3117ZM51.3511 13.0727L51.5087 11.5728H48.6453L48.4877 13.0727H51.3511ZM51.7509 9.26856L51.9131 7.72517H49.0497L48.8875 9.26856H51.7509ZM52.1553 5.42093L52.3129 3.92101H49.4496L49.2919 5.42093H52.1553Z" fill="#1D2129"/>
+<path d="M39.1591 18.8546C37.9598 15.0143 37.0285 10.5652 36.3652 5.5075H34.8352L33.3113 20.0068H30.5135L32.0374 5.5075H30.5074C28.7809 10.5652 26.9144 15.0143 24.9078 18.8546H21.8696C24.158 14.5505 26.0901 10.1015 27.6658 5.5075H24.103L24.4001 2.68156H32.3344L32.5926 0.225159H35.3904L35.1322 2.68156H43.0666L42.7696 5.5075H39.2067C39.8168 10.1015 40.8136 14.5505 42.1973 18.8546H39.1591Z" fill="#1D2129"/>
+<path d="M1.2839 7.78995C3.07786 5.83353 4.43545 3.38438 5.35667 0.442505H8.11075C7.55931 2.50037 6.85085 4.31912 5.98538 5.89875L4.50257 20.0067H1.66106L2.66407 10.4637C2.1736 10.971 1.56199 11.5216 0.829237 12.1158L1.2839 7.78995ZM6.31463 17.1156H11.8884L12.158 14.5505H7.32739L7.61527 11.8115H12.4458L12.6629 9.74638H7.22031L7.50819 7.00739H12.9508L13.1975 4.65968H8.36696L8.65484 1.92069H13.4854L13.6408 0.442505H16.4823L16.3269 1.92069H21.1575L20.8696 4.65968H16.039L15.7923 7.00739H21.2349L20.947 9.74638H15.5044L15.2873 11.8115H20.1179L19.83 14.5505H14.9995L14.7299 17.1156H20.3036L20.0157 19.8546H6.02675L6.31463 17.1156Z" fill="#1D2129"/>
+</svg>

BIN
src/assets/images/logo/logo.png


+ 3 - 0
src/assets/images/tools/icon-avatar-call.svg

@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="34" height="38" viewBox="0 0 34 38" fill="none">
+  <path fill-rule="evenodd" clip-rule="evenodd" d="M17.0345 38C23.661 38 29.5971 35.1462 33.7386 30.5257C34.0147 30.118 34.1528 29.4385 33.7386 29.0308C30.7015 24.5463 26.2839 21.8284 21.4522 20.8771C25.7317 18.9745 28.6308 14.49 27.9405 9.32591C27.2503 4.56954 23.2468 0.628548 18.277 0.0849633C11.6506 -0.730414 5.99053 4.43364 5.99053 10.8208C5.99053 15.3053 8.75154 19.1104 12.4789 20.7412C7.64714 21.8284 3.22953 24.5463 0.192425 28.895C-0.0836756 29.4385 -0.083675 30.118 0.330475 30.5257C4.47198 35.1462 10.4081 38 17.0345 38Z" fill="#F8F8F8"/>
+</svg>

+ 4 - 0
src/assets/images/tools/icon-phone-off.svg

@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40" fill="none">
+  <rect x="-0.000488281" width="40" height="40" rx="20" fill="#34C759"/>
+  <path d="M15.774 24.037C18.4357 26.6987 21.6683 28.7525 24.3115 28.7525C25.4996 28.7525 26.5403 28.338 27.3784 27.417C27.8665 26.8737 28.1705 26.2382 28.1705 25.6119C28.1705 25.1514 27.9955 24.7094 27.5626 24.3962L24.7352 22.3885C24.3023 22.0938 23.9432 21.9464 23.6116 21.9464C23.1972 21.9464 22.8196 22.1859 22.3959 22.6003L21.742 23.245C21.6407 23.3463 21.5118 23.3924 21.392 23.3924C21.2539 23.3924 21.1157 23.3371 21.0236 23.291C20.4526 22.9871 19.4764 22.149 18.5646 21.2465C17.6621 20.3439 16.824 19.3677 16.5293 18.7967C16.4832 18.6954 16.4279 18.5664 16.4279 18.4283C16.4279 18.3085 16.4648 18.1888 16.5661 18.0875L17.22 17.4152C17.6252 16.9915 17.8739 16.6231 17.8739 16.1995C17.8739 15.8679 17.7173 15.5088 17.4134 15.0759L15.4333 12.2853C15.1109 11.8432 14.6597 11.6498 14.1623 11.6498C13.5545 11.6498 12.9282 11.9261 12.3848 12.4511C11.4915 13.3076 11.0955 14.3667 11.0955 15.5364C11.0955 18.1796 13.1124 21.3846 15.774 24.037Z" fill="white"/>
+</svg>

+ 4 - 0
src/assets/images/tools/icon-phone-on.svg

@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40" fill="none">
+<rect width="40" height="40" rx="20" fill="#DB4445"/>
+<path d="M19.7016 16.0879C15.7137 16.0879 11.9653 16.926 10.0957 18.7956C9.24843 19.6429 8.82478 20.6652 8.88004 21.8993C8.91688 22.6453 9.14712 23.3084 9.57998 23.7412C9.90233 24.0728 10.3536 24.257 10.8786 24.1741L14.2954 23.5939C14.8112 23.511 15.1703 23.3544 15.4006 23.115C15.7045 22.8203 15.7966 22.3782 15.7966 21.798V20.8678C15.7966 20.7204 15.8611 20.6099 15.9532 20.5178C16.0453 20.4073 16.1834 20.3612 16.2847 20.3336C16.911 20.1863 18.182 20.0481 19.7016 20.0481C21.2212 20.0481 22.4829 20.1586 23.1184 20.3428C23.2105 20.3705 23.3395 20.4257 23.4408 20.5178C23.5236 20.6099 23.5789 20.7112 23.5881 20.8586L23.5973 21.798C23.6065 22.3782 23.6986 22.8203 23.9933 23.115C24.2328 23.3544 24.592 23.511 25.1077 23.5939L28.4785 24.1649C29.0219 24.257 29.4824 24.0636 29.8416 23.7136C30.2744 23.29 30.5139 22.6361 30.5323 21.8901C30.5599 20.6467 30.081 19.6245 29.2522 18.7956C27.3826 16.926 23.6894 16.0879 19.7016 16.0879Z" fill="white"/>
+</svg>

+ 4 - 0
src/assets/images/tools/icon-toggle.svg

@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
+  <path d="M7.99951 20H2.99951C2.44723 20 1.99951 19.5523 1.99951 19V5C1.99951 4.44771 2.44723 4 2.99951 4H20.9995C21.5518 4 21.9995 4.44771 21.9995 5V8" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+  <path d="M20.9995 12H12.9995C12.4472 12 11.9995 12.4477 11.9995 13V19C11.9995 19.5523 12.4472 20 12.9995 20H20.9995C21.5518 20 21.9995 19.5523 21.9995 19V13C21.9995 12.4477 21.5518 12 20.9995 12Z" stroke="white" stroke-width="2" stroke-linejoin="round"/>
+</svg>

+ 2 - 1
src/assets/styles/index.scss

@@ -5,6 +5,7 @@
 @import './sidebar.scss';
 @import './btn.scss';
 @import './ruoyi.scss';
+// @import './reset.scss';
 
 body {
   height: 100%;
@@ -181,4 +182,4 @@ aside {
     vertical-align: middle;
     margin-bottom: 10px;
   }
-}
+}

+ 139 - 0
src/assets/styles/reset.scss

@@ -0,0 +1,139 @@
+body {
+  height: 100%;
+  -moz-osx-font-smoothing: grayscale;
+  -webkit-font-smoothing: antialiased;
+  text-rendering: optimizeLegibility;
+  font-family: PingFang SC, Noto Sans SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;
+  overflow-x: hidden;
+}
+
+/* Helvetica Neue, Helvetica, */
+label {
+  font-weight: 700;
+}
+
+html {
+  /* height: 100%; */
+  box-sizing: border-box;
+}
+
+#app {
+  height: 100%;
+}
+
+*,
+*:before,
+*:after {
+  box-sizing: inherit;
+}
+
+body,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+hr,
+p,
+blockquote,
+dl,
+dt,
+dd,
+ul,
+ol,
+li,
+pre,
+form,
+fieldset,
+legend,
+button,
+input,
+textarea,
+th,
+td {
+  margin: 0;
+  padding: 0;
+}
+
+
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+  font-size: 100%;
+}
+
+address,
+cite,
+dfn,
+em,
+var {
+  font-style: normal;
+}
+
+ul,
+ol {
+  list-style: none;
+}
+
+a {
+  text-decoration: none;
+}
+
+a:hover {
+  text-decoration: underline;
+}
+
+sup {
+  vertical-align: text-top;
+}
+
+sub {
+  vertical-align: text-bottom;
+}
+
+legend {
+  color: #000;
+}
+
+fieldset,
+img {
+  border: 0;
+}
+
+i {
+  font-style: normal;
+}
+
+table {
+  border-collapse: collapse;
+  border-spacing: 0;
+}
+
+video:focus {
+  outline: none;
+}
+
+::-webkit-scrollbar {
+  width: 5px;
+}
+
+::-webkit-scrollbar-track {
+  background-color: #f1f1f1;
+}
+
+::-webkit-scrollbar-thumb {
+  background-color: rgba(0, 0, 0, .25);
+  border-radius: 5px;
+}
+
+::-webkit-scrollbar-thumb:hover {
+  background-color: rgba(0, 0, 0, 0.4);
+}
+
+::-webkit-scrollbar-button {
+  display: none;
+}

+ 5 - 5
src/assets/styles/sidebar.scss

@@ -8,7 +8,7 @@
   .main-container {
     height: 100%;
     transition: margin-left .28s;
-    margin-left: $base-sidebar-width;
+    // margin-left: $base-sidebar-width;
     position: relative;
   }
 
@@ -22,11 +22,11 @@
     width: $base-sidebar-width !important;
     background-color: $base-menu-background;
     height: 100%;
-    position: fixed;
+    // position: fixed;
     font-size: 0px;
-    top: 0;
-    bottom: 0;
-    left: 0;
+    // top: 0;
+    // bottom: 0;
+    // left: 0;
     z-index: 1001;
     overflow: hidden;
     // -webkit-box-shadow: 2px 0 6px rgba(0,21,41,.35);

+ 3 - 0
src/assets/styles/tailwind.css

@@ -0,0 +1,3 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;

+ 166 - 0
src/layout/components/HeaderGroup/TelCallBoard.vue

@@ -0,0 +1,166 @@
+<script setup>
+import iconCallOn from '@/assets/images/header/icon-call-on.svg';
+import iconCallOff from '@/assets/images/header/icon-call-off.svg';
+
+const popoverVisible = ref(false);
+const popoverNums = ref(null);
+
+const isPlay = ref(false);
+
+const num = ref(null);
+const boardNums = [1, 2, 3, 4, 5, 6, 7, 8, 9,'*', 0, '#']
+
+// 计算属性 - 切换状态图标
+const phoneIcon = computed(() => isPlay.value ? iconCallOff : iconCallOn);
+
+const handleMakingCall = () => {
+  isPlay.value = !isPlay.value;
+  popoverVisible.value = false;
+}
+
+// 事件 - 输入电话号码
+const handleInpNumber = (num) => {
+  popoverNums.value = (popoverNums.value || '')  + num;
+}
+
+// 时间 - 清除popover中的电话最后一位
+const handleCleanNums = () => {
+  popoverNums.value = popoverNums.value.substring(0, popoverNums.value.length - 1);
+}
+
+</script>
+
+<template>
+  <div class="phone-inp-wrapper">
+    <div class="inp-left">
+      <el-input-number v-model="num" :step="1" class="reset-inp-number" placeholder="请输入外呼电话号码" step-strictly />
+    </div>
+    <div class="inp-right flex items-center">
+      <el-popover
+        trigger="click"
+        placement="bottom"
+        v-model:visible="popoverVisible"
+        :width="270"
+        :show-arrow="false"
+        :popper-style="{
+          height: '394px',
+          borderRadius: '16px',
+          padding: '22px 46px 26px 46px'
+        }"
+      >
+        <template #reference>
+          <div class="pointer">
+            <img src="@/assets/images/header/icon-tel-keyboard.svg" alt="">
+          </div>
+        </template>
+
+        <div class="h-full flex flex-col items-center justify-between">
+          <div class="flex items-center text-[#E0E0E0] text-[12px] text-center font-bold">
+            <el-input
+              v-model="popoverNums"
+              class="reset-inp-text"
+              placeholder="请点击外呼电话号码"
+              readonly
+            ></el-input>
+            <img src="@/assets/images/header/icon-clean-btn.svg" class="cursor-pointer" @click="handleCleanNums" v-show="popoverNums?.length">
+          </div>  
+          <ul class="popover-board-nums">
+            <li class="board-item" v-for="item in boardNums" @click="handleInpNumber(item)">{{ item }}</li>
+          </ul>
+          <div class="flex justify-center">
+            <img src="@/assets/images/header/icon-call-on.svg" alt=""  class="w-[50px] h-[50px] cursor-pointer" @click="handleMakingCall">
+          </div>
+        </div>
+
+      </el-popover>
+
+      <div class="line"></div>
+
+      <div class="pointer">
+        <img src="@/assets/images/header/icon-call-on.svg" alt=""></img>
+      </div>
+    </div>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.phone-inp-wrapper {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  width: 218px;
+  height: 100%;
+  padding: 8px;
+  border-radius: 8px;
+  background: #fff;
+  
+  .inp-right {
+    .line {
+      width: 1px;
+      height: 16px;
+      margin: 0 8px;
+      background: #EEEFF1;
+    }
+  }
+}
+
+.reset-inp-number {
+  width: 126px;
+  height: 100%;
+  line-height: 20px;
+  font-size: 14px;
+  outline: none;
+
+  :deep(.el-input-number__decrease),
+  :deep(.el-input-number__increase) {
+    display: none;
+  }
+
+  :deep(.el-input__wrapper) {
+    padding: 0;
+    box-shadow: none;
+  }
+}
+
+:deep(.reset-inp-text) {
+  .is-focus {
+    box-shadow: none;
+  }
+  .el-input__wrapper {
+    font-size: 16px;
+    list-style: normal;
+    box-shadow: none !important;
+  }
+  .el-input__inner::-webkit-input-placeholder {
+    font-size: 14px !important;
+  }
+}
+
+.popover-board-nums {
+  display: grid;
+  grid-row-gap: 15px;
+  grid-column-gap: 10px;
+  grid-template-columns: auto auto auto;
+  .board-item {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    width: 50px;
+    height: 50px;
+    border-radius: 100%;
+    background: #E5E6EB;
+    font-size: 24px;
+    color: #1D2129;
+    cursor: pointer;
+    transition: all 0.3s;
+
+    &:nth-child(10) {
+      padding-top: 10px;
+    }
+    
+    &:active {
+      background: #cfcfd0;
+    }
+  }
+}
+</style>

+ 185 - 0
src/layout/components/HeaderGroup/index.vue

@@ -0,0 +1,185 @@
+<script setup>
+import { ElMessageBox } from 'element-plus';
+import TelCallBoard from './TelCallBoard.vue'
+import useUserStore from '@/store/modules/user';
+const { proxy } = getCurrentInstance();
+const systemState = ref(false);
+const userStore = useUserStore();
+const SYSTEM_STAT_ENUM = [
+  {label: '置闲', state: true, icon: 'online-icon'},
+  {label: '置忙', state: false, icon: 'offline-icon'}
+];
+
+const num = ref(null);
+
+// 修改在线状态
+const handlePopoverItem = ({ state, label }) => {
+  systemState.value = state;
+  proxy.$modal[state ? 'msgSuccess' : 'msgWarning']('当前系统状态: ' + label);
+}
+
+// 
+const handleCommand = (command) => {
+  switch (command) {
+    case "logout":
+      logout();
+      break;
+    default:
+      break;
+  }
+}
+
+const logout = () => {
+  ElMessageBox.confirm('确定注销并退出系统吗?', '提示', {
+    confirmButtonText: '确定',
+    cancelButtonText: '取消',
+    type: 'warning'
+  }).then(() => {
+    userStore.logOut().then(() => {
+      location.href = '/index';
+    })
+  }).catch(() => { });
+}
+</script>
+
+<template>
+  <header class="header-container">
+    <div class="navbar-left flex items-center space-x-[8px]">
+      <img src="@/assets/images/logo/logo.png" alt="" class="w-[28px] h-[28px]">
+      <img src="@/assets/images/logo/logo-text.svg" alt="" class="w-[130px] h-[20px]">
+    </div>
+
+    <div class="navbar-right flex items-center space-x-[12px]">
+      <el-dropdown trigger="click" popper-class="custom-dropdown" size="small">
+        <div class="system-state-wrapper">
+          <div class="system-state-btn">
+            <p class="flex items-center space-x-[4px]">
+              <span :class="['icon', systemState ? 'online-icon' : 'offline-icon']"></span>
+              <span>{{ systemState ? '置闲' : '置忙' }}</span>
+            </p>
+            <el-icon size="10"><ArrowDown /></el-icon>
+          </div>
+        </div>
+        <template #dropdown>
+          <el-dropdown-menu>
+            <el-dropdown-item v-for="item in SYSTEM_STAT_ENUM" :key="item.label" @click="handlePopoverItem(item)">
+              <div class="system-state-btn">
+                <p class="flex items-center space-x-[4px]">
+                  <span :class="['icon', item.icon]"></span>
+                  <span>{{ item.label }}</span>
+                </p>
+              </div> 
+            </el-dropdown-item>
+          </el-dropdown-menu>
+        </template>
+      </el-dropdown>
+      
+      <TelCallBoard />
+
+
+      <div class="avatar-wrapper flex items-center space-x-[4px]">
+        <div class="avatar-img">
+          <img :src="userStore.avatar" alt="" class="img">
+        </div>
+        <el-dropdown @command="handleCommand" class="right-menu-item hover-effect" trigger="click" size="small">
+          <div class="h-[36px] flex items-center space-x-[4px]">
+            <div class="user-name">{{ userStore.name }}</div>
+            <el-icon><caret-bottom /></el-icon>
+          </div>
+          <template #dropdown>
+            <el-dropdown-menu>
+              <!-- <router-link to="/user/profile" >
+                <el-dropdown-item>
+                  <span >个人中心</span>
+                </el-dropdown-item>
+              </router-link> -->
+              <el-dropdown-item command="logout">
+                <span class="text-[#1D2129] text-[14px] py-[4px]">退出登录</span>
+              </el-dropdown-item>
+            </el-dropdown-menu>
+          </template>
+        </el-dropdown>
+      </div>
+    </div>
+  </header>
+</template>
+<style lang="scss">
+.custom-dropdown {
+  width: 86px;
+  .el-dropdown-menu__item {
+    padding: 8px !important;
+  }
+}
+</style>
+<style lang="scss" scoped>
+.header-container {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  height: 92px;
+  padding: 34px 20px 22px 20px;
+  
+  .navbar-left, .navbar-right {
+    height: 100%;
+  }
+
+  .avatar-wrapper {
+    font-size: 14px;
+    color: #1D2129;
+    font-weight: bold;
+    .avatar-img {
+      flex-shrink: 0;
+      width: 36px;
+      height: 36px;
+      border-radius: 50%;
+      background: #fff;
+      overflow: hidden;
+      .img {
+        width: 100%;
+        height: 100%;
+        border-radius: 100%;
+      }
+    }
+    .user-name {
+      max-width: 100px;
+      overflow:hidden;
+      white-space: nowrap;
+      text-overflow: ellipsis;
+      -o-text-overflow:ellipsis;
+    }
+  }
+}
+
+.system-state-wrapper {
+    width: 86px;
+    height: 36px;
+    padding: 9px 8px;
+    border-radius: 8px;
+    overflow: hidden;
+    background: #fff;
+  }
+
+.system-state-btn {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  line-height: 18px;
+  text-align: center;
+  font-size: 14px;
+  color: #1D2129;
+  cursor: pointer;
+
+  .icon {
+    display: block;
+    width: 18px;
+    height: 18px;
+  }
+
+  .online-icon {
+    background: url('@/assets/images/header/icon-phone-online.svg') center center no-repeat;
+  }
+  .offline-icon {
+    background: url('@/assets/images/header/icon-phone-offline.svg') center center no-repeat;
+  }
+}
+</style>

+ 1 - 1
src/layout/components/Sidebar/index.vue

@@ -34,7 +34,7 @@ const handleSelect = (key) => {
 
 </script>
 <template>
-  <div :style="{ backgroundColor: '#EFF1F8' }">
+  <div :style="{ backgroundColor: '#EFF1F8', paddingTop: '20px'}">
     <el-scrollbar :class="theme" wrap-class="scrollbar-wrapper">
       <div style="padding: 0 20px;">
         <el-menu

+ 149 - 0
src/layout/components/TelNoticeBar/index.vue

@@ -0,0 +1,149 @@
+<script setup>
+const isExists = ref(false);
+</script>
+
+<template>
+  <div class="tel-notice-bar_view flex items-center justify-between" :style="{ top: isExists ? '10px' : '-88px' }">
+    <div class="notice-left flex items-center justify-center">
+      <div class="avatar-box">
+        <div class="circle1"></div>
+        <div class="circle2"></div>
+        <div class="circle3"></div>
+        <div class="circle4">
+          <img src="@/assets/images/tools/icon-avatar-call.svg" alt="">
+        </div>
+      </div>
+
+      <ul class="pl-[20px]">
+        <li class="text-[14px] text-[#7B7B7D] space-x-[4px]">
+          <span>黑龙江</span>
+          <span>牡丹江</span>
+        </li>
+        <li class="text-[18px] font-bold">
+          <span>18645683435</span>
+        </li>
+      </ul>
+    </div>
+
+    <div class="notice-right flex">
+      <ul class="flex items-center space-x-[12px]">
+        <li class="icon-off"></li>
+        <li class="icon-on"></li>
+        <li class="icon-toggle"></li>
+      </ul>
+    </div>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.tel-notice-bar_view {
+  position: absolute;
+  left: 50%;
+  top: 10px;
+  transform: translateX(-50%);
+  display: flex;
+  align-items: center;
+  width: 484px;
+  height: 88px;
+  padding: 0 24px;
+  background: #000;
+  z-index: 100;
+  border-radius: 8px;
+  color: #fff;
+  transition: top ease-out .5s;
+
+  .notice-left .avatar-box {
+    position: relative;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    width: 48px;
+    height: 48px;
+    border-radius: 100% 100%;
+    background: linear-gradient(180deg, #656565 0%, #2F2F2F 100%);
+    
+    .circle1,
+    .circle2,
+    .circle3 {
+      position: absolute;
+      width: 30px;
+      height: 30px;
+      background: rgba(137, 142, 254, 0);
+      border: 1px solid rgba(255, 255, 255, 0.60);
+      border-radius: 999px;
+    }
+
+    .circle1,
+    .circle2,
+    .circle3 {
+      animation-name: circleChange;
+      animation-duration: 3s;
+      animation-iteration-count: infinite;
+      animation-timing-function: linear;
+    }
+
+    .circle1 {
+      animation-delay: 0.5s;
+    }
+
+    .circle2 {
+      animation-delay: 1.5s;
+    }
+
+    .circle3 {
+      animation-delay: 2.5s;
+    }
+
+    @keyframes circleChange {
+      0% {
+        transform: scale(1.5);
+        opacity: 0.75;
+      }
+
+      100% {
+        transform: scale(2.5);
+        opacity: 0.05;
+      }
+    }
+
+    .circle4 {
+      position: relative;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      z-index: 11111;
+      width: 100%;
+      height: 100%;
+      border-radius: 100% 100%;
+      background: linear-gradient(180deg, #656565 0%, #2F2F2F 100%);
+    }
+  }
+
+  .notice-right {
+
+    .icon-off,
+    .icon-on {
+      width: 40px;
+      height: 40px;
+      border-radius: 50%;
+      cursor: pointer;
+    }
+
+    .icon-off {
+      background: url("@/assets/images/tools/icon-phone-on.svg") center center no-repeat;
+    }
+
+    .icon-on {
+      background: url("@/assets/images/tools/icon-phone-off.svg") center center no-repeat;
+    }
+
+    .icon-toggle {
+      width: 24px;
+      height: 24px;
+      background: url("@/assets/images/tools/icon-toggle.svg") center center no-repeat;
+      cursor: pointer;
+    }
+  }
+}
+
+</style>

+ 114 - 0
src/layout/index copy.vue

@@ -0,0 +1,114 @@
+<template>
+  <div :class="classObj" class="app-wrapper" :style="{ '--current-color': theme }">
+    <div v-if="device === 'mobile' && sidebar.opened" class="drawer-bg" @click="handleClickOutside"/>
+    <sidebar v-if="!sidebar.hide" class="sidebar-container" />
+    <div :class="{ hasTagsView: needTagsView, sidebarHide: sidebar.hide }" class="main-container">
+      <div :class="{ 'fixed-header': fixedHeader }">
+        <navbar @setLayout="setLayout" />
+        <tags-view v-if="needTagsView" />
+      </div>
+      <app-main />
+      <settings ref="settingRef" />
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { useWindowSize } from '@vueuse/core'
+import Sidebar from './components/Sidebar/index.vue'
+import { AppMain, Navbar, Settings, TagsView } from './components'
+import defaultSettings from '@/settings'
+
+import useAppStore from '@/store/modules/app'
+import useSettingsStore from '@/store/modules/settings'
+
+const settingsStore = useSettingsStore()
+const theme = computed(() => settingsStore.theme);
+const sideTheme = computed(() => settingsStore.sideTheme);
+const sidebar = computed(() => useAppStore().sidebar);
+const device = computed(() => useAppStore().device);
+const needTagsView = computed(() => settingsStore.tagsView);
+const fixedHeader = computed(() => settingsStore.fixedHeader);
+
+const classObj = computed(() => ({
+  // hideSidebar: !sidebar.value.opened,
+  // openSidebar: sidebar.value.opened,
+  withoutAnimation: sidebar.value.withoutAnimation,
+  mobile: device.value === 'mobile'
+}))
+
+const { width, height } = useWindowSize();
+const WIDTH = 992; // refer to Bootstrap's responsive design
+
+watch(() => device.value, () => {
+  if (device.value === 'mobile' && sidebar.value.opened) {
+    useAppStore().closeSideBar({ withoutAnimation: false })
+  }
+})
+
+watchEffect(() => {
+  if (width.value - 1 < WIDTH) {
+    useAppStore().toggleDevice('mobile')
+    useAppStore().closeSideBar({ withoutAnimation: true })
+  } else {
+    useAppStore().toggleDevice('desktop')
+  }
+})
+
+function handleClickOutside() {
+  useAppStore().closeSideBar({ withoutAnimation: false })
+}
+
+const settingRef = ref(null);
+function setLayout() {
+  settingRef.value.openSetting();
+}
+</script>
+
+<style lang="scss" scoped>
+  @import "@/assets/styles/mixin.scss";
+  @import "@/assets/styles/variables.module.scss";
+
+.app-wrapper {
+  @include clearfix;
+  position: relative;
+  height: 100%;
+  width: 100%;
+
+  &.mobile.openSidebar {
+    position: fixed;
+    top: 0;
+  }
+}
+
+.drawer-bg {
+  background: #000;
+  opacity: 0.3;
+  width: 100%;
+  top: 0;
+  height: 100%;
+  position: absolute;
+  z-index: 999;
+}
+
+.fixed-header {
+  position: fixed;
+  top: 0;
+  right: 0;
+  z-index: 9;
+  width: calc(100% - #{$base-sidebar-width});
+  transition: width 0.28s;
+}
+
+.hideSidebar .fixed-header {
+  width: calc(100% - 54px);
+}
+
+.sidebarHide .fixed-header {
+  width: 100%;
+}
+
+.mobile .fixed-header {
+  width: 100%;
+}
+</style>

+ 31 - 100
src/layout/index.vue

@@ -1,114 +1,45 @@
-<template>
-  <div :class="classObj" class="app-wrapper" :style="{ '--current-color': theme }">
-    <div v-if="device === 'mobile' && sidebar.opened" class="drawer-bg" @click="handleClickOutside"/>
-    <sidebar v-if="!sidebar.hide" class="sidebar-container" />
-    <div :class="{ hasTagsView: needTagsView, sidebarHide: sidebar.hide }" class="main-container">
-      <div :class="{ 'fixed-header': fixedHeader }">
-        <navbar @setLayout="setLayout" />
-        <tags-view v-if="needTagsView" />
-      </div>
-      <app-main />
-      <settings ref="settingRef" />
-    </div>
-  </div>
-</template>
-
 <script setup>
-import { useWindowSize } from '@vueuse/core'
-import Sidebar from './components/Sidebar/index.vue'
-import { AppMain, Navbar, Settings, TagsView } from './components'
-import defaultSettings from '@/settings'
-
-import useAppStore from '@/store/modules/app'
-import useSettingsStore from '@/store/modules/settings'
-
-const settingsStore = useSettingsStore()
-const theme = computed(() => settingsStore.theme);
-const sideTheme = computed(() => settingsStore.sideTheme);
-const sidebar = computed(() => useAppStore().sidebar);
-const device = computed(() => useAppStore().device);
-const needTagsView = computed(() => settingsStore.tagsView);
-const fixedHeader = computed(() => settingsStore.fixedHeader);
-
-const classObj = computed(() => ({
-  // hideSidebar: !sidebar.value.opened,
-  // openSidebar: sidebar.value.opened,
-  withoutAnimation: sidebar.value.withoutAnimation,
-  mobile: device.value === 'mobile'
-}))
+import HeaderGroup  from './components/HeaderGroup';
+import Sidebar from './components/Sidebar'
+import TelNoticeBar from './components/TelNoticeBar/index.vue'
+import { AppMain } from './components'
 
-const { width, height } = useWindowSize();
-const WIDTH = 992; // refer to Bootstrap's responsive design
 
-watch(() => device.value, () => {
-  if (device.value === 'mobile' && sidebar.value.opened) {
-    useAppStore().closeSideBar({ withoutAnimation: false })
-  }
-})
-
-watchEffect(() => {
-  if (width.value - 1 < WIDTH) {
-    useAppStore().toggleDevice('mobile')
-    useAppStore().closeSideBar({ withoutAnimation: true })
-  } else {
-    useAppStore().toggleDevice('desktop')
-  }
-})
-
-function handleClickOutside() {
-  useAppStore().closeSideBar({ withoutAnimation: false })
-}
-
-const settingRef = ref(null);
-function setLayout() {
-  settingRef.value.openSetting();
-}
 </script>
 
+<template>
+  <div class="app-wrapper">
+    <HeaderGroup></HeaderGroup>
+    <div class="main-wrapper">
+      <Sidebar class="sidebar-container" />
+      <AppMain class="main-container"/>
+    </div>
+    
+    <TelNoticeBar></TelNoticeBar>
+    
+  </div>
+</template>
+
 <style lang="scss" scoped>
-  @import "@/assets/styles/mixin.scss";
-  @import "@/assets/styles/variables.module.scss";
+@import "@/assets/styles/mixin.scss";
+@import "@/assets/styles/variables.module.scss";
 
 .app-wrapper {
   @include clearfix;
   position: relative;
-  height: 100%;
-  width: 100%;
-
-  &.mobile.openSidebar {
-    position: fixed;
-    top: 0;
+  height: 100vh;
+  width: 100vw;
+  background: #EFF1F8;
+  overflow: hidden;
+  
+  .main-wrapper {
+    display: flex;
+    height: calc(100vh - 92px);
+    
+    .sidebar-container {
+      flex-shrink: 0;
+    }
   }
 }
 
-.drawer-bg {
-  background: #000;
-  opacity: 0.3;
-  width: 100%;
-  top: 0;
-  height: 100%;
-  position: absolute;
-  z-index: 999;
-}
-
-.fixed-header {
-  position: fixed;
-  top: 0;
-  right: 0;
-  z-index: 9;
-  width: calc(100% - #{$base-sidebar-width});
-  transition: width 0.28s;
-}
-
-.hideSidebar .fixed-header {
-  width: calc(100% - 54px);
-}
-
-.sidebarHide .fixed-header {
-  width: 100%;
-}
-
-.mobile .fixed-header {
-  width: 100%;
-}
 </style>

+ 1 - 0
src/main.js

@@ -7,6 +7,7 @@ import 'element-plus/dist/index.css'
 import locale from 'element-plus/es/locale/lang/zh-cn'
 
 import '@/assets/styles/index.scss' // global css
+import '@/assets/styles/tailwind.css'
 
 import App from './App'
 import store from './store'

+ 13 - 13
src/router/index.js

@@ -57,19 +57,19 @@ export const constantRoutes = [
     component: () => import('@/views/error/401'),
     hidden: true
   },
-  // {
-  //   path: '',
-  //   component: Layout,
-  //   redirect: '/index',
-  //   children: [
-  //     {
-  //       path: '/index',
-  //       component: () => import('@/views/index'),
-  //       name: 'Index',
-  //       meta: { title: '首页', icon: 'dashboard', affix: true }
-  //     }
-  //   ]
-  // },
+  {
+    path: '',
+    // component: Layout,
+    redirect: '/workbench',
+    // children: [
+    //   {
+    //     path: '/index',
+    //     component: () => import('@/views/index'),
+    //     name: 'Index',
+    //     meta: { title: '首页', icon: 'dashboard', affix: true }
+    //   }
+    // ]
+  },
   {
     path: '/user',
     component: Layout,

+ 121 - 101
src/views/login.vue

@@ -1,66 +1,52 @@
 <template>
   <div class="login">
-    <el-form ref="loginRef" :model="loginForm" :rules="loginRules" class="login-form">
-      <h3 class="title">若依后台管理系统</h3>
-      <el-form-item prop="username">
-        <el-input
-          v-model="loginForm.username"
-          type="text"
-          size="large"
-          auto-complete="off"
-          placeholder="账号"
-        >
-          <template #prefix><svg-icon icon-class="user" class="el-input__icon input-icon" /></template>
-        </el-input>
-      </el-form-item>
-      <el-form-item prop="password">
-        <el-input
-          v-model="loginForm.password"
-          type="password"
-          size="large"
-          auto-complete="off"
-          placeholder="密码"
-          @keyup.enter="handleLogin"
-        >
-          <template #prefix><svg-icon icon-class="password" class="el-input__icon input-icon" /></template>
-        </el-input>
-      </el-form-item>
-      <el-form-item prop="code" v-if="captchaEnabled">
-        <el-input
-          v-model="loginForm.code"
-          size="large"
-          auto-complete="off"
-          placeholder="验证码"
-          style="width: 63%"
-          @keyup.enter="handleLogin"
-        >
-          <template #prefix><svg-icon icon-class="validCode" class="el-input__icon input-icon" /></template>
-        </el-input>
-        <div class="login-code">
-          <img :src="codeUrl" @click="getCode" class="login-code-img"/>
-        </div>
-      </el-form-item>
-      <el-checkbox v-model="loginForm.rememberMe" style="margin:0px 0px 25px 0px;">记住密码</el-checkbox>
-      <el-form-item style="width:100%;">
-        <el-button
-          :loading="loading"
-          size="large"
-          type="primary"
-          style="width:100%;"
-          @click.prevent="handleLogin"
-        >
-          <span v-if="!loading">登 录</span>
-          <span v-else>登 录 中...</span>
-        </el-button>
-        <div style="float: right;" v-if="register">
-          <router-link class="link-type" :to="'/register'">立即注册</router-link>
-        </div>
-      </el-form-item>
+    <el-form ref="loginRef" :model="loginForm" class="login-form">
+      <h3 class="title">账号登录</h3>
+      <div class="main">
+        <el-form-item prop="username">
+          <el-input
+            v-model="loginForm.username"
+            type="text"
+            size="large"
+            auto-complete="off"
+            placeholder="请输入登录账号"
+          ></el-input>
+        </el-form-item>
+        <el-form-item prop="password">
+          <el-input
+            v-model="loginForm.password"
+            type="password"
+            size="large"
+            auto-complete="off"
+            placeholder="请输入登录密码"
+            @keyup.enter="handleLogin"
+          ></el-input>
+        </el-form-item>
+        <el-form-item style="width:100%;">
+          <div class="w-full h-[116px]">
+            <div class="error-tips space-x-[4px]" v-show="errorMsg">
+              <el-icon><Warning /></el-icon>
+              <span>{{ errorMsg }}</span>
+            </div>
+          </div>
+          <el-button
+            :loading="loading"
+            size="large"
+            type="primary"
+            style="width:100%;"
+            @click.prevent="handleLogin"
+          >
+            <span v-if="!loading">登 录</span>
+            <span v-else>登 录 中...</span>
+          </el-button>
+        </el-form-item>
+      </div>
     </el-form>
-    <!--  底部  -->
+    <!--  底部  
     <div class="el-login-footer">
       <span>Copyright © 2018-2024 ruoyi.vip All Rights Reserved.</span>
     </div>
+    -->
   </div>
 </template>
 
@@ -89,6 +75,8 @@ const loginRules = {
   code: [{ required: true, trigger: "change", message: "请输入验证码" }]
 };
 
+const errorMsg = ref("");
+
 const codeUrl = ref("");
 const loading = ref(false);
 // 验证码开关
@@ -102,39 +90,35 @@ watch(route, (newRoute) => {
 }, { immediate: true });
 
 function handleLogin() {
-  proxy.$refs.loginRef.validate(valid => {
-    if (valid) {
-      loading.value = true;
-      // 勾选了需要记住密码设置在 cookie 中设置记住用户名和密码
-      if (loginForm.value.rememberMe) {
-        Cookies.set("username", loginForm.value.username, { expires: 30 });
-        Cookies.set("password", encrypt(loginForm.value.password), { expires: 30 });
-        Cookies.set("rememberMe", loginForm.value.rememberMe, { expires: 30 });
-      } else {
-        // 否则移除
-        Cookies.remove("username");
-        Cookies.remove("password");
-        Cookies.remove("rememberMe");
+
+  const { username, password } = unref(loginForm);
+
+  if (!username) {
+    return errorMsg.value = '请输入用户名称'
+  }
+  if (!password) {
+    return errorMsg.value = '请输入登录密码'
+  }
+  
+  errorMsg.value = '';
+
+  loading.value = true;
+  
+  // 调用action的登录方法
+  userStore.login(loginForm.value).then(() => {
+    const query = route.query;
+    const otherQueryParams = Object.keys(query).reduce((acc, cur) => {
+      if (cur !== "redirect") {
+        acc[cur] = query[cur];
       }
-      // 调用action的登录方法
-      userStore.login(loginForm.value).then(() => {
-        const query = route.query;
-        const otherQueryParams = Object.keys(query).reduce((acc, cur) => {
-          if (cur !== "redirect") {
-            acc[cur] = query[cur];
-          }
-          return acc;
-        }, {});
-        router.push({ path: redirect.value || "/", query: otherQueryParams });
-      }).catch(() => {
-        loading.value = false;
-        // 重新获取验证码
-        if (captchaEnabled.value) {
-          getCode();
-        }
-      });
-    }
+      return acc;
+    }, {});
+    // redirect.value
+    router.push({ path: "/workbench", query: otherQueryParams });
+  }).catch(() => {
+    loading.value = false;
   });
+
 }
 
 function getCode() {
@@ -165,27 +149,63 @@ getCookie();
 <style lang='scss' scoped>
 .login {
   display: flex;
-  justify-content: center;
+  justify-content: flex-end;
   align-items: center;
   height: 100%;
-  background-image: url("../assets/images/login-background.jpg");
+  background-image: url("../assets/images/login/bg-login-text.png");
   background-size: cover;
 }
-.title {
-  margin: 0px auto 30px auto;
-  text-align: center;
-  color: #707070;
-}
 
 .login-form {
-  border-radius: 6px;
-  background: #ffffff;
-  width: 400px;
-  padding: 25px 25px 5px 25px;
+  width: 440px;
+  margin-right: 100px;
+  border-radius: 12px;
+  border: 2px solid #FFF;
+  background: rgba(255, 255, 255, 0.8);
+  box-shadow: 0px 2px 80px 5px rgba(19, 29, 107, 0.06);
+
+  .title {
+    padding: 20px 0 20px 60px;
+    border-bottom: 1px solid #EDEFF2;
+    text-align: left;
+    font-size: 24px;
+    font-weight: bold;
+    color: #1D2129;
+  }
+  
+  .main {
+    padding: 60px 60px 66px 60px;
+
+    :deep(.el-input__wrapper) {
+      border: 1px solid #F2F4F9;
+      border-radius: 8px;
+      box-shadow: none;
+    }
+    
+    .error-tips {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      height: 32px;
+      border-radius: 8px;
+      background: #FFF1F0;
+      text-align: center;
+      font-size: 12px;
+      color: #FF7152;
+    }
+
+    .el-button {
+      height: 54px;
+      border-radius: 8px;
+      background: linear-gradient(91deg, #165DFF 0.41%, #00A6FF 97.92%);
+      font-size: 18px;
+    }
+  }
+
   .el-input {
-    height: 40px;
+    height: 54px;
     input {
-      height: 40px;
+      height: 54px;
     }
   }
   .input-icon {

+ 9 - 0
tailwind.config.js

@@ -0,0 +1,9 @@
+/** @type {import('tailwindcss').Config} */
+export default {
+  content: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],
+  theme: {
+    extend: {},
+  },
+  plugins: [],
+}
+

+ 2 - 0
vite.config.js

@@ -1,6 +1,7 @@
 import { defineConfig, loadEnv } from 'vite'
 import path from 'path'
 import createVitePlugins from './vite/plugins'
+import tailwindcss from 'tailwindcss'
 
 // https://vitejs.dev/config/
 export default defineConfig(({ mode, command }) => {
@@ -41,6 +42,7 @@ export default defineConfig(({ mode, command }) => {
     css: {
       postcss: {
         plugins: [
+          tailwindcss,
           {
             postcssPlugin: 'internal:charset-removal',
             AtRule: {