create-young-proj 1.4.0 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/package.json +1 -1
  3. package/template-nuxt-admin/README.md +1 -1
  4. package/template-nuxt-admin/nuxt.config.ts +4 -5
  5. package/template-nuxt-admin/package.json +0 -1
  6. package/template-nuxt-admin/server/plugins/env.ts +8 -0
  7. package/template-nuxt-admin/yarn.lock +0 -5
  8. package/template-nuxt-mobile/.nvmrc +1 -0
  9. package/{template-vue-mobile → template-nuxt-mobile}/.vscode/extensions.json +5 -3
  10. package/template-nuxt-mobile/.vscode/settings.json +39 -0
  11. package/{template-vue-mobile → template-nuxt-mobile}/Dockerfile +12 -13
  12. package/template-nuxt-mobile/README.md +90 -0
  13. package/template-nuxt-mobile/_gitignore +26 -0
  14. package/template-nuxt-mobile/_npmrc +2 -0
  15. package/template-nuxt-mobile/_nvmrc +1 -0
  16. package/template-nuxt-mobile/app.vue +79 -0
  17. package/template-nuxt-mobile/boot.mjs +17 -0
  18. package/template-nuxt-mobile/components/young/CodeInput.vue +72 -0
  19. package/template-nuxt-mobile/composables/api.ts +57 -0
  20. package/template-nuxt-mobile/composables/apis/get.ts +22 -0
  21. package/template-nuxt-mobile/composables/apis/index.ts +8 -0
  22. package/template-nuxt-mobile/composables/apis/post.ts +39 -0
  23. package/template-nuxt-mobile/composables/share.ts +22 -0
  24. package/template-nuxt-mobile/composables/user.ts +48 -0
  25. package/template-nuxt-mobile/composables/utils.ts +80 -0
  26. package/template-nuxt-mobile/config/.devrc +2 -0
  27. package/template-nuxt-mobile/env.d.ts +65 -0
  28. package/template-nuxt-mobile/error.vue +69 -0
  29. package/template-nuxt-mobile/eslint.config.js +33 -0
  30. package/template-nuxt-mobile/layouts/blank.vue +11 -0
  31. package/template-nuxt-mobile/layouts/default.vue +32 -0
  32. package/template-nuxt-mobile/layouts/tabbar.vue +29 -0
  33. package/template-nuxt-mobile/middleware/auth.global.ts +34 -0
  34. package/template-nuxt-mobile/nuxt.config.ts +78 -0
  35. package/template-nuxt-mobile/package.json +40 -0
  36. package/template-nuxt-mobile/pages/base/login.vue +124 -0
  37. package/template-nuxt-mobile/pages/base/resetPasswd.vue +84 -0
  38. package/template-nuxt-mobile/pages/index.vue +22 -0
  39. package/template-nuxt-mobile/pages/my.vue +18 -0
  40. package/template-nuxt-mobile/pages/sub/[id].vue +21 -0
  41. package/template-nuxt-mobile/public/favicon.ico +0 -0
  42. package/template-nuxt-mobile/public/robots.txt +2 -0
  43. package/template-nuxt-mobile/server/plugins/init.ts +94 -0
  44. package/template-nuxt-mobile/server/routes/get/env.ts +13 -0
  45. package/template-nuxt-mobile/server/tsconfig.json +3 -0
  46. package/template-nuxt-mobile/server/utils/index.ts +12 -0
  47. package/template-nuxt-mobile/server/utils/proxy.ts +59 -0
  48. package/template-nuxt-mobile/tsconfig.json +4 -0
  49. package/template-nuxt-mobile/typings/system.d.ts +22 -0
  50. package/template-nuxt-mobile/uno.config.ts +40 -0
  51. package/template-nuxt-mobile/utils/tool.ts +153 -0
  52. package/template-nuxt-mobile/yarn.lock +7737 -0
  53. package/template-nuxt-website/README.md +3 -1
  54. package/template-nuxt-website/nuxt.config.ts +4 -5
  55. package/template-vitepress/Dockerfile +6 -2
  56. package/template-vue-mobile/.vscode/base.code-snippets +0 -24
  57. package/template-vue-mobile/.vscode/settings.json +0 -7
  58. package/template-vue-mobile/README.md +0 -71
  59. package/template-vue-mobile/_env +0 -6
  60. package/template-vue-mobile/_gitignore +0 -30
  61. package/template-vue-mobile/boot.mjs +0 -16
  62. package/template-vue-mobile/build/custom-plugin.ts +0 -30
  63. package/template-vue-mobile/build/index.ts +0 -7
  64. package/template-vue-mobile/build/plugins.ts +0 -68
  65. package/template-vue-mobile/config/.devrc +0 -2
  66. package/template-vue-mobile/index.html +0 -25
  67. package/template-vue-mobile/nitro.config.ts +0 -19
  68. package/template-vue-mobile/package.json +0 -48
  69. package/template-vue-mobile/plugins/env.ts +0 -26
  70. package/template-vue-mobile/public/vite.svg +0 -1
  71. package/template-vue-mobile/rome.json +0 -24
  72. package/template-vue-mobile/routes/[...all].ts +0 -11
  73. package/template-vue-mobile/routes/get/env.ts +0 -25
  74. package/template-vue-mobile/src/App.vue +0 -29
  75. package/template-vue-mobile/src/auto-components.d.ts +0 -24
  76. package/template-vue-mobile/src/auto-imports.d.ts +0 -289
  77. package/template-vue-mobile/src/components/Init.vue +0 -36
  78. package/template-vue-mobile/src/global.d.ts +0 -7
  79. package/template-vue-mobile/src/hooks/useVerifyCode.ts +0 -46
  80. package/template-vue-mobile/src/layouts/blank.vue +0 -9
  81. package/template-vue-mobile/src/layouts/default.vue +0 -27
  82. package/template-vue-mobile/src/layouts/sub.vue +0 -20
  83. package/template-vue-mobile/src/main.ts +0 -35
  84. package/template-vue-mobile/src/modules/1-router.ts +0 -40
  85. package/template-vue-mobile/src/modules/2-pinia.ts +0 -10
  86. package/template-vue-mobile/src/modules/3-net.ts +0 -46
  87. package/template-vue-mobile/src/modules/4-auth.ts +0 -64
  88. package/template-vue-mobile/src/views/[...all_404].vue +0 -557
  89. package/template-vue-mobile/src/views/base/login.vue +0 -110
  90. package/template-vue-mobile/src/views/base/resetPasswd.vue +0 -88
  91. package/template-vue-mobile/src/views/index.vue +0 -18
  92. package/template-vue-mobile/src/views/my.vue +0 -15
  93. package/template-vue-mobile/src/views/sub.vue +0 -18
  94. package/template-vue-mobile/src/vite-env.d.ts +0 -43
  95. package/template-vue-mobile/tsconfig.json +0 -21
  96. package/template-vue-mobile/tsconfig.node.json +0 -9
  97. package/template-vue-mobile/unocss.config.ts +0 -47
  98. package/template-vue-mobile/vite.config.ts +0 -32
  99. package/template-vue-mobile/yarn.lock +0 -4395
  100. /package/{template-vue-mobile → template-nuxt-mobile}/config/.onlinerc +0 -0
  101. /package/{template-vue-mobile → template-nuxt-mobile}/config/.testrc +0 -0
@@ -0,0 +1,48 @@
1
+ /*
2
+ * @Author: zhangyang
3
+ * @Date: 2023-06-21 12:03:42
4
+ * @LastEditTime: 2023-11-07 16:41:48
5
+ * @Description:
6
+ */
7
+ export const useUserStore = defineStore('useUserStore', () => {
8
+ const cookie = useLocalStorage<UserLoginRes>('token', {} as UserLoginRes)
9
+
10
+ const avatar = computed(() => cookie.value?.headimgurl)
11
+ const nick = computed(() => cookie.value?.nickname)
12
+ const userId = computed(() => cookie.value?.uuid)
13
+ const token = computed(() => {
14
+ if (!validateToken())
15
+ removeToken()
16
+
17
+ return cookie.value?.token
18
+ })
19
+ const hasLogin = computed(() => !!token.value)
20
+
21
+ const SaveFlag = useLocalStorage('n天免登', true)
22
+ const Exptime = useLocalStorage('token过期时间', 0)
23
+
24
+ function validateToken() {
25
+ const now = Date.now()
26
+ const exp = Exptime.value
27
+ return now < exp
28
+ }
29
+
30
+ function removeToken() {
31
+ cookie.value = undefined
32
+ }
33
+
34
+ return {
35
+ cookie,
36
+ hasLogin,
37
+ avatar,
38
+ nick,
39
+ token,
40
+ SaveFlag,
41
+ Exptime,
42
+ removeToken,
43
+ userId,
44
+ }
45
+ })
46
+
47
+ if (import.meta.hot)
48
+ import.meta.hot.accept(acceptHMRUpdate(useUserStore, import.meta.hot))
@@ -0,0 +1,80 @@
1
+ /*
2
+ * @Author: zhangyang
3
+ * @Date: 2022-08-12 16:31:53
4
+ * @LastEditTime: 2023-11-07 18:01:33
5
+ * @Description:
6
+ */
7
+ import { createVNode, render } from 'vue'
8
+
9
+ export function goDownload(android?: string, ios?: string) {
10
+ const { isApple } = useDevice()
11
+ if (isApple)
12
+ ios && window.open(ios)
13
+
14
+ else
15
+ android && window.open(android)
16
+ }
17
+
18
+ export function closeWindow() {
19
+ if (process.server)
20
+ return
21
+
22
+ // @ts-expect-error
23
+ if (typeof WeixinJSBridge !== 'undefined') {
24
+ // @ts-expect-error 微信浏览器关闭窗口
25
+ WeixinJSBridge.call('closeWindow')
26
+ }
27
+
28
+ // @ts-expect-error
29
+ if (typeof AlipayJSBridge !== 'undefined') {
30
+ // @ts-expect-error 支付宝浏览器关闭窗口
31
+ AlipayJSBridge.call('popWindow')
32
+ }
33
+ // 其他关闭窗口
34
+ window.close()
35
+ }
36
+
37
+ export async function showLoading() {
38
+ if (process.server)
39
+ return
40
+
41
+ showLoadingToast({
42
+ message: '加载中...',
43
+ forbidClick: true,
44
+ })
45
+ }
46
+
47
+ export function hideLoading() {
48
+ if (process.server)
49
+ return
50
+
51
+ closeToast()
52
+ }
53
+ /**
54
+ * 异步插入组件
55
+ * @param El 组件实例
56
+ */
57
+ export function injectComponent(El: ReturnType<typeof defineComponent>) {
58
+ if (process.server)
59
+ return false
60
+
61
+ const appendTo = document.createElement('div')
62
+
63
+ const onDestroy = () => {
64
+ try {
65
+ render(null, appendTo)
66
+ }
67
+ catch (error) {
68
+ console.info('remove child error', error)
69
+ }
70
+ }
71
+
72
+ const vnode = createVNode(El, {
73
+ onDestroy,
74
+ })
75
+ render(vnode, appendTo)
76
+
77
+ appendTo.firstElementChild && document.body.appendChild(appendTo.firstElementChild)
78
+
79
+ return onDestroy
80
+ }
@@ -0,0 +1,2 @@
1
+ VITE_TITLE = Mobile-dev
2
+ VITE_VCONSOLE = true
@@ -0,0 +1,65 @@
1
+ /*
2
+ * @Author: zhangyang
3
+ * @Date: 2023-05-26 11:50:06
4
+ * @LastEditTime: 2023-11-29 17:21:16
5
+ * @Description:
6
+ */
7
+ /**
8
+ * 环境变量
9
+ */
10
+ interface ImportMetaEnv {
11
+ /**
12
+ * 微信公众号 appid,用于网页授权登录,SDK 签名等
13
+ */
14
+ VITE_WECHAT_APPID: string
15
+
16
+ /**
17
+ * 默认标题
18
+ */
19
+ VITE_TITLE: string
20
+
21
+ /**
22
+ * 调试控制台
23
+ */
24
+ VITE_VCONSOLE?: boolean
25
+ };
26
+
27
+ declare interface Window {
28
+ /**
29
+ * 注入到前端使用的环境变量
30
+ */
31
+ __YOUNG_ENV__: ImportMetaEnv
32
+ };
33
+
34
+ declare module '#app' {
35
+ interface PageMeta {
36
+ /**
37
+ * 顶部标题
38
+ */
39
+ title?: string
40
+ /**
41
+ * 不受 container 的宽度限制
42
+ */
43
+ full?: boolean
44
+ /**
45
+ * main 背景色
46
+ */
47
+ bgColor?: string
48
+ }
49
+ }
50
+
51
+ /**
52
+ * 路由元数据类型扩展
53
+ */
54
+ declare module 'vue-router' {
55
+ interface RouteMeta {
56
+ /**
57
+ * 页面标题
58
+ */
59
+ title: string
60
+ /**
61
+ * 默认需要登录,显示设置为 false 则无需登录
62
+ */
63
+ auth?: boolean
64
+ }
65
+ }
@@ -0,0 +1,69 @@
1
+ <!--
2
+ * @Author: zhangyang
3
+ * @Date: 2023-06-12 15:00:07
4
+ * @LastEditTime: 2023-10-30 14:34:37
5
+ * @Description:
6
+ -->
7
+ <script lang="ts" setup>
8
+ import type { NuxtError } from '#app'
9
+
10
+ const props = defineProps<{
11
+ error: NuxtError
12
+ }>()
13
+
14
+ let timer: NodeJS.Timeout
15
+
16
+ const i = ref(5)
17
+
18
+ function clear() {
19
+ clearError()
20
+ clearInterval(timer)
21
+ location.replace('/')
22
+ }
23
+
24
+ onMounted(() => {
25
+ if (process.server)
26
+ return
27
+
28
+ const isDev = process.dev || window.__YOUNG_ENV__
29
+ ? !!window.__YOUNG_ENV__.VITE_VCONSOLE
30
+ : location.href.includes('-dev-')
31
+ || location.href.includes('-test-')
32
+
33
+ console.log(props.error)
34
+ if (isDev) {
35
+ console.log('开发模式,不自动清理')
36
+ const srcEl = document.createElement('script')
37
+ srcEl.src = '//g2021-cdn.laiyouxi.com/image/21Store/admin-img/vconsole.min.js'
38
+ document.body.appendChild(srcEl)
39
+ // @ts-expect-error
40
+ // eslint-disable-next-line no-new
41
+ new VConsole()
42
+ console.log(props.error)
43
+ return
44
+ }
45
+ timer = setInterval(() => {
46
+ i.value--
47
+ if (i.value === 0)
48
+ clear()
49
+ }, 1e3)
50
+ })
51
+ </script>
52
+
53
+ <template>
54
+ <div class="flex w-full h-full flex-col justify-center items-center">
55
+ <div class="text-5xl my-6">
56
+ {{ error.statusCode }}
57
+ </div>
58
+ <div class="text-lg my-2">
59
+ {{ error.statusMessage || error.message || '出错啦~' }}
60
+ </div>
61
+
62
+ <div class="mb-2">
63
+ {{ i }}s 后自动跳转首页
64
+ </div>
65
+ <ElButton @click="clear">
66
+ 立即返回首页
67
+ </ElButton>
68
+ </div>
69
+ </template>
@@ -0,0 +1,33 @@
1
+ /*
2
+ * @Author: zhangyang
3
+ * @Date: 2023-11-01 10:13:50
4
+ * @LastEditTime: 2023-11-01 11:11:55
5
+ * @Description:
6
+ */
7
+ import antfu from '@antfu/eslint-config'
8
+
9
+ export default antfu({
10
+ overrides: {
11
+ vue: {
12
+ 'vue/valid-v-model': 'off',
13
+ },
14
+ typescript: {
15
+ 'ts/no-use-before-define': 'off',
16
+ 'ts/no-unused-vars': 'off',
17
+ 'ts/ban-types': 'off',
18
+ 'ts/ban-ts-comment': 'off',
19
+ },
20
+ },
21
+ rules: {
22
+ 'no-console': 'off',
23
+ 'node/prefer-global/process': 'off',
24
+ 'unused-imports/no-unused-vars': 'off',
25
+ 'no-throw-literal': 'off',
26
+ 'antfu/consistent-list-newline': 'off',
27
+ 'style/jsx-indent': 'off',
28
+ },
29
+ }, {
30
+ ignores: [
31
+ 'public',
32
+ ],
33
+ })
@@ -0,0 +1,11 @@
1
+ <!--
2
+ * @Author: zhangyang
3
+ * @Date: 2023-11-29 17:09:54
4
+ * @LastEditTime: 2023-11-29 17:09:54
5
+ * @Description:
6
+ -->
7
+ <template>
8
+ <div lg="max-w-800px mx-auto">
9
+ <NuxtPage />
10
+ </div>
11
+ </template>
@@ -0,0 +1,32 @@
1
+ <!--
2
+ * @Author: zhangyang
3
+ * @Date: 2023-09-27 14:15:48
4
+ * @LastEditTime: 2023-11-29 17:19:17
5
+ * @Description:
6
+ -->
7
+ <script lang="ts" setup>
8
+ const route = useRoute()
9
+ const title = computed(() => route.meta.title || import.meta.env.VITE_TITLE)
10
+
11
+ const router = useRouter()
12
+ function backFn() {
13
+ if (history.length === 1) {
14
+ console.log('扫码直接进入, 返回直接回首页')
15
+ router.replace('/')
16
+ }
17
+ else {
18
+ router.back()
19
+ }
20
+ }
21
+ </script>
22
+
23
+ <template>
24
+ <div lg="max-w-800px mx-auto">
25
+ <VanNavBar :border="false" left-arrow @click-left="backFn">
26
+ <template #title>
27
+ <span class="text-[#FFFFFF] text-16px font-700">{{ title }}</span>
28
+ </template>
29
+ </VanNavBar>
30
+ <NuxtPage v-bind="$attrs" />
31
+ </div>
32
+ </template>
@@ -0,0 +1,29 @@
1
+ <!--
2
+ * @Author: zhangyang
3
+ * @Date: 2023-11-29 17:11:40
4
+ * @LastEditTime: 2023-11-29 17:15:39
5
+ * @Description:
6
+ -->
7
+ <script lang="ts" setup>
8
+ const route = useRoute()
9
+ const title = computed(() => route.meta.title || import.meta.env.VITE_TITLE)
10
+ </script>
11
+
12
+ <template>
13
+ <div lg="max-w-800px mx-auto">
14
+ <VanNavBar :border="false">
15
+ <template #title>
16
+ <span class="text-[#FFFFFF] text-16px font-700">{{ title }}</span>
17
+ </template>
18
+ </VanNavBar>
19
+ <NuxtPage v-bind="$attrs" />
20
+ <VanTabbar route lg="!left-50% transform max-w-800px -translate-x-50%">
21
+ <VanTabbarItem to="/" icon="home-o">
22
+ <span>首页</span>
23
+ </VanTabbarItem>
24
+ <VanTabbarItem to="/my" icon="user-o">
25
+ <span>我的</span>
26
+ </VanTabbarItem>
27
+ </VanTabbar>
28
+ </div>
29
+ </template>
@@ -0,0 +1,34 @@
1
+ /*
2
+ * @Author: zhangyang
3
+ * @Date: 2023-11-29 17:22:28
4
+ * @LastEditTime: 2023-11-29 17:56:58
5
+ * @Description:
6
+ */
7
+ export default defineNuxtRouteMiddleware(async (to, from) => {
8
+ if (to.matched.length === 0)
9
+ throw createError({ message: '页面不存在', statusCode: 404 })
10
+
11
+ const { hasLogin } = storeToRefs(useUserStore())
12
+ console.log('🚀 ~ file: auth.global.ts:12 ~ defineNuxtRouteMiddleware ~ hasLogin:', hasLogin.value)
13
+
14
+ const changeTitle = () => {
15
+ document.title = (to.meta.title as string) || window.__YOUNG_ENV__.VITE_TITLE
16
+ }
17
+
18
+ // 页面无需登录
19
+ if (to.meta.auth === false || hasLogin.value) {
20
+ changeTitle()
21
+ return
22
+ }
23
+
24
+ if (!hasLogin.value && to.path !== '/base/login') {
25
+ // 页面需要登录,但是未登录
26
+ showNotify({
27
+ message: '未登录或登录过期,请重新登录',
28
+ type: 'danger',
29
+ })
30
+ return navigateTo(`/base/login?redirect=${encodeURIComponent(to.fullPath)}`, {
31
+ replace: true,
32
+ })
33
+ }
34
+ })
@@ -0,0 +1,78 @@
1
+ /*
2
+ * @Author: zhangyang
3
+ * @Date: 2023-09-21 15:57:55
4
+ * @LastEditTime: 2023-11-29 16:51:03
5
+ * @Description:
6
+ */
7
+ import { resolve } from 'node:path'
8
+
9
+ // https://nuxt.com/docs/api/configuration/nuxt-config
10
+ export default defineNuxtConfig({
11
+ ssr: false,
12
+ app: {
13
+ head: {
14
+ htmlAttrs: {
15
+ lang: 'zh-CN',
16
+ },
17
+ viewport: 'width=device-width,initial-scale=1.0,user-scalable=no,shrink-to-fit=no',
18
+ meta: [
19
+ { name: 'theme-color', content: '#ffffff' },
20
+ { name: 'screen-orientation', content: 'portrait' },
21
+ { name: 'x5-orientation', content: 'portrait' },
22
+ { name: 'renderer', content: 'webkit' },
23
+ { name: 'mobile-web-app-capable', content: 'yes' },
24
+ { name: 'creator', content: 'BluesYoung-web' },
25
+ { 'http-equiv': 'X-UA-Compatible', 'content': 'IE=edge,chrome=1' },
26
+ { id: 'viewportMeta', name: 'viewport', content: 'maximum-scale=1.0,minimum-scale=1.0,user-scalable=0,width=device-width,initial-scale=1.0' },
27
+ { name: 'format-detection', content: 'telephone=no,email=no,date=no,address=no' },
28
+ ],
29
+ script: [
30
+ {
31
+ innerHTML: `
32
+ this.globalThis || (this.globalThis = this);
33
+ window.onerror = function(e) {
34
+ console.log(e)
35
+ window.alert('您的浏览器版本过低,请尝试使用其他浏览器或将浏览器升级至最新版本后重试!');
36
+ }
37
+ `,
38
+ },
39
+ ],
40
+ link: [
41
+ { rel: 'icon', type: 'image/png', href: '/favicon.ico' },
42
+ { rel: 'apple-touch-icon', type: 'image/png', href: '/favicon.ico' },
43
+ { rel: 'apple-touch-icon-precomposed', type: 'image/png', href: '/favicon.ico' },
44
+ ],
45
+ },
46
+ },
47
+ devtools: { enabled: false },
48
+ modules: [
49
+ '@nuxtjs/device',
50
+ '@pinia/nuxt',
51
+ '@unocss/nuxt',
52
+ '@vueuse/nuxt',
53
+ '@element-plus/nuxt',
54
+ '@vant/nuxt',
55
+ ],
56
+ unocss: {
57
+ // 默认的 @unocss/reset/tailwind.css 会造成 element 样式异常
58
+ // 手动注入 @unocss/reset/tailwind-compat.css
59
+ preflight: false,
60
+ },
61
+
62
+ nitro: {
63
+ output: {
64
+ dir: resolve(__dirname, './dist'),
65
+ },
66
+ sourceMap: false,
67
+ minify: true,
68
+ },
69
+
70
+ vite: {
71
+ build: {
72
+ sourcemap: false,
73
+ // 兼容旧浏览器
74
+ target: ['chrome58'],
75
+ cssTarget: ['chrome58'],
76
+ },
77
+ },
78
+ })
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "template-nuxt-mobile",
3
+ "type": "module",
4
+ "version": "0.0.0",
5
+ "private": true,
6
+ "scripts": {
7
+ "build": "nuxt build",
8
+ "dev": "nuxt dev",
9
+ "generate": "nuxt generate",
10
+ "preview": "nuxt preview",
11
+ "postinstall": "nuxt prepare",
12
+ "lint": "eslint .",
13
+ "lint:fix": "eslint . --fix"
14
+ },
15
+ "dependencies": {
16
+ "@bluesyoung/http": "^1.2.0",
17
+ "@bluesyoung/logger": "^0.0.1",
18
+ "@bluesyoung/utils": "^0.2.1",
19
+ "c12": "^1.4.2",
20
+ "http-proxy": "^1.18.1"
21
+ },
22
+ "devDependencies": {
23
+ "@antfu/eslint-config": "^1.1.0",
24
+ "@bluesyoung/ui-vue3": "^0.1.1",
25
+ "@bluesyoung/ui-vue3-element-plus": "^1.3.7",
26
+ "@element-plus/nuxt": "^1.0.6",
27
+ "@iconify/json": "^2.2.135",
28
+ "@nuxtjs/device": "^3.1.1",
29
+ "@pinia/nuxt": "^0.5.1",
30
+ "@unocss/nuxt": "^0.57.1",
31
+ "@vant/nuxt": "^1.0.3",
32
+ "@vueuse/nuxt": "^10.5.0",
33
+ "element-plus": "^2.3.14",
34
+ "eslint": "^8.51.0",
35
+ "nuxt": "3.5.1",
36
+ "sass": "^1.69.5",
37
+ "typescript": "^5.2.2",
38
+ "vant": "^4.7.3"
39
+ }
40
+ }
@@ -0,0 +1,124 @@
1
+ <!--
2
+ * @Author: zhangyang
3
+ * @Date: 2023-11-29 16:30:21
4
+ * @LastEditTime: 2023-11-30 09:41:00
5
+ * @Description:
6
+ -->
7
+ <script lang="ts" setup>
8
+ import { isMobile } from '@bluesyoung/utils'
9
+ import type { FormInstance } from 'vant'
10
+
11
+ definePageMeta({
12
+ title: '登录',
13
+ layout: 'blank',
14
+ auth: false,
15
+ })
16
+
17
+ const formRef = ref<FormInstance>()
18
+ const router = useRouter()
19
+ const route = useRoute()
20
+ const isPass = ref(true)
21
+ const form = ref<LoginForm>({ tel: '', passwd: '' })
22
+
23
+ const isReaded = ref(true)
24
+ const showPopup = ref(false)
25
+ const popupContent = ref(`<h1>我是用户协议</h1>`)
26
+
27
+ enum ProtoType {
28
+ 用户协议,
29
+ 隐私政策,
30
+ }
31
+
32
+ function read(type: ProtoType) {
33
+ if (type === ProtoType.用户协议)
34
+ popupContent.value = `<h1>我是用户协议</h1>`
35
+
36
+ else
37
+ popupContent.value = `<h1>我是隐私政策</h1>`
38
+
39
+ showPopup.value = true
40
+ }
41
+ const [showPass, toggle] = useToggle(false)
42
+
43
+ watch(() => isPass.value, () => form.value.passwd = '')
44
+
45
+ const { cookie, Exptime, hasLogin } = storeToRefs(useUserStore())
46
+
47
+ async function loginHandler() {
48
+ try {
49
+ await formRef.value?.validate()
50
+ if (!isReaded.value) {
51
+ showToast('请先同意服务协议!')
52
+ return
53
+ }
54
+ const data = await apis.post.login(form.value)
55
+ console.log('🚀 ~ file: login.vue:55 ~ loginHandler ~ data:', data)
56
+ if (data) {
57
+ cookie.value = data
58
+ // 三天后过期
59
+ Exptime.value = Date.now() + 1000 * 60 * 60 * 24 * 3
60
+ showSuccessToast('登录成功!')
61
+
62
+ nextTick(() => {
63
+ if (route.query.redirect)
64
+ navigateTo({ path: route.query.redirect as string })
65
+ else
66
+ navigateTo('/')
67
+ })
68
+ }
69
+ }
70
+ catch (error) {
71
+
72
+ }
73
+ }
74
+
75
+ onMounted(() => {
76
+ hasLogin.value && navigateTo('/', {
77
+ replace: true,
78
+ })
79
+ })
80
+ </script>
81
+
82
+ <template>
83
+ <VanNavBar title="登录" safe-area-inset-bottom />
84
+ <div v-bind="$attrs" class="w-full h-full mt-10">
85
+ <VanForm ref="formRef">
86
+ <VanCellGroup inset>
87
+ <VanField
88
+ v-model.trim="form.tel" maxlength="11" placeholder="手机号" :rules="[
89
+ { required: true, message: '请填写手机号' },
90
+ { validator: isMobile, message: '请输入合法的手机号' },
91
+ ]"
92
+ />
93
+ <VanField
94
+ v-if="isPass" v-model.trim="form.passwd" placeholder="密码" :type="showPass ? 'password' : 'text'"
95
+ :rules="[{ required: true, message: '请填写密码' }]"
96
+ >
97
+ <template #right-icon>
98
+ <VanIcon :name="!showPass ? 'eye' : 'closed-eye'" @click="toggle(!showPass)" />
99
+ </template>
100
+ </VanField>
101
+ <YoungCodeInput v-else v-model.trim="form.passwd" :tel="form.tel" placeholder="验证码" :rules="[{ required: true, message: '请填写验证码' }]" />
102
+ </VanCellGroup>
103
+ <VanCheckbox v-model="isReaded" shape="square" icon-size="small" class="p-4">
104
+ 我已阅读并同意
105
+ <span class=" text-purple-500" @click.prevent="read(ProtoType.用户协议)">用户协议</span>
106
+
107
+ <span class=" text-purple-500" @click.prevent="read(ProtoType.隐私政策)">隐私政策</span>
108
+ </VanCheckbox>
109
+ <div class="m-4">
110
+ <VanButton block type="primary" @click="loginHandler">
111
+ 登录
112
+ </VanButton>
113
+ </div>
114
+ <div class="flex justify-between p-4">
115
+ <span v-if="isPass" class="text-purple-500 cursor-pointer" @click.prevent="isPass = false">短信登录</span>
116
+ <span v-if="isPass" class="text-purple-500 cursor-pointer" @click.prevent="$router.push('/base/resetPasswd')">找回密码</span>
117
+ <span v-else class="text-purple-500 cursor-pointer" @click.prevent="isPass = true">密码登录</span>
118
+ </div>
119
+ </VanForm>
120
+ <VanPopup v-model:show="showPopup" position="center" :style="{ height: '80%', width: '90%', overflow: 'auto' }">
121
+ <div v-html="popupContent" />
122
+ </VanPopup>
123
+ </div>
124
+ </template>