create-pubinfo 2.0.0-rc.4 → 2.0.0-rc.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/dist/index.js +199 -148
  2. package/package.json +5 -1
  3. package/templates/{pubinfo-template → pubinfo-app}/.env +0 -3
  4. package/templates/{pubinfo-template/openapi.config.ts → pubinfo-app/openapi.config.ts.hbs} +1 -1
  5. package/templates/{pubinfo-template/package.json → pubinfo-app/package.json.hbs} +8 -4
  6. package/templates/pubinfo-app/src/settings.ts.hbs +8 -0
  7. package/templates/pubinfo-module/.editorconfig +10 -0
  8. package/templates/pubinfo-module/README.md.hbs +27 -0
  9. package/templates/pubinfo-module/_gitignore +38 -0
  10. package/templates/pubinfo-module/_npmrc +5 -0
  11. package/templates/pubinfo-module/eslint.config.ts +3 -0
  12. package/templates/pubinfo-module/package.json.hbs +67 -0
  13. package/templates/pubinfo-module/playground/.env +2 -0
  14. package/templates/pubinfo-module/playground/.env.development +6 -0
  15. package/templates/pubinfo-module/playground/.env.production +10 -0
  16. package/templates/pubinfo-module/playground/index.html +47 -0
  17. package/templates/pubinfo-module/playground/openapi.config.ts.hbs +33 -0
  18. package/templates/pubinfo-module/playground/package.json.hbs +22 -0
  19. package/templates/pubinfo-module/playground/pubinfo.config.ts +9 -0
  20. package/templates/pubinfo-module/playground/public/browser_upgrade/chrome.png +0 -0
  21. package/templates/pubinfo-module/playground/public/browser_upgrade/edge.png +0 -0
  22. package/templates/pubinfo-module/playground/public/browser_upgrade/index.css +49 -0
  23. package/templates/pubinfo-module/playground/public/loading.css +92 -0
  24. package/templates/pubinfo-module/playground/src/App.vue +7 -0
  25. package/templates/pubinfo-module/playground/src/api/modules/auth/index.ts +3 -0
  26. package/templates/pubinfo-module/playground/src/api/modules/auth/renzhengfuwu.ts +145 -0
  27. package/templates/pubinfo-module/playground/src/api/modules/auth/typings.d.ts +97 -0
  28. package/templates/pubinfo-module/playground/src/api/request.ts +125 -0
  29. package/templates/pubinfo-module/playground/src/assets/icons/logo.svg +1 -0
  30. package/templates/pubinfo-module/playground/src/assets/icons/process-management.svg +1 -0
  31. package/templates/pubinfo-module/playground/src/assets/icons/workbench.svg +1 -0
  32. package/templates/pubinfo-module/playground/src/assets/images/login-bg.webp +0 -0
  33. package/templates/pubinfo-module/playground/src/assets/images/login-bg_dark.webp +0 -0
  34. package/templates/pubinfo-module/playground/src/assets/images/login-small.png +0 -0
  35. package/templates/pubinfo-module/playground/src/assets/images/login-small_dark.webp +0 -0
  36. package/templates/pubinfo-module/playground/src/components/UIProvider/index.vue +51 -0
  37. package/templates/pubinfo-module/playground/src/layouts/index.vue +44 -0
  38. package/templates/pubinfo-module/playground/src/main.ts.hbs +24 -0
  39. package/templates/pubinfo-module/playground/src/modules/auth.ts +20 -0
  40. package/templates/pubinfo-module/playground/src/modules/rbac.ts +10 -0
  41. package/templates/pubinfo-module/playground/src/routes/index.ts +71 -0
  42. package/templates/pubinfo-module/playground/src/routes/modules/demo/breadcrumb.example.ts +62 -0
  43. package/templates/pubinfo-module/playground/src/routes/modules/demo/link.ts +15 -0
  44. package/templates/pubinfo-module/playground/src/routes/modules/demo/multilevel.menu.example.ts +68 -0
  45. package/templates/pubinfo-module/playground/src/routes/modules/demo/other.page.ts +37 -0
  46. package/templates/pubinfo-module/playground/src/routes/modules/demo/single.ts +14 -0
  47. package/templates/{pubinfo-template → pubinfo-module/playground}/src/settings.ts +1 -1
  48. package/templates/pubinfo-module/playground/src/stores/index.ts +2 -0
  49. package/templates/pubinfo-module/playground/src/stores/modules/conter.ts +16 -0
  50. package/templates/pubinfo-module/playground/src/views/demo/breadcrumb_example/detail1.vue +11 -0
  51. package/templates/pubinfo-module/playground/src/views/demo/breadcrumb_example/detail2.vue +11 -0
  52. package/templates/pubinfo-module/playground/src/views/demo/breadcrumb_example/list1.vue +11 -0
  53. package/templates/pubinfo-module/playground/src/views/demo/breadcrumb_example/list2.vue +11 -0
  54. package/templates/pubinfo-module/playground/src/views/demo/multilevel_menu_example/level2/level3/page1.vue +11 -0
  55. package/templates/pubinfo-module/playground/src/views/demo/multilevel_menu_example/level2/level3/page2.vue +11 -0
  56. package/templates/pubinfo-module/playground/src/views/demo/multilevel_menu_example/level2/page.vue +11 -0
  57. package/templates/pubinfo-module/playground/src/views/demo/multilevel_menu_example/page.vue +11 -0
  58. package/templates/pubinfo-module/playground/src/views/demo/other_page/des.vue +13 -0
  59. package/templates/pubinfo-module/playground/src/views/demo/other_page/index.vue +25 -0
  60. package/templates/pubinfo-module/playground/src/views/demo/preview-empty/index.vue +17 -0
  61. package/templates/pubinfo-module/playground/src/views/demo/single/index.vue +13 -0
  62. package/templates/pubinfo-module/playground/src/views/system/index.vue +5 -0
  63. package/templates/pubinfo-module/playground/src/views/system/login/components/LoginForm.vue +29 -0
  64. package/templates/pubinfo-module/playground/src/views/system/login/components/LoginWithPhone.vue +213 -0
  65. package/templates/pubinfo-module/playground/src/views/system/login/components/PasswordLogin.vue +194 -0
  66. package/templates/pubinfo-module/playground/src/views/system/login/components/Savephone.vue +17 -0
  67. package/templates/pubinfo-module/playground/src/views/system/login/components/Useragreement.vue +26 -0
  68. package/templates/pubinfo-module/playground/src/views/system/login/composables.ts +84 -0
  69. package/templates/pubinfo-module/playground/src/views/system/login/index.vue +142 -0
  70. package/templates/pubinfo-module/playground/tsconfig.json +3 -0
  71. package/templates/pubinfo-module/playground/uno.config.ts +17 -0
  72. package/templates/pubinfo-module/pnpm-workspace.yaml +2 -0
  73. package/templates/pubinfo-module/pubinfo.config.ts.hbs +36 -0
  74. package/templates/pubinfo-module/src/index.ts.hbs +9 -0
  75. package/templates/pubinfo-module/src/pages/demo.vue +11 -0
  76. package/templates/pubinfo-module/stylelint.config.js +3 -0
  77. package/templates/pubinfo-module/tsconfig.json +16 -0
  78. package/templates/pubinfo-module/uno.config.ts +8 -0
  79. package/templates/{pubinfo-template → pubinfo-app}/.browserslistrc +0 -0
  80. package/templates/{pubinfo-template → pubinfo-app}/.editorconfig +0 -0
  81. package/templates/{pubinfo-template → pubinfo-app}/.env.development +0 -0
  82. package/templates/{pubinfo-template → pubinfo-app}/.env.production +0 -0
  83. package/templates/{pubinfo-template → pubinfo-app}/_gitignore +0 -0
  84. package/templates/{pubinfo-template → pubinfo-app}/_npmrc +0 -0
  85. package/templates/{pubinfo-template → pubinfo-app}/eslint.config.ts +0 -0
  86. package/templates/{pubinfo-template → pubinfo-app}/index.html +0 -0
  87. package/templates/{pubinfo-template → pubinfo-app}/pubinfo.config.ts +0 -0
  88. package/templates/{pubinfo-template → pubinfo-app}/public/browser_upgrade/chrome.png +0 -0
  89. package/templates/{pubinfo-template → pubinfo-app}/public/browser_upgrade/edge.png +0 -0
  90. package/templates/{pubinfo-template → pubinfo-app}/public/browser_upgrade/index.css +0 -0
  91. package/templates/{pubinfo-template → pubinfo-app}/public/loading.css +0 -0
  92. package/templates/{pubinfo-template → pubinfo-app}/src/App.vue +0 -0
  93. package/templates/{pubinfo-template → pubinfo-app}/src/api/modules/auth/index.ts +0 -0
  94. package/templates/{pubinfo-template → pubinfo-app}/src/api/modules/auth/renzhengfuwu.ts +0 -0
  95. package/templates/{pubinfo-template → pubinfo-app}/src/api/modules/auth/typings.d.ts +0 -0
  96. package/templates/{pubinfo-template → pubinfo-app}/src/api/request.ts +0 -0
  97. package/templates/{pubinfo-template → pubinfo-app}/src/assets/icons/logo.svg +0 -0
  98. package/templates/{pubinfo-template → pubinfo-app}/src/assets/icons/process-management.svg +0 -0
  99. package/templates/{pubinfo-template → pubinfo-app}/src/assets/icons/workbench.svg +0 -0
  100. package/templates/{pubinfo-template → pubinfo-app}/src/assets/images/login-bg.webp +0 -0
  101. package/templates/{pubinfo-template → pubinfo-app}/src/assets/images/login-bg_dark.webp +0 -0
  102. package/templates/{pubinfo-template → pubinfo-app}/src/assets/images/login-small.png +0 -0
  103. package/templates/{pubinfo-template → pubinfo-app}/src/assets/images/login-small_dark.webp +0 -0
  104. package/templates/{pubinfo-template → pubinfo-app}/src/components/UIProvider/index.vue +0 -0
  105. package/templates/{pubinfo-template → pubinfo-app}/src/layouts/index.vue +0 -0
  106. package/templates/{pubinfo-template → pubinfo-app}/src/main.ts +0 -0
  107. package/templates/{pubinfo-template → pubinfo-app}/src/modules/auth.ts +0 -0
  108. package/templates/{pubinfo-template → pubinfo-app}/src/modules/rbac.ts +0 -0
  109. package/templates/{pubinfo-template → pubinfo-app}/src/routes/index.ts +0 -0
  110. package/templates/{pubinfo-template → pubinfo-app}/src/routes/modules/demo/breadcrumb.example.ts +0 -0
  111. package/templates/{pubinfo-template → pubinfo-app}/src/routes/modules/demo/link.ts +0 -0
  112. package/templates/{pubinfo-template → pubinfo-app}/src/routes/modules/demo/multilevel.menu.example.ts +0 -0
  113. package/templates/{pubinfo-template → pubinfo-app}/src/routes/modules/demo/other.page.ts +0 -0
  114. package/templates/{pubinfo-template → pubinfo-app}/src/routes/modules/demo/single.ts +0 -0
  115. package/templates/{pubinfo-template → pubinfo-app}/src/stores/index.ts +0 -0
  116. package/templates/{pubinfo-template → pubinfo-app}/src/stores/modules/conter.ts +0 -0
  117. package/templates/{pubinfo-template → pubinfo-app}/src/views/demo/breadcrumb_example/detail1.vue +0 -0
  118. package/templates/{pubinfo-template → pubinfo-app}/src/views/demo/breadcrumb_example/detail2.vue +0 -0
  119. package/templates/{pubinfo-template → pubinfo-app}/src/views/demo/breadcrumb_example/list1.vue +0 -0
  120. package/templates/{pubinfo-template → pubinfo-app}/src/views/demo/breadcrumb_example/list2.vue +0 -0
  121. package/templates/{pubinfo-template → pubinfo-app}/src/views/demo/multilevel_menu_example/level2/level3/page1.vue +0 -0
  122. package/templates/{pubinfo-template → pubinfo-app}/src/views/demo/multilevel_menu_example/level2/level3/page2.vue +0 -0
  123. package/templates/{pubinfo-template → pubinfo-app}/src/views/demo/multilevel_menu_example/level2/page.vue +0 -0
  124. package/templates/{pubinfo-template → pubinfo-app}/src/views/demo/multilevel_menu_example/page.vue +0 -0
  125. package/templates/{pubinfo-template → pubinfo-app}/src/views/demo/other_page/des.vue +0 -0
  126. package/templates/{pubinfo-template → pubinfo-app}/src/views/demo/other_page/index.vue +0 -0
  127. package/templates/{pubinfo-template → pubinfo-app}/src/views/demo/preview-empty/index.vue +0 -0
  128. package/templates/{pubinfo-template → pubinfo-app}/src/views/demo/single/index.vue +0 -0
  129. package/templates/{pubinfo-template → pubinfo-app}/src/views/system/index.vue +0 -0
  130. package/templates/{pubinfo-template → pubinfo-app}/src/views/system/login/components/LoginForm.vue +0 -0
  131. package/templates/{pubinfo-template → pubinfo-app}/src/views/system/login/components/LoginWithPhone.vue +0 -0
  132. package/templates/{pubinfo-template → pubinfo-app}/src/views/system/login/components/PasswordLogin.vue +0 -0
  133. package/templates/{pubinfo-template → pubinfo-app}/src/views/system/login/components/Savephone.vue +0 -0
  134. package/templates/{pubinfo-template → pubinfo-app}/src/views/system/login/components/Useragreement.vue +0 -0
  135. package/templates/{pubinfo-template → pubinfo-app}/src/views/system/login/composables.ts +1 -1
  136. /package/templates/{pubinfo-template → pubinfo-app}/src/views/system/login/index.vue +0 -0
  137. /package/templates/{pubinfo-template → pubinfo-app}/stylelint.config.js +0 -0
  138. /package/templates/{pubinfo-template → pubinfo-app}/tsconfig.json +0 -0
  139. /package/templates/{pubinfo-template → pubinfo-app}/uno.config.ts +0 -0
@@ -0,0 +1,213 @@
1
+ <script setup lang="ts">
2
+ import type { SelectProps } from 'ant-design-vue';
3
+ import type { FormInstance, Rule } from 'ant-design-vue/es/form';
4
+ import { useToggle } from '@vueuse/core';
5
+ import { message } from 'ant-design-vue';
6
+ import { useCountdown } from '../composables';
7
+ import Savephone from './Savephone.vue';
8
+
9
+ defineOptions({
10
+ name: 'LoginWithPhone',
11
+ });
12
+
13
+ interface FormState {
14
+ phone: string
15
+ verify: string
16
+ checked: false
17
+ }
18
+ const [verifyDisabled, setVerifyDisabled] = useToggle(true);
19
+ const phoneForm = ref<FormInstance>();
20
+ const { count, start, isStart } = useCountdown();
21
+ const regionCode = ref<number>(0);
22
+ const router = useRouter();
23
+ const route = useRoute();
24
+
25
+ const formState = reactive<FormState>({
26
+ phone: '',
27
+ verify: '',
28
+ checked: false,
29
+ });
30
+
31
+ const rules: Record<string, Rule[]> = {
32
+ phone: [
33
+ {
34
+ validator: checkPhoneIsCorrect,
35
+ whitespace: true,
36
+ trigger: 'change',
37
+ },
38
+ ],
39
+ verify: [
40
+ {
41
+ required: true,
42
+ whitespace: true,
43
+ message: '请输入验证码',
44
+ },
45
+ ],
46
+ };
47
+
48
+ const regionCodeOptins: SelectProps['options'] = [
49
+ {
50
+ value: 0,
51
+ label: '+86 (中国大陆)',
52
+ },
53
+ {
54
+ value: 1,
55
+ label: '+852 (香港行政区)',
56
+ },
57
+ {
58
+ value: 2,
59
+ label: '+853 (澳门行政区)',
60
+ },
61
+ {
62
+ value: 3,
63
+ label: '+886 (台湾省)',
64
+ },
65
+ ];
66
+
67
+ async function checkPhoneIsCorrect(_rule: Rule, value: string) {
68
+ function reject(area: string) {
69
+ setVerifyDisabled(true);
70
+ return Promise.reject(new Error(`请输入正确的${area}手机号`));
71
+ }
72
+ if (!value) {
73
+ setVerifyDisabled(true);
74
+ return Promise.reject(new Error(`请输入手机号`));
75
+ }
76
+ switch (regionCode.value) {
77
+ // 中国大陆
78
+ case 0:
79
+ if (!/^1[3-9]\d{9}$/.test(value)) {
80
+ return reject('中国大陆');
81
+ }
82
+ break;
83
+ // 香港特别行政区
84
+ case 1:
85
+ if (!/^([569])\d{7}$/.test(value)) {
86
+ return reject('香港特别行政区');
87
+ }
88
+ break;
89
+ // 澳门特别行政区
90
+ case 2:
91
+ if (!/^6\d{7}$/.test(value)) {
92
+ return reject('澳门特别行政区');
93
+ }
94
+ break;
95
+ // 台湾省
96
+ case 3:
97
+ if (!/^9\d{8}$/.test(value)) {
98
+ return reject('台湾省');
99
+ }
100
+ break;
101
+ default:
102
+ break;
103
+ }
104
+ setVerifyDisabled(false);
105
+ }
106
+
107
+ async function areaChange() {
108
+ if (!formState.phone) {
109
+ return;
110
+ }
111
+ phoneForm.value?.validateFields('phone');
112
+ }
113
+
114
+ async function sendVerifyCode() {
115
+ message.success('验证码已发送,请注意查收');
116
+ start();
117
+ }
118
+
119
+ async function submit() {
120
+ try {
121
+ await phoneForm.value?.validate();
122
+
123
+ // 实现手机号登录逻辑
124
+
125
+ if (formState.checked) {
126
+ localStorage.setItem('pubinfo_web_savedPhone', formState.phone);
127
+ }
128
+ else {
129
+ localStorage.removeItem('pubinfo_web_savedPhone');
130
+ }
131
+ message.success('登录成功');
132
+ router.push(route.query.redirect?.toString() ?? '/');
133
+ }
134
+ catch (error) {
135
+ }
136
+ }
137
+
138
+ onMounted(() => {
139
+ const phone = localStorage.getItem('pubinfo_web_savedPhone');
140
+ if (phone) {
141
+ formState.phone = phone;
142
+ setVerifyDisabled(false);
143
+ formState.checked = true as any;
144
+ }
145
+ });
146
+ </script>
147
+
148
+ <template>
149
+ <div class="phone-form" mt-40px>
150
+ <a-form
151
+ ref="phoneForm"
152
+ :model="formState"
153
+ autocomplete="off"
154
+ :rules="rules"
155
+ @finish="submit"
156
+ >
157
+ <a-form-item name="phone">
158
+ <a-input-group compact>
159
+ <a-form-item-rest>
160
+ <a-select
161
+ v-model:value="regionCode"
162
+ size="large"
163
+ style="width: 35%;"
164
+ :options="regionCodeOptins"
165
+ @change="areaChange"
166
+ />
167
+ </a-form-item-rest>
168
+ <a-input
169
+ v-model:value="formState.phone"
170
+ allow-clear
171
+ size="large"
172
+ placeholder="请输入手机号"
173
+ style="width: 65%;"
174
+ />
175
+ </a-input-group>
176
+ </a-form-item>
177
+
178
+ <a-form-item name="verify">
179
+ <a-input
180
+ v-model:value="formState.verify"
181
+ allow-clear
182
+ size="large"
183
+ placeholder="请输入验证码"
184
+ >
185
+ <template #suffix>
186
+ <a-button
187
+ size="small"
188
+ type="link"
189
+ :disabled="verifyDisabled"
190
+ >
191
+ <span v-if="!isStart" @click="sendVerifyCode">获取短信验证码</span>
192
+ <span v-else text="#8A8E92">{{ count }}秒后重新获取</span>
193
+ </a-button>
194
+ </template>
195
+ </a-input>
196
+ </a-form-item>
197
+ <a-form-item>
198
+ <Savephone v-model:checked="formState.checked" />
199
+ </a-form-item>
200
+ <a-form-item>
201
+ <a-button
202
+ class="h-48px!"
203
+ block
204
+ type="primary"
205
+ html-type="submit"
206
+ :disabled="!formState.checked"
207
+ >
208
+ 登录
209
+ </a-button>
210
+ </a-form-item>
211
+ </a-form>
212
+ </div>
213
+ </template>
@@ -0,0 +1,194 @@
1
+ <script setup lang="ts">
2
+ import type { FormInstance, Rule } from 'ant-design-vue/es/form';
3
+ import type { UnwrapRef } from 'vue';
4
+ import { useToggle } from '@vueuse/core';
5
+ import { message, Modal } from 'ant-design-vue';
6
+ import { RESPONSE_CODE, useUserStore } from 'pubinfo';
7
+ import { useCaptchas, useFocusElement } from '../composables';
8
+ import Useragreement from './Useragreement.vue';
9
+
10
+ defineOptions({
11
+ name: 'PasswordLogin',
12
+ });
13
+
14
+ interface FormState {
15
+ account: string
16
+ password: string
17
+ captchas: string
18
+ captchaHash: string
19
+ checked: boolean
20
+ }
21
+
22
+ interface HttpError {
23
+ code: RESPONSE_CODE
24
+ data: any
25
+ hint: string
26
+ msg: string
27
+ success: boolean
28
+ }
29
+
30
+ function isHttpError(error: unknown): error is HttpError {
31
+ return (error as HttpError).code !== undefined;
32
+ }
33
+
34
+ const userStore = useUserStore();
35
+ const { send, captcha, captchaHash } = useCaptchas();
36
+ const router = useRouter();
37
+ const route = useRoute();
38
+ const { focusElementDataId } = useFocusElement();
39
+
40
+ const loginForm = ref<FormInstance | null>(null);
41
+ const formState: UnwrapRef<FormState> = reactive({
42
+ account: '',
43
+ password: '',
44
+ captchas: '',
45
+ checked: true,
46
+ captchaHash,
47
+ });
48
+
49
+ const rules: Record<string, Rule[]> = {
50
+ account: [{ required: true, whitespace: true, message: '请输入账号' }],
51
+ password: [{ required: true, whitespace: true, message: '请输入密码' }],
52
+ captchas: [{ required: true, whitespace: true, message: '请输入验证码' }],
53
+ };
54
+
55
+ const [loading, setLoading] = useToggle(false);
56
+
57
+ async function onSubmit() {
58
+ if (loading.value) {
59
+ return;
60
+ }
61
+ setLoading(true);
62
+ try {
63
+ await loginForm.value?.validate();
64
+ await userStore.login(formState);
65
+ message.success('登录成功');
66
+ router.push(route.query.redirect?.toString() ?? '/');
67
+ }
68
+ catch (error) {
69
+ if (isHttpError(error)) {
70
+ const code = error.code;
71
+ switch (code) {
72
+ case RESPONSE_CODE.LOGINNAME_PASSWORD_WRONG:
73
+ case RESPONSE_CODE.CODE_OVERTIME:
74
+ case RESPONSE_CODE.CODE_WRONG:
75
+ send();
76
+ break;
77
+ case RESPONSE_CODE.CHANGE_INIT_PASSWORD:
78
+ case RESPONSE_CODE.PASSWORD_EXPIRED:
79
+ createChangePasswordModal(error.msg, error?.data?.accessToken);
80
+ break;
81
+ default:
82
+ break;
83
+ }
84
+ }
85
+ }
86
+ finally {
87
+ setLoading(false);
88
+ }
89
+ }
90
+
91
+ function createChangePasswordModal(msg: string, token: string) {
92
+ Modal.confirm({
93
+ title: '提示',
94
+ content() {
95
+ return msg;
96
+ },
97
+ okText: '修改密码',
98
+ cancelText: '暂不登录',
99
+ onOk() {
100
+ router.push({
101
+ name: 'ChangePassword',
102
+ params: {
103
+ changePassWordToken: token,
104
+ },
105
+ });
106
+ },
107
+ });
108
+ }
109
+ </script>
110
+
111
+ <template>
112
+ <div class="mt-10">
113
+ <AForm ref="loginForm" :model="formState" :rules="rules" @finish="onSubmit">
114
+ <a-form-item name="account">
115
+ <a-input
116
+ v-model:value="formState.account"
117
+ allow-clear
118
+ size="large"
119
+ placeholder="请输入登录账号"
120
+ data-id="account"
121
+ >
122
+ <template #prefix>
123
+ <PubinfoIcon v-if="focusElementDataId === 'account'" name="i-solar-user-bold" color="#1578FF" />
124
+ <PubinfoIcon v-else name="i-solar-user-broken" color="#AFB0B2" />
125
+ </template>
126
+ </a-input>
127
+ </a-form-item>
128
+ <a-form-item name="password">
129
+ <a-input-password
130
+ v-model:value="formState.password"
131
+ allow-clear
132
+ size="large"
133
+ placeholder="请输入登录密码"
134
+ visibility-toggle
135
+ data-id="password"
136
+ >
137
+ <template #prefix>
138
+ <PubinfoIcon v-if="focusElementDataId === 'password'" name="i-solar-lock-password-unlocked-bold" color="#1578FF" />
139
+ <PubinfoIcon v-else name="i-solar-lock-password-unlocked-broken" color="#AFB0B2" />
140
+ </template>
141
+ </a-input-password>
142
+ </a-form-item>
143
+ <a-form-item name="captchas">
144
+ <a-input
145
+ v-model:value="formState.captchas"
146
+ size="large"
147
+ allow-clear
148
+ placeholder="请输入验证码"
149
+ data-id="captchas"
150
+ >
151
+ <template #prefix>
152
+ <PubinfoIcon v-if="focusElementDataId === 'captchas'" name="i-solar-shield-check-bold" color="#1578FF" />
153
+ <PubinfoIcon v-else name="i-solar-shield-check-broken" color="#AFB0B2" />
154
+ </template>
155
+ <template #addonAfter>
156
+ <div
157
+ w-100px
158
+ h-38px
159
+ cursor-pointer
160
+ @click="send"
161
+ >
162
+ <img :src="captcha">
163
+ </div>
164
+ </template>
165
+ </a-input>
166
+ </a-form-item>
167
+ <a-form-item>
168
+ <Useragreement v-model:checked="formState.checked" />
169
+ </a-form-item>
170
+ <!-- 登录 -->
171
+ <a-form-item>
172
+ <div w-full h-48px>
173
+ <a-button
174
+ class="h-48px!"
175
+ type="primary"
176
+ html-type="submit"
177
+ block
178
+ :disabled="!formState.checked"
179
+ :loading="loading"
180
+ >
181
+ 登录
182
+ </a-button>
183
+ </div>
184
+ </a-form-item>
185
+ </AForm>
186
+ </div>
187
+ </template>
188
+
189
+ <style scoped>
190
+ :deep(.ant-input-group-addon) {
191
+ padding: 0 !important;
192
+ overflow: hidden;
193
+ }
194
+ </style>
@@ -0,0 +1,17 @@
1
+ <script setup lang="ts">
2
+ defineOptions({
3
+ name: 'Savephone',
4
+ });
5
+
6
+ const checked = defineModel(
7
+ 'checked',
8
+ {
9
+ type: Boolean, default: false,
10
+ });
11
+ </script>
12
+
13
+ <template>
14
+ <a-checkbox v-model:checked="checked">
15
+ 记住手机号
16
+ </a-checkbox>
17
+ </template>
@@ -0,0 +1,26 @@
1
+ <script setup lang="ts">
2
+ defineOptions({
3
+ name: 'Useragreement',
4
+ });
5
+
6
+ const checked = defineModel(
7
+ 'checked',
8
+ {
9
+ type: Boolean, default: true,
10
+ });
11
+
12
+ function jumpUseragreementPage() {
13
+ // 暂未实现用户协议、隐私政策
14
+ }
15
+ </script>
16
+
17
+ <template>
18
+ <a-checkbox v-model:checked="checked">
19
+ 我已阅读并同意<span
20
+ hover:underline
21
+ decoration-2
22
+ text="#1578FF"
23
+ @click.prevent="jumpUseragreementPage"
24
+ >用户协议、隐私政策</span>
25
+ </a-checkbox>
26
+ </template>
@@ -0,0 +1,84 @@
1
+ import type { Component, MaybeRef } from 'vue';
2
+ import { useActiveElement } from '@vueuse/core';
3
+ import { useRequest } from 'alova/client';
4
+ import { getAuthLoginValidCode } from '@/api/modules/auth';
5
+
6
+ interface LoginTabs {
7
+ title: string
8
+ component: Component
9
+ }
10
+
11
+ export function useLoginTabs() {
12
+ const tabs: LoginTabs[] = [
13
+ {
14
+ title: '账号登录',
15
+ component: defineAsyncComponent(() => import('./components/PasswordLogin.vue')),
16
+ },
17
+ {
18
+ title: '手机号登录',
19
+ component: defineAsyncComponent(() => import('./components/LoginWithPhone.vue')),
20
+ },
21
+ ];
22
+ const initialTab = ref(0);
23
+
24
+ function changeActiveTab(activeTab: number): void {
25
+ initialTab.value = activeTab;
26
+ }
27
+ return {
28
+ tabs,
29
+ initialTab,
30
+ changeActiveTab,
31
+ };
32
+ }
33
+
34
+ export function useCaptchas() {
35
+ const { send, onSuccess } = useRequest(getAuthLoginValidCode({ size: '100x40' }));
36
+ const captcha = ref('');
37
+ const captchaHash = ref('');
38
+
39
+ onSuccess(({ data }) => {
40
+ const { key, plaintext } = data.data!;
41
+
42
+ const imgUrl = `data:image/png;base64,${plaintext}`;
43
+ captcha.value = imgUrl;
44
+ captchaHash.value = key!;
45
+ });
46
+ return {
47
+ send,
48
+ captcha,
49
+ captchaHash,
50
+ };
51
+ }
52
+
53
+ export function useFocusElement() {
54
+ const activeElement = useActiveElement();
55
+ const focusElementDataId = computed(() =>
56
+ activeElement.value?.dataset?.id || 'null',
57
+ );
58
+ return {
59
+ focusElementDataId,
60
+ };
61
+ }
62
+
63
+ export function useCountdown(initialTime: MaybeRef<number> = 60) {
64
+ const count = ref(toValue(initialTime));
65
+ const isStart = computed(() => {
66
+ return toValue(count) !== toValue(initialTime);
67
+ });
68
+
69
+ function start() {
70
+ count.value--;
71
+ const timer = setInterval(() => {
72
+ count.value--;
73
+ if (count.value === 0) {
74
+ clearInterval(timer);
75
+ count.value = toValue(initialTime);
76
+ }
77
+ }, 1000);
78
+ }
79
+ return {
80
+ count,
81
+ start,
82
+ isStart,
83
+ };
84
+ }
@@ -0,0 +1,142 @@
1
+ <script setup lang="ts">
2
+ import LoginFrom from './components/LoginForm.vue';
3
+
4
+ defineOptions({
5
+ name: 'Login',
6
+ });
7
+
8
+ const title = ref(import.meta.env.VITE_APP_TITLE);
9
+
10
+ const designDescription = [
11
+ {
12
+ title: '快速',
13
+ description: '快速搭建技术中台系统',
14
+ },
15
+ {
16
+ title: '灵活',
17
+ description: '灵活配置系统功能',
18
+ },
19
+ {
20
+ title: '便捷',
21
+ description: '高效提升系统开发效率',
22
+ },
23
+ ];
24
+ </script>
25
+
26
+ <template>
27
+ <div class="warp">
28
+ <div class="warp-blank">
29
+ <div
30
+ hidden
31
+ smd:block
32
+ class="w-full pl-[5vw] pt-[7vh]"
33
+ >
34
+ <div class="logo-group">
35
+ <PubinfoIcon
36
+ name="logo"
37
+ color="transparent"
38
+ mr="10px"
39
+ :size="32"
40
+ />
41
+ <span text="black dark:[#C9D6EF]">{{ title }}</span>
42
+ </div>
43
+ <div class="title-group">
44
+ <span text="black dark:[#C9D6EF]">一体化技术架构 一站式技术支撑</span>
45
+ <div flex="~ row" mt="30px">
46
+ <div
47
+ v-for="(item, index) in designDescription"
48
+ :key="index"
49
+ w-84px
50
+ h-36px
51
+ bg="#2F6BFF"
52
+ rounded-20px
53
+ text="20px #F7F7F7 dark:[#fff]"
54
+ font-normal
55
+ flex="~ center"
56
+ mr="15px"
57
+ >
58
+ {{ item.title }}
59
+ </div>
60
+ </div>
61
+ </div>
62
+ </div>
63
+ </div>
64
+ <div
65
+ h-full
66
+ bg="[rgba(255,255,255,0.1)] smd:[rgba(255,255,255,1)] dark:[#0F1E3B]"
67
+ backdrop-blur-sm
68
+ w="full smd:1/2 xl:800px 2xl:900px"
69
+ transition-all
70
+ flex="~ col"
71
+ justify-between
72
+ pb="40px"
73
+ relative
74
+ >
75
+ <div
76
+ smd:hidden
77
+ flex="~ row center"
78
+ w-full
79
+ h-200px
80
+ absolute
81
+ disable="none"
82
+ >
83
+ <PubinfoIcon
84
+ name="logo"
85
+ color="transparent"
86
+ :size="42"
87
+ mr-15px
88
+ />
89
+ <span text="33px" font="bold">{{ title }}</span>
90
+ </div>
91
+ <div
92
+ w-full
93
+ flex="~"
94
+ justify-center
95
+ pt="20vh"
96
+ >
97
+ <LoginFrom />
98
+ </div>
99
+ </div>
100
+ </div>
101
+ </template>
102
+
103
+ <style scoped>
104
+ .warp {
105
+ --at-apply: relative flex flex-row w-full h-full overflow-hidden;
106
+ background: url("@/assets/images/login-bg.webp") no-repeat;
107
+ background-size: cover;
108
+ }
109
+
110
+ .warp .warp-blank {
111
+ --at-apply: flex-auto basis-0 overflow-hidden;
112
+ }
113
+
114
+ .warp .logo-group {
115
+ --at-apply: flex flex-row items-center h-33px mb-35px text-6 font-400 text-[#151e26];
116
+ }
117
+
118
+ .warp .title-group {
119
+ --at-apply: h-52px text-38px font-800 text-[#072347] tracking-[1.5px];
120
+ }
121
+
122
+ @media (width < 1024px) {
123
+ .warp {
124
+ background: url("@/assets/images/login-small.png") no-repeat left;
125
+ background-size: cover;
126
+ }
127
+ }
128
+
129
+ [data-theme="dark"] {
130
+ .warp {
131
+ background: url("@/assets/images/login-bg_dark.webp") no-repeat;
132
+ background-size: cover;
133
+ }
134
+
135
+ @media (width < 1024px) {
136
+ .warp {
137
+ background: url("@/assets/images/login-small_dark.webp") no-repeat left;
138
+ background-size: cover;
139
+ }
140
+ }
141
+ }
142
+ </style>
@@ -0,0 +1,3 @@
1
+ {
2
+ "extends": "./.pubinfo/tsconfig.app.json"
3
+ }
@@ -0,0 +1,17 @@
1
+ import { presetUnocss } from 'pubinfo/node';
2
+ import { defineConfig } from 'unocss';
3
+
4
+ export default defineConfig({
5
+ presets: [
6
+ presetUnocss(),
7
+ ],
8
+
9
+ content: {
10
+ pipeline: {
11
+ include: [
12
+ /\.(vue|svelte|[jt]sx|mdx?|astro|elm|php|phtml|html)($|\?)/,
13
+ 'src/routes/**/*.ts',
14
+ ],
15
+ },
16
+ },
17
+ });
@@ -0,0 +1,2 @@
1
+ packages:
2
+ - playground