From 6c86cc30a636d5f4cd2f3f41d2b054facb9e0c9d Mon Sep 17 00:00:00 2001 From: hanlee Date: Wed, 9 Jun 2021 22:41:47 +0900 Subject: [PATCH 01/13] feat: add login form validation --- components.d.ts | 4 ++ package.json | 3 ++ pnpm-lock.yaml | 22 ++++++++++ src/components/Checkbox.vue | 42 +++++++++++++++++++ src/components/Input.vue | 63 +++++++++++++++++----------- src/components/LoginForm.vue | 45 ++++++++++++++++++++ src/components/RegisterAccount.vue | 20 +++++++++ src/components/SSOLogin.vue | 24 +++++++++++ src/logics/auth.ts | 15 +++++++ src/main.ts | 4 ++ src/pages/account/password_reset.vue | 12 ++++++ src/pages/login.vue | 57 ++----------------------- src/plugins/vee-validate.ts | 19 +++++++++ tsconfig.json | 8 +++- windi.config.ts | 5 ++- 15 files changed, 263 insertions(+), 80 deletions(-) create mode 100644 src/components/Checkbox.vue create mode 100644 src/components/LoginForm.vue create mode 100644 src/components/RegisterAccount.vue create mode 100644 src/components/SSOLogin.vue create mode 100644 src/pages/account/password_reset.vue create mode 100644 src/plugins/vee-validate.ts diff --git a/components.d.ts b/components.d.ts index b12a615..d27f5a9 100644 --- a/components.d.ts +++ b/components.d.ts @@ -3,8 +3,12 @@ declare module 'vue' { export interface GlobalComponents { + Checkbox: typeof import('./src/components/Checkbox.vue')['default'] Header: typeof import('./src/components/Header.vue')['default'] Input: typeof import('./src/components/Input.vue')['default'] + LoginForm: typeof import('./src/components/LoginForm.vue')['default'] + RegisterAccount: typeof import('./src/components/RegisterAccount.vue')['default'] + SSOLogin: typeof import('./src/components/SSOLogin.vue')['default'] } } diff --git a/package.json b/package.json index 99bc0af..0b76e73 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,10 @@ "lint:fix": "eslint --fix --ext .js,.ts,.vue,.json ." }, "dependencies": { + "@vee-validate/i18n": "^4.1.20", + "@vee-validate/rules": "^4.1.20", "@vueuse/core": "^5.0.2", + "vee-validate": "^4.4.4", "vue": "^3.1.1", "vue-router": "^4.0.8" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5ce63e2..188cde7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3,11 +3,14 @@ lockfileVersion: 5.3 specifiers: '@hannoeru/eslint-config': ^0.1.1 '@iconify/json': ^1.1.354 + '@vee-validate/i18n': ^4.1.20 + '@vee-validate/rules': ^4.1.20 '@vitejs/plugin-vue': ^1.2.3 '@vue/compiler-sfc': ^3.1.1 '@vueuse/core': ^5.0.2 eslint: ^7.28.0 typescript: ^4.3.2 + vee-validate: ^4.4.4 vite: ^2.3.7 vite-plugin-components: ^0.10.4 vite-plugin-icons: ^0.6.1 @@ -19,7 +22,10 @@ specifiers: windicss: ^3.1.3 dependencies: + '@vee-validate/i18n': 4.1.20 + '@vee-validate/rules': 4.1.20 '@vueuse/core': 5.0.2_vue@3.1.1 + vee-validate: 4.4.4_vue@3.1.1 vue: 3.1.1 vue-router: 4.0.8_vue@3.1.1 @@ -646,6 +652,14 @@ packages: eslint-visitor-keys: 2.1.0 dev: true + /@vee-validate/i18n/4.1.20: + resolution: {integrity: sha512-3K7R3m3Z206Hq6iJpXHJNSYDjJFFHzZrA7wjowTts52iEsv9kNpA/WCkdJsQLae8J+QLiKyK53ma35gAet8yFg==} + dev: false + + /@vee-validate/rules/4.1.20: + resolution: {integrity: sha512-DsG+NrCv/rEIpEEOqv+R7Za5rsA9xdIq4XEARjcHI3liXgkd8aeNWCnnmGyDbkz2BVav7n37IhP16ceJHnFXBA==} + dev: false + /@vitejs/plugin-vue/1.2.3_@vue+compiler-sfc@3.1.1: resolution: {integrity: sha512-LlnLpObkGKZ+b7dcpL4T24l13nPSHLjo+6Oc7MbZiKz5PMAUzADfNJ3EKfYIQ0l0969nxf2jp/9vsfnuJ7h6fw==} engines: {node: '>=12.0.0'} @@ -4206,6 +4220,14 @@ packages: spdx-expression-parse: 3.0.1 dev: true + /vee-validate/4.4.4_vue@3.1.1: + resolution: {integrity: sha512-1zVt1wJEE/I7ozD2Ixrz7pfAbcZsCItvSNVJq3CAw48cKZOM/m6i8IM/LfbhwIs/HF4xjx0G7YbMQZTG6N8keA==} + peerDependencies: + vue: ^3.0.0 + dependencies: + vue: 3.1.1 + dev: false + /vfile-message/1.1.1: resolution: {integrity: sha512-1WmsopSGhWt5laNir+633LszXvZ+Z/lxveBf6yhGsqnQIhlhzooZae7zV6YVM1Sdkw68dtAW3ow0pOdPANugvA==} dependencies: diff --git a/src/components/Checkbox.vue b/src/components/Checkbox.vue new file mode 100644 index 0000000..bbb8b5c --- /dev/null +++ b/src/components/Checkbox.vue @@ -0,0 +1,42 @@ + + + diff --git a/src/components/Input.vue b/src/components/Input.vue index 1635999..4d03cc0 100644 --- a/src/components/Input.vue +++ b/src/components/Input.vue @@ -1,20 +1,23 @@ diff --git a/src/components/LoginForm.vue b/src/components/LoginForm.vue new file mode 100644 index 0000000..6657836 --- /dev/null +++ b/src/components/LoginForm.vue @@ -0,0 +1,45 @@ + + + diff --git a/src/components/RegisterAccount.vue b/src/components/RegisterAccount.vue new file mode 100644 index 0000000..d6ad957 --- /dev/null +++ b/src/components/RegisterAccount.vue @@ -0,0 +1,20 @@ + + + diff --git a/src/components/SSOLogin.vue b/src/components/SSOLogin.vue new file mode 100644 index 0000000..1ac291c --- /dev/null +++ b/src/components/SSOLogin.vue @@ -0,0 +1,24 @@ + + + diff --git a/src/logics/auth.ts b/src/logics/auth.ts index d01ca83..f9296aa 100644 --- a/src/logics/auth.ts +++ b/src/logics/auth.ts @@ -15,6 +15,7 @@ interface LogoutResponse { export interface LoginArgs { username: string password: string + remember: boolean } export function useAuth() { @@ -41,6 +42,17 @@ export function useAuth() { } } + // handle SSO logins + function loginWithLine() { + alert('Login with Line') + } + function loginWithFacebook() { + alert('Login with Facebook') + } + function loginWithTwitter() { + alert('Login with Twitter') + } + function logout() { const { isFetching, error, data } = useFetch(`${API_ENDPOINT}/logout`).json().post() @@ -60,6 +72,9 @@ export function useAuth() { return { userId, login, + loginWithLine, + loginWithFacebook, + loginWithTwitter, logout, isAuthorized, } diff --git a/src/main.ts b/src/main.ts index 95522f8..ee961a7 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,9 +3,13 @@ import { createRouter, createWebHistory } from 'vue-router' import routes from 'virtual:generated-pages' import App from './App.vue' +// css import 'virtual:windi.css' import './style/main.css' +// plugins +import './plugins/vee-validate' + const app = createApp(App) const router = createRouter({ history: createWebHistory(), diff --git a/src/pages/account/password_reset.vue b/src/pages/account/password_reset.vue new file mode 100644 index 0000000..c73889a --- /dev/null +++ b/src/pages/account/password_reset.vue @@ -0,0 +1,12 @@ + + + diff --git a/src/pages/login.vue b/src/pages/login.vue index 2f2e790..c59c1c2 100644 --- a/src/pages/login.vue +++ b/src/pages/login.vue @@ -1,21 +1,7 @@ diff --git a/src/plugins/vee-validate.ts b/src/plugins/vee-validate.ts new file mode 100644 index 0000000..583d6c3 --- /dev/null +++ b/src/plugins/vee-validate.ts @@ -0,0 +1,19 @@ +import { defineRule, configure } from 'vee-validate' +import { required, email, min } from '@vee-validate/rules' +import { localize, setLocale } from '@vee-validate/i18n' +import zh_TW from '@vee-validate/i18n/dist/locale/zh_TW.json' + +// 導入驗證規則 +defineRule('min', min) +defineRule('email', email) +defineRule('required', required) + +// 新增語言(用於錯誤信息) +configure({ + generateMessage: localize({ + 'zh-tw': zh_TW, + }), +}) + +// 設定當前語言 +setLocale('zh-tw') diff --git a/tsconfig.json b/tsconfig.json index 9563901..4424932 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -16,5 +16,11 @@ "@/*": ["src/*"] } }, - "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"] + "include": [ + "src/**/*.ts", + "src/**/*.d.ts", + "src/**/*.tsx", + "src/**/*.vue", + "*.d.ts" + ] } diff --git a/windi.config.ts b/windi.config.ts index 102dff2..ac9f7c0 100644 --- a/windi.config.ts +++ b/windi.config.ts @@ -18,8 +18,11 @@ export default defineConfig({ '&:focus': { '@apply': 'outline-none', }, + '&:disabled': { + '@apply': 'bg-gray-300', + }, '&-outline': { - '@apply': ' py-1.5 bg-white border border-primary text-primary', + '@apply': 'py-1.5 bg-white border border-primary text-primary', }, }, 'icon-btn': { From 7a28796ba1819b6e883d116ff51a29e1eb5bf7db Mon Sep 17 00:00:00 2001 From: hanlee Date: Wed, 9 Jun 2021 22:44:48 +0900 Subject: [PATCH 02/13] fix: build --- package.json | 3 ++- pnpm-lock.yaml | 46 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 0b76e73..b428a19 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,8 @@ "@vueuse/core": "^5.0.2", "vee-validate": "^4.4.4", "vue": "^3.1.1", - "vue-router": "^4.0.8" + "vue-router": "^4.0.8", + "yup": "^0.32.9" }, "devDependencies": { "@hannoeru/eslint-config": "^0.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 188cde7..181a524 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,6 +20,7 @@ specifiers: vue-router: ^4.0.8 vue-tsc: ^0.1.7 windicss: ^3.1.3 + yup: ^0.32.9 dependencies: '@vee-validate/i18n': 4.1.20 @@ -28,6 +29,7 @@ dependencies: vee-validate: 4.4.4_vue@3.1.1 vue: 3.1.1 vue-router: 4.0.8_vue@3.1.1 + yup: 0.32.9 devDependencies: '@hannoeru/eslint-config': 0.1.1_eslint@7.28.0+typescript@4.3.2 @@ -223,6 +225,12 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + /@babel/runtime/7.14.0: + resolution: {integrity: sha512-JELkvo/DlpNdJ7dlyw/eY7E0suy5i5GQH+Vlxaq1nsNJ+H7f4Vtv3jMeCEgRhZZQFXTjldYfQgv2qmM6M1v5wA==} + dependencies: + regenerator-runtime: 0.13.7 + dev: false + /@babel/template/7.12.13: resolution: {integrity: sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==} dependencies: @@ -519,6 +527,10 @@ packages: resolution: {integrity: sha1-7ihweulOEdK4J7y+UnC86n8+ce4=} dev: true + /@types/lodash/4.14.170: + resolution: {integrity: sha512-bpcvu/MKHHeYX+qeEN8GE7DIravODWdACVA1ctevD8CN24RhPZIKMn9ntfAsrvLfSX3cR5RrBKAbYm9bGs0A+Q==} + dev: false + /@types/node/15.12.2: resolution: {integrity: sha512-zjQ69G564OCIWIOHSXyQEEDpdpGl+G348RAKY0XXy9Z5kU9Vzv1GMNnkar/ZJ8dzXB3COzD9Mo9NtRZ4xfgUww==} dev: true @@ -2751,6 +2763,10 @@ packages: p-locate: 4.1.0 dev: true + /lodash-es/4.17.21: + resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} + dev: false + /lodash.camelcase/4.3.0: resolution: {integrity: sha1-soqmKIorn8ZRA1x3EfZathkDMaY=} dev: true @@ -2773,7 +2789,6 @@ packages: /lodash/4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - dev: true /loose-envify/1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} @@ -2916,6 +2931,10 @@ packages: resolution: {integrity: sha512-0ZIR9PasPxGXmRsEF8jsDzndzHDj7tIav+JUmvIFB/WHswliFnquxECT/De7GR4yg99ky/NlRKJT82G1y271bw==} dev: true + /nanoclone/0.2.1: + resolution: {integrity: sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA==} + dev: false + /nanoid/3.1.23: resolution: {integrity: sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -3321,6 +3340,10 @@ packages: react-is: 16.13.1 dev: true + /property-expr/2.0.4: + resolution: {integrity: sha512-sFPkHQjVKheDNnPvotjQmm3KD3uk1fWKUN7CrpdbwmUx3CrG3QiM8QpTSimvig5vTXmTvjz7+TDvXOI9+4rkcg==} + dev: false + /property-information/5.6.0: resolution: {integrity: sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==} dependencies: @@ -3524,6 +3547,10 @@ packages: strip-indent: 2.0.0 dev: true + /regenerator-runtime/0.13.7: + resolution: {integrity: sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==} + dev: false + /regexp-tree/0.1.23: resolution: {integrity: sha512-+7HWfb4Bvu8Rs2eQTUIpX9I/PlQkYOuTNbRpKLJlQpSgwSkzFYh+pUj0gtvglnOZLKB6YgnIgRuJ2/IlpL48qw==} hasBin: true @@ -3981,6 +4008,10 @@ packages: resolution: {integrity: sha1-zCAOqyYT9BZtJ/+a/HylbUnfbrQ=} dev: true + /toposort/2.0.2: + resolution: {integrity: sha1-riF2gXXRVZ1IvvNUILL0li8JwzA=} + dev: false + /trim-newlines/2.0.0: resolution: {integrity: sha1-tAPQuRvlDDMd/EuC7s6yLD3hbSA=} engines: {node: '>=4'} @@ -4721,3 +4752,16 @@ packages: dependencies: camelcase: 4.1.0 dev: true + + /yup/0.32.9: + resolution: {integrity: sha512-Ci1qN+i2H0XpY7syDQ0k5zKQ/DoxO0LzPg8PAR/X4Mpj6DqaeCoIYEEjDJwhArh3Fa7GWbQQVDZKeXYlSH4JMg==} + engines: {node: '>=10'} + dependencies: + '@babel/runtime': 7.14.0 + '@types/lodash': 4.14.170 + lodash: 4.17.21 + lodash-es: 4.17.21 + nanoclone: 0.2.1 + property-expr: 2.0.4 + toposort: 2.0.2 + dev: false From 3230a85db7267d134aeee837f13ed95178dd97a8 Mon Sep 17 00:00:00 2001 From: hanlee Date: Wed, 9 Jun 2021 22:59:13 +0900 Subject: [PATCH 03/13] fix: localStorage serialize with null --- src/logics/auth.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/logics/auth.ts b/src/logics/auth.ts index f9296aa..0fa55f2 100644 --- a/src/logics/auth.ts +++ b/src/logics/auth.ts @@ -20,10 +20,19 @@ export interface LoginArgs { export function useAuth() { const router = useRouter() - const userId = useStorage('userId', null) + const userId = useStorage('userId', null, undefined, { + serializer: { + read(raw) { + return raw === 'null' ? null : raw + }, + write(raw) { + return String(raw) + }, + }, + }) const isAuthorized = computed(() => { - return userId.value !== 'null' + return userId.value !== null }) function login(user: LoginArgs) { From 3fc587426539a4c763b13c4f1590aaac9d4c6f0b Mon Sep 17 00:00:00 2001 From: hanlee Date: Thu, 10 Jun 2021 01:34:11 +0900 Subject: [PATCH 04/13] feat: personal register page --- components.d.ts | 7 +- src/components/LoginForm.vue | 8 +- src/components/RegisterAccount.vue | 6 +- .../{Checkbox.vue => TheCheckbox.vue} | 0 src/components/{Input.vue => TheInput.vue} | 17 +-- src/components/TheRadio.vue | 52 ++++++++ src/components/TheSelect.vue | 72 +++++++++++ src/components/layout/AuthLayout.vue | 7 + src/pages/login.vue | 18 ++- src/pages/register/org.vue | 0 src/pages/register/personal.vue | 122 ++++++++++++++++++ src/plugins/vee-validate.ts | 3 +- 12 files changed, 284 insertions(+), 28 deletions(-) rename src/components/{Checkbox.vue => TheCheckbox.vue} (100%) rename src/components/{Input.vue => TheInput.vue} (72%) create mode 100644 src/components/TheRadio.vue create mode 100644 src/components/TheSelect.vue create mode 100644 src/components/layout/AuthLayout.vue create mode 100644 src/pages/register/org.vue create mode 100644 src/pages/register/personal.vue diff --git a/components.d.ts b/components.d.ts index d27f5a9..0dfcc23 100644 --- a/components.d.ts +++ b/components.d.ts @@ -3,12 +3,15 @@ declare module 'vue' { export interface GlobalComponents { - Checkbox: typeof import('./src/components/Checkbox.vue')['default'] Header: typeof import('./src/components/Header.vue')['default'] - Input: typeof import('./src/components/Input.vue')['default'] LoginForm: typeof import('./src/components/LoginForm.vue')['default'] RegisterAccount: typeof import('./src/components/RegisterAccount.vue')['default'] SSOLogin: typeof import('./src/components/SSOLogin.vue')['default'] + TheCheckbox: typeof import('./src/components/TheCheckbox.vue')['default'] + TheInput: typeof import('./src/components/TheInput.vue')['default'] + TheRadio: typeof import('./src/components/TheRadio.vue')['default'] + TheSelect: typeof import('./src/components/TheSelect.vue')['default'] + AuthLayout: typeof import('./src/components/layout/AuthLayout.vue')['default'] } } diff --git a/src/components/LoginForm.vue b/src/components/LoginForm.vue index 6657836..32f8af0 100644 --- a/src/components/LoginForm.vue +++ b/src/components/LoginForm.vue @@ -15,25 +15,25 @@ function onSubmit(values: LoginArgs) {