feat: login component
This commit is contained in:
42
bun.lock
42
bun.lock
@@ -25,6 +25,8 @@
|
||||
"vue-router": "^4.5.1",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@iconify-json/material-symbols": "^1.2.55",
|
||||
"@iconify/tailwind4": "^1.2.1",
|
||||
"@tailwindcss/vite": "^4.1.11",
|
||||
"@types/node": "^24.1.0",
|
||||
"@vitejs/plugin-vue": "^6.0.0",
|
||||
@@ -106,6 +108,8 @@
|
||||
|
||||
"@ctrl/tinycolor": ["@ctrl/tinycolor@3.6.1", "", {}, "sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA=="],
|
||||
|
||||
"@cyberalien/svg-utils": ["@cyberalien/svg-utils@1.1.4", "", { "dependencies": { "@iconify/types": "^2.0.0" } }, "sha512-LlBw+9nsQPMO1QNJMuJSgXGWK1/r4D0PohTK0Uq1ehyGE420Qxq4+LVYkA9W0xkrNOCVYvRegbSy5By4f30W7w=="],
|
||||
|
||||
"@docsearch/css": ["@docsearch/css@4.5.3", "", {}, "sha512-kUpHaxn0AgI3LQfyzTYkNUuaFY4uEz/Ym9/N/FvyDE+PzSgZsCyDH9jE49B6N6f1eLCm9Yp64J9wENd6vypdxA=="],
|
||||
|
||||
"@docsearch/js": ["@docsearch/js@4.5.3", "", {}, "sha512-rcBiUMCXbZLqrLIT6F6FDcrG/tyvM2WM0zum6NPbIiQNDQxbSgmNc+/bToS0rxBsXaxiU64esiWoS02WqrWLsg=="],
|
||||
@@ -200,10 +204,16 @@
|
||||
|
||||
"@iconify-json/logos": ["@iconify-json/logos@1.2.10", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-qxaXKJ6fu8jzTMPQdHtNxlfx6tBQ0jXRbHZIYy5Ilh8Lx9US9FsAdzZWUR8MXV8PnWTKGDFO4ZZee9VwerCyMA=="],
|
||||
|
||||
"@iconify-json/material-symbols": ["@iconify-json/material-symbols@1.2.55", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-9EHfqSpzXxQb5neA5/n5GcF15rccKG+oBJDwh/s8mmxfYswY1A4dKYPOWXCqrWMe/J3CmHb0tXFloscVarrqkQ=="],
|
||||
|
||||
"@iconify-json/simple-icons": ["@iconify-json/simple-icons@1.2.68", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-bQPl1zuZlX6AnofreA1v7J+hoPncrFMppqGboe/SH54jZO37meiBUGBqNOxEpc0HKfZGxJaVVJwZd4gdMYu3hw=="],
|
||||
|
||||
"@iconify-json/vscode-icons": ["@iconify-json/vscode-icons@1.2.40", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-Q7JIWAxENwmcRg4EGRY+u16gBwrAj6mWeuSmuyuPvNvoTJHh8Ss8qoeDhrFYNgtWqNkzH5hSf4b2T9XLK5MsrA=="],
|
||||
|
||||
"@iconify/tailwind4": ["@iconify/tailwind4@1.2.1", "", { "dependencies": { "@iconify/tools": "^5.0.2", "@iconify/types": "^2.0.0", "@iconify/utils": "^3.1.0" }, "peerDependencies": { "tailwindcss": ">= 4.0.0" } }, "sha512-Hd7k8y7uzT3hk8ltw0jGku0r0wA8sc3d2iMvVTYv/9tMxBb+frZtWZGD9hDMU3EYuE+lMn58wi2lS8R2ZbwFcQ=="],
|
||||
|
||||
"@iconify/tools": ["@iconify/tools@5.0.3", "", { "dependencies": { "@cyberalien/svg-utils": "^1.1.1", "@iconify/types": "^2.0.0", "@iconify/utils": "^3.1.0", "fflate": "^0.8.2", "modern-tar": "^0.7.3", "pathe": "^2.0.3", "svgo": "^4.0.0" } }, "sha512-W5nbH5fNv20TvU49Al19Foos/ViAnmppbCNV9ieGl6/dRMDRzxeFol6peXX/NAgaOytQwZZxTTJRq/Kxd4eWsA=="],
|
||||
|
||||
"@iconify/types": ["@iconify/types@2.0.0", "", {}, "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg=="],
|
||||
|
||||
"@iconify/utils": ["@iconify/utils@3.1.0", "", { "dependencies": { "@antfu/install-pkg": "^1.1.0", "@iconify/types": "^2.0.0", "mlly": "^1.8.0" } }, "sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw=="],
|
||||
@@ -518,6 +528,8 @@
|
||||
|
||||
"comma-separated-tokens": ["comma-separated-tokens@2.0.3", "", {}, "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="],
|
||||
|
||||
"commander": ["commander@11.1.0", "", {}, "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ=="],
|
||||
|
||||
"concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
|
||||
|
||||
"confbox": ["confbox@0.2.2", "", {}, "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ=="],
|
||||
@@ -528,8 +540,16 @@
|
||||
|
||||
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
|
||||
|
||||
"css-select": ["css-select@5.2.2", "", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", "domhandler": "^5.0.2", "domutils": "^3.0.1", "nth-check": "^2.0.1" } }, "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw=="],
|
||||
|
||||
"css-tree": ["css-tree@3.1.0", "", { "dependencies": { "mdn-data": "2.12.2", "source-map-js": "^1.0.1" } }, "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w=="],
|
||||
|
||||
"css-what": ["css-what@6.2.2", "", {}, "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA=="],
|
||||
|
||||
"cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="],
|
||||
|
||||
"csso": ["csso@5.0.5", "", { "dependencies": { "css-tree": "~2.2.0" } }, "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ=="],
|
||||
|
||||
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
|
||||
|
||||
"daisyui": ["daisyui@5.1.10", "", {}, "sha512-p1J/HME2WmaSiy6u2alIbeP3gd5PNVft3+6Bdll0BRSm/UdI4084+pD01LxFug/5wGexNewWqbcEL6nB2n2o+Q=="],
|
||||
@@ -568,6 +588,14 @@
|
||||
|
||||
"doctrine": ["doctrine@2.1.0", "", { "dependencies": { "esutils": "^2.0.2" } }, "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw=="],
|
||||
|
||||
"dom-serializer": ["dom-serializer@2.0.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", "entities": "^4.2.0" } }, "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg=="],
|
||||
|
||||
"domelementtype": ["domelementtype@2.3.0", "", {}, "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="],
|
||||
|
||||
"domhandler": ["domhandler@5.0.3", "", { "dependencies": { "domelementtype": "^2.3.0" } }, "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w=="],
|
||||
|
||||
"domutils": ["domutils@3.2.2", "", { "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3" } }, "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw=="],
|
||||
|
||||
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
|
||||
|
||||
"echarts": ["echarts@6.0.0", "", { "dependencies": { "tslib": "2.3.0", "zrender": "6.0.0" } }, "sha512-Tte/grDQRiETQP4xz3iZWSvoHrkCQtwqd6hs+mifXcjrCuo2iKWbajFObuLJVBlDIJlOzgQPd1hsaKt/3+OMkQ=="],
|
||||
@@ -640,6 +668,8 @@
|
||||
|
||||
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
|
||||
|
||||
"fflate": ["fflate@0.8.2", "", {}, "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A=="],
|
||||
|
||||
"file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="],
|
||||
|
||||
"fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
|
||||
@@ -850,6 +880,8 @@
|
||||
|
||||
"mdast-util-to-hast": ["mdast-util-to-hast@13.2.1", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@ungap/structured-clone": "^1.0.0", "devlop": "^1.0.0", "micromark-util-sanitize-uri": "^2.0.0", "trim-lines": "^3.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA=="],
|
||||
|
||||
"mdn-data": ["mdn-data@2.12.2", "", {}, "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA=="],
|
||||
|
||||
"memoize-one": ["memoize-one@6.0.0", "", {}, "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw=="],
|
||||
|
||||
"merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="],
|
||||
@@ -884,6 +916,8 @@
|
||||
|
||||
"mlly": ["mlly@1.8.0", "", { "dependencies": { "acorn": "^8.15.0", "pathe": "^2.0.3", "pkg-types": "^1.3.1", "ufo": "^1.6.1" } }, "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g=="],
|
||||
|
||||
"modern-tar": ["modern-tar@0.7.3", "", {}, "sha512-4W79zekKGyYU4JXVmB78DOscMFaJth2gGhgfTl2alWE4rNe3nf4N2pqenQ0rEtIewrnD79M687Ouba3YGTLOvg=="],
|
||||
|
||||
"motion-dom": ["motion-dom@12.23.12", "", { "dependencies": { "motion-utils": "^12.23.6" } }, "sha512-RcR4fvMCTESQBD/uKQe49D5RUeDOokkGRmz4ceaJKDBgHYtZtntC/s2vLvY38gqGaytinij/yi3hMcWVcEF5Kw=="],
|
||||
|
||||
"motion-utils": ["motion-utils@12.23.6", "", {}, "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ=="],
|
||||
@@ -1018,6 +1052,8 @@
|
||||
|
||||
"safe-regex-test": ["safe-regex-test@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-regex": "^1.2.1" } }, "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw=="],
|
||||
|
||||
"sax": ["sax@1.4.4", "", {}, "sha512-1n3r/tGXO6b6VXMdFT54SHzT9ytu9yr7TaELowdYpMqY/Ao7EnlQGmAQ1+RatX7Tkkdm6hONI2owqNx2aZj5Sw=="],
|
||||
|
||||
"scule": ["scule@1.3.0", "", {}, "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g=="],
|
||||
|
||||
"semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
||||
@@ -1072,6 +1108,8 @@
|
||||
|
||||
"supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="],
|
||||
|
||||
"svgo": ["svgo@4.0.0", "", { "dependencies": { "commander": "^11.1.0", "css-select": "^5.1.0", "css-tree": "^3.0.1", "css-what": "^6.1.0", "csso": "^5.0.5", "picocolors": "^1.1.1", "sax": "^1.4.1" }, "bin": "./bin/svgo.js" }, "sha512-VvrHQ+9uniE+Mvx3+C9IEe/lWasXCU0nXMY2kZeLrHNICuRiC8uMPyM14UEaMOFA5mhyQqEkB02VoQ16n3DLaw=="],
|
||||
|
||||
"tabbable": ["tabbable@6.4.0", "", {}, "sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg=="],
|
||||
|
||||
"tailwind-merge": ["tailwind-merge@3.4.0", "", {}, "sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g=="],
|
||||
@@ -1224,6 +1262,8 @@
|
||||
|
||||
"aria-hidden/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"csso/css-tree": ["css-tree@2.2.1", "", { "dependencies": { "mdn-data": "2.0.28", "source-map-js": "^1.0.1" } }, "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA=="],
|
||||
|
||||
"element-plus/@vueuse/core": ["@vueuse/core@10.11.1", "", { "dependencies": { "@types/web-bluetooth": "^0.0.20", "@vueuse/metadata": "10.11.1", "@vueuse/shared": "10.11.1", "vue-demi": ">=0.14.8" } }, "sha512-guoy26JQktXPcz+0n3GukWIy/JDNKti9v6VEMu6kV2sYBsWuGiTU8OWdg+ADfUbHg3/3DlqySDe7JmdHrktiww=="],
|
||||
|
||||
"eslint-plugin-vue/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
|
||||
@@ -1284,6 +1324,8 @@
|
||||
|
||||
"@vue/language-core/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
|
||||
|
||||
"csso/css-tree/mdn-data": ["mdn-data@2.0.28", "", {}, "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g=="],
|
||||
|
||||
"element-plus/@vueuse/core/@types/web-bluetooth": ["@types/web-bluetooth@0.0.20", "", {}, "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow=="],
|
||||
|
||||
"element-plus/@vueuse/core/@vueuse/metadata": ["@vueuse/metadata@10.11.1", "", {}, "sha512-IGa5FXd003Ug1qAZmyE8wF3sJ81xGLSqTqtQ6jaVfkeZ4i5kS2mwQF61yhVqojRnenVew5PldLyRgvdl4YYuSw=="],
|
||||
|
||||
4
components.d.ts
vendored
4
components.d.ts
vendored
@@ -13,6 +13,7 @@ export {}
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
AnimatePresence: typeof import('motion-v')['AnimatePresence']
|
||||
AuthDialog: typeof import('./src/components/dialog/AuthDialog.vue')['default']
|
||||
Button: typeof import('./src/components/ui/button/Button.vue')['default']
|
||||
Calendar: typeof import('./src/components/ui/calendar/Calendar.vue')['default']
|
||||
CalendarCell: typeof import('./src/components/ui/calendar/CalendarCell.vue')['default']
|
||||
@@ -44,12 +45,14 @@ declare module 'vue' {
|
||||
PopoverTrigger: typeof import('./src/components/ui/popover/PopoverTrigger.vue')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
UserAuthNavButton: typeof import('./src/components/button/UserAuthNavButton.vue')['default']
|
||||
}
|
||||
}
|
||||
|
||||
// For TSX support
|
||||
declare global {
|
||||
const AnimatePresence: typeof import('motion-v')['AnimatePresence']
|
||||
const AuthDialog: typeof import('./src/components/dialog/AuthDialog.vue')['default']
|
||||
const Button: typeof import('./src/components/ui/button/Button.vue')['default']
|
||||
const Calendar: typeof import('./src/components/ui/calendar/Calendar.vue')['default']
|
||||
const CalendarCell: typeof import('./src/components/ui/calendar/CalendarCell.vue')['default']
|
||||
@@ -81,4 +84,5 @@ declare global {
|
||||
const PopoverTrigger: typeof import('./src/components/ui/popover/PopoverTrigger.vue')['default']
|
||||
const RouterLink: typeof import('vue-router')['RouterLink']
|
||||
const RouterView: typeof import('vue-router')['RouterView']
|
||||
const UserAuthNavButton: typeof import('./src/components/button/UserAuthNavButton.vue')['default']
|
||||
}
|
||||
41
docs/cli-feature/icon.md
Normal file
41
docs/cli-feature/icon.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# 图标
|
||||
|
||||
## 使用图标
|
||||
|
||||
可以通过 tailwindcss 中的图标 class 来直接使用图标,例如
|
||||
|
||||
```html
|
||||
<span class="icon-[material-symbols--person-outline]"></span>
|
||||
```
|
||||
|
||||
而你需要的图标可以在 [Iconify Material 图标库](https://icon-sets.iconify.design/material-symbols/) 中搜索。
|
||||
|
||||
通过点击图标库中的某个图标,你需要在详情栏中选择 CSS - Tailwind Css 栏,其中会告知你该图标对应的 class 名称。
|
||||
|
||||
## 调整图标
|
||||
|
||||
如果您需要调整颜色,您可以在 class 中添加 `text-<color>` 来调整图标颜色,例如
|
||||
|
||||
```html
|
||||
<span class="icon-[material-symbols--person-outline] text-red-500"></span>
|
||||
```
|
||||
|
||||
如果您需要调整图标大小,您可以在 class 中添加 `text-<size>` 来调整图标大小,例如
|
||||
|
||||
```html
|
||||
<span class="icon-[material-symbols--person-outline] text-2xl"></span>
|
||||
```
|
||||
|
||||
更多的调整方法可以参考 [Iconify 图标库调整](https://iconify.design/docs/usage/css/tailwind/tailwind4/size-color.html)
|
||||
|
||||
## 需要更多图标
|
||||
|
||||
您可以在 [Iconify 图标库](https://icon-sets.iconify.design/) 中搜索更多图标。
|
||||
|
||||
我们为您预装的是 Material Symbols 图标库。如果您需要使用别的图标库,可以通过 `@iconify-json/{prefix}` 来 install,注意 {prefix} 的名称要改成中划线模式,例如您需要安装名为 Material Symbols Light 的图标库
|
||||
|
||||
```bash
|
||||
bun add -D @iconify-json/material-symbols-light
|
||||
```
|
||||
|
||||
更多的安装方法可以参考 [Iconify 图标库安装](https://iconify.design/docs/usage/css/tailwind/tailwind4/)
|
||||
@@ -36,6 +36,8 @@
|
||||
"vue-router": "^4.5.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@iconify-json/material-symbols": "^1.2.55",
|
||||
"@iconify/tailwind4": "^1.2.1",
|
||||
"@tailwindcss/vite": "^4.1.11",
|
||||
"@types/node": "^24.1.0",
|
||||
"@vitejs/plugin-vue": "^6.0.0",
|
||||
|
||||
@@ -18,8 +18,6 @@ const onClickChangeLocal = (newLanguage: string) => {
|
||||
tabindex="0"
|
||||
role="button"
|
||||
class="btn btn-sm btn-ghost gap-1 px-1.5 font-bold"
|
||||
aria-label="Language"
|
||||
title="Change Language"
|
||||
>
|
||||
<svg
|
||||
class="text-base-content/70 size-4"
|
||||
|
||||
69
src/components/button/UserAuthNavButton.vue
Normal file
69
src/components/button/UserAuthNavButton.vue
Normal file
@@ -0,0 +1,69 @@
|
||||
<script setup lang="ts">
|
||||
import { renderAuthDialog } from "@/hooks/dialog/renderAuthDialog";
|
||||
|
||||
const dropdownVisible = ref(true);
|
||||
|
||||
const closeDropdown = () => {
|
||||
dropdownVisible.value = false;
|
||||
setTimeout(() => {
|
||||
dropdownVisible.value = true;
|
||||
}, 300);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="dropdown dropdown-hover dropdown-end">
|
||||
<div
|
||||
tabindex="0"
|
||||
role="button"
|
||||
class="btn btn-circle btn-sm btn-ghost px-3 w-16"
|
||||
>
|
||||
<span class="text-sm text-base-content/70">{{ $t("nav.login") }}</span>
|
||||
</div>
|
||||
<ul
|
||||
v-show="dropdownVisible"
|
||||
tabindex="-1"
|
||||
class="dropdown-content z-1 w-100 p-2"
|
||||
>
|
||||
<div class="card bg-base-200 w-96 mt-4 shadow-sm">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title">现在加入!</h2>
|
||||
<p>登录我们的系统</p>
|
||||
<div class="card-actions justify-end">
|
||||
<button
|
||||
class="btn btn-sm"
|
||||
@click="
|
||||
renderAuthDialog('register');
|
||||
closeDropdown();
|
||||
"
|
||||
>
|
||||
{{ $t("nav.register") }}
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-primary btn-sm"
|
||||
@click="
|
||||
renderAuthDialog('login');
|
||||
closeDropdown();
|
||||
"
|
||||
>
|
||||
{{ $t("nav.login") }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ul>
|
||||
|
||||
<!-- 登录弹窗 -->
|
||||
<dialog id="my_modal" class="modal">
|
||||
<div class="modal-box">
|
||||
<h3 class="text-lg font-bold">Hello!</h3>
|
||||
<p class="py-4">Press ESC key or click outside to close</p>
|
||||
</div>
|
||||
<form method="dialog" class="modal-backdrop">
|
||||
<button>close</button>
|
||||
</form>
|
||||
</dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
336
src/components/dialog/AuthDialog.vue
Normal file
336
src/components/dialog/AuthDialog.vue
Normal file
@@ -0,0 +1,336 @@
|
||||
<script setup lang="ts">
|
||||
import { closeAuthDialog } from "@/hooks/dialog/renderAuthDialog";
|
||||
import { AnimatePresence, motion } from "motion-v";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const props = defineProps({
|
||||
defaultAuthFormType: {
|
||||
type: String as PropType<"login" | "register">,
|
||||
default: "login",
|
||||
},
|
||||
});
|
||||
|
||||
const authFormType = ref<"login" | "register">(props.defaultAuthFormType);
|
||||
|
||||
const changeAuthFormType = (type: "login" | "register") => {
|
||||
authFormType.value = "";
|
||||
setTimeout(() => {
|
||||
authFormType.value = type;
|
||||
}, 300);
|
||||
};
|
||||
|
||||
const isNotClose = ref(true);
|
||||
const closeAuthDialogWaitAnim = () => {
|
||||
isNotClose.value = false;
|
||||
setTimeout(() => {
|
||||
closeAuthDialog();
|
||||
}, 500);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div class="fixed inset-0 flex items-center justify-center z-50">
|
||||
<AnimatePresence>
|
||||
<motion.div
|
||||
v-show="isNotClose"
|
||||
class="wrapper"
|
||||
:initial="{ opacity: 0, scale: 0 }"
|
||||
:animate="{ opacity: 1, scale: 1 }"
|
||||
:transition="{
|
||||
duration: 0.4,
|
||||
scale: { type: 'spring', visualDuration: 0.4, bounce: 0.5 },
|
||||
}"
|
||||
:exit="{
|
||||
opacity: 0,
|
||||
scale: 0.2,
|
||||
}"
|
||||
>
|
||||
<span
|
||||
class="icon-close bg-primary text-primary-content"
|
||||
@click="closeAuthDialogWaitAnim"
|
||||
>
|
||||
<span class="icon-[material-symbols--close-small]" />
|
||||
</span>
|
||||
|
||||
<!-- 登录表单 -->
|
||||
<AnimatePresence>
|
||||
<motion.div
|
||||
v-if="authFormType === 'login'"
|
||||
class="form-box login"
|
||||
:initial="{
|
||||
y: -100,
|
||||
opacity: 0,
|
||||
}"
|
||||
:animate="{
|
||||
y: 0,
|
||||
opacity: 1,
|
||||
}"
|
||||
:transition="{
|
||||
duration: 0.3,
|
||||
scale: { type: 'spring', visualDuration: 0.3, bounce: 0.5 },
|
||||
}"
|
||||
:exit="{
|
||||
y: 100,
|
||||
opacity: 0,
|
||||
}"
|
||||
>
|
||||
<h2>
|
||||
{{ t("auth.login") }}
|
||||
</h2>
|
||||
<form action="#">
|
||||
<div class="input-box">
|
||||
<span class="icon">
|
||||
<span class="icon-[material-symbols--mail-outline]" />
|
||||
</span>
|
||||
<input type="text" required />
|
||||
<label>{{ t("auth.email") }}</label>
|
||||
</div>
|
||||
<div class="input-box">
|
||||
<span class="icon">
|
||||
<span class="icon-[material-symbols--lock-outline]" />
|
||||
</span>
|
||||
<input type="password" required />
|
||||
<label>{{ t("auth.password") }}</label>
|
||||
</div>
|
||||
|
||||
<div class="remember-forgot">
|
||||
<label class="label">
|
||||
<input type="checkbox" class="checkbox checkbox-xs" />
|
||||
{{ t("auth.remember_me") }}
|
||||
</label>
|
||||
<a class="label" href="#">{{ t("auth.forget_password") }}</a>
|
||||
</div>
|
||||
<button
|
||||
type="submit"
|
||||
class="btn bg-primary text-primary-content"
|
||||
>
|
||||
{{ t("auth.login") }}
|
||||
</button>
|
||||
<div
|
||||
class="login-register"
|
||||
@click="changeAuthFormType('register')"
|
||||
>
|
||||
<p>
|
||||
{{ t("auth.no_account") }}
|
||||
<a class="register-link cursor-pointer">
|
||||
{{ t("auth.register") }}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
|
||||
<!-- 注册表单 -->
|
||||
<AnimatePresence>
|
||||
<motion.div
|
||||
v-if="authFormType === 'register'"
|
||||
class="form-box register"
|
||||
:initial="{
|
||||
y: -100,
|
||||
opacity: 0,
|
||||
}"
|
||||
:animate="{
|
||||
y: 0,
|
||||
opacity: 1,
|
||||
}"
|
||||
:transition="{
|
||||
duration: 0.3,
|
||||
scale: { type: 'spring', visualDuration: 0.3, bounce: 0.5 },
|
||||
}"
|
||||
:exit="{
|
||||
y: 100,
|
||||
opacity: 0,
|
||||
}"
|
||||
>
|
||||
<h2>{{ t("auth.register") }}</h2>
|
||||
<form action="#">
|
||||
<div class="input-box">
|
||||
<span class="icon">
|
||||
<span class="icon-[material-symbols--mail-outline]" />
|
||||
</span>
|
||||
<input type="text" required />
|
||||
<label>{{ t("auth.email") }}</label>
|
||||
</div>
|
||||
|
||||
<div class="input-box">
|
||||
<span class="icon">
|
||||
<span class="icon-[material-symbols--lock-outline]" />
|
||||
</span>
|
||||
<input type="password" required />
|
||||
<label>{{ t("auth.password") }}</label>
|
||||
</div>
|
||||
|
||||
<div class="remember-forgot">
|
||||
<label class="label">
|
||||
<input type="checkbox" class="checkbox checkbox-xs" />
|
||||
{{ t("auth.agree_terms") }}
|
||||
</label>
|
||||
</div>
|
||||
<button
|
||||
type="submit"
|
||||
class="btn bg-primary text-primary-content"
|
||||
>
|
||||
{{ t("auth.register") }}
|
||||
</button>
|
||||
<div
|
||||
class="login-register"
|
||||
@click="changeAuthFormType('login')"
|
||||
>
|
||||
<p>
|
||||
{{ t("auth.have_account") }}
|
||||
<a class="login-link cursor-pointer">
|
||||
{{ t("auth.login") }}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700;800;900&display=swap");
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: "Poppins", sans-serif;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
position: relative;
|
||||
width: 400px;
|
||||
height: 440px;
|
||||
background: transparent;
|
||||
border: 2px solid rgba(255, 255, 255, 0.5);
|
||||
border-radius: 20px;
|
||||
backdrop-filter: blur(20px);
|
||||
box-shadow: 0 0 30px rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.wrapper .form-box {
|
||||
width: 100%;
|
||||
padding: 40px;
|
||||
}
|
||||
|
||||
.wrapper .icon-close {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
font-size: 2em;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-bottom-left-radius: 20px;
|
||||
cursor: pointer;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.form-box h2 {
|
||||
font-size: 2em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.input-box {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
border-bottom: 2px solid;
|
||||
margin: 30px 0;
|
||||
}
|
||||
|
||||
.input-box label {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 5px;
|
||||
transform: translateY(-50%);
|
||||
font-size: 1em;
|
||||
font-weight: 500;
|
||||
pointer-events: none;
|
||||
transition: 0.5s;
|
||||
}
|
||||
|
||||
.input-box input:focus ~ label,
|
||||
.input-box input:valid ~ label {
|
||||
top: -5px;
|
||||
}
|
||||
|
||||
.input-box input {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: transparent;
|
||||
border: none;
|
||||
outline: none;
|
||||
font-size: 1em;
|
||||
font-weight: 600;
|
||||
padding: 0 35px 0 5px;
|
||||
}
|
||||
|
||||
.input-box .icon {
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
font-size: 1.2rem;
|
||||
line-height: 57px;
|
||||
}
|
||||
|
||||
.remember-forgot {
|
||||
font-size: 0.9em;
|
||||
font-weight: 500;
|
||||
margin: -15px 0 15px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.remember-forgot label input {
|
||||
margin-right: 3px;
|
||||
}
|
||||
.remember-forgot a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.remember-forgot a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.btn {
|
||||
width: 100%;
|
||||
height: 45px;
|
||||
border: none;
|
||||
outline: none;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 1em;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.login-register {
|
||||
font-size: 0.9em;
|
||||
text-align: center;
|
||||
font-weight: 500;
|
||||
margin: 25px 0 10px;
|
||||
}
|
||||
|
||||
.login-register p a {
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.login-register p a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
||||
@@ -5,6 +5,7 @@ import { useI18n } from "vue-i18n";
|
||||
import { throttle } from "radash";
|
||||
import { AnimatePresence, motion } from "motion-v";
|
||||
import { navigateTo } from "@/utils/navigator";
|
||||
import UserAuthNavButton from "../button/UserAuthNavButton.vue";
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
@@ -82,6 +83,7 @@ window.addEventListener("scroll", handleThrottleScroll);
|
||||
<div class="navbar-end">
|
||||
<ChangeThemeDropdownButton />
|
||||
<ChangeLanguageDropdownButton />
|
||||
<UserAuthNavButton />
|
||||
</div>
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
|
||||
22
src/hooks/dialog/renderAuthDialog.ts
Normal file
22
src/hooks/dialog/renderAuthDialog.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import AuthDialog from "@/components/dialog/AuthDialog.vue";
|
||||
import i18n from "@/i18n";
|
||||
|
||||
export const renderAuthDialog = (defaultAuthFormType: "login" | "register") => {
|
||||
const authDialogContainer = document.createElement("div");
|
||||
authDialogContainer.id = "auth-dialog-container";
|
||||
|
||||
document.body.appendChild(authDialogContainer);
|
||||
|
||||
createApp(AuthDialog, {
|
||||
defaultAuthFormType,
|
||||
})
|
||||
.use(i18n)
|
||||
.mount(authDialogContainer);
|
||||
};
|
||||
|
||||
export const closeAuthDialog = () => {
|
||||
const authDialogContainer = document.getElementById("auth-dialog-container");
|
||||
if (authDialogContainer) {
|
||||
authDialogContainer.remove();
|
||||
}
|
||||
};
|
||||
@@ -6,5 +6,18 @@ export const en_USMessages: messagesInterface = {
|
||||
about: "About",
|
||||
theme: "Theme",
|
||||
locale: "Locale",
|
||||
login: "Login",
|
||||
register: "Register",
|
||||
},
|
||||
auth: {
|
||||
login: "Login",
|
||||
register: "Register",
|
||||
email: "Email",
|
||||
password: "Password",
|
||||
remember_me: "Remember Me",
|
||||
forget_password: "Forget Password",
|
||||
no_account: "Don't have an account?",
|
||||
agree_terms: "Agree to the Terms & Conditions",
|
||||
have_account: "Already have an account?",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -8,6 +8,19 @@ export interface messagesInterface {
|
||||
about: string;
|
||||
theme: string;
|
||||
locale: string;
|
||||
login: string;
|
||||
register: string;
|
||||
};
|
||||
auth: {
|
||||
login: string;
|
||||
register: string;
|
||||
email: string;
|
||||
password: string;
|
||||
remember_me: string;
|
||||
forget_password: string;
|
||||
no_account: string;
|
||||
agree_terms: string;
|
||||
have_account: string;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -6,5 +6,18 @@ export const zh_CNMessages: messagesInterface = {
|
||||
about: "关于",
|
||||
theme: "主题",
|
||||
locale: "语言",
|
||||
login: "登录",
|
||||
register: "注册",
|
||||
},
|
||||
auth: {
|
||||
login: "登录",
|
||||
register: "注册",
|
||||
email: "邮箱",
|
||||
password: "密码",
|
||||
remember_me: "记住我",
|
||||
forget_password: "忘记密码",
|
||||
no_account: "没有账号?",
|
||||
agree_terms: "同意条款和条件",
|
||||
have_account: "已经有账号?",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
@import "tailwindcss";
|
||||
@import "tw-animate-css";
|
||||
@plugin "@iconify/tailwind4";
|
||||
|
||||
@custom-variant dark (&:is(.dark *));
|
||||
@plugin "daisyui" {
|
||||
|
||||
Reference in New Issue
Block a user