Browse Source

优化会议登录

master
科技小王子 9 months ago
parent
commit
2ca30a6146
  1. 4
      api/system/user/model/index.ts
  2. 14
      components/AppFooter.vue
  3. 24
      components/AppHeader.vue
  4. 2
      components/UnderMaintenance.vue
  5. 61
      components/Upload.vue
  6. 58
      components/UserMenu.vue
  7. 18
      composables/configState.ts
  8. 9
      composables/useServerRequest.ts
  9. 5
      layouts/default.vue
  10. 8
      nuxt.config.ts
  11. 3
      package.json
  12. 79
      pages/_____bak/[custom].vue
  13. 53
      pages/_____bak/a/index.vue
  14. 97
      pages/_____bak/customs/index.vue
  15. 274
      pages/_____bak/index_.vue
  16. 80
      pages/_____bak/page/[id].vue
  17. 67
      pages/_____bak/spm/[spm].vue
  18. 2
      pages/index.vue
  19. 183
      pages/login.vue
  20. 184
      pages/user/auth.vue
  21. 170
      pages/user/index.vue
  22. 13
      pages/user/logout.vue
  23. 104
      pages/user/order.vue
  24. 161
      pages/user/password.vue
  25. 6
      plugins/element-ui.js
  26. 261
      pnpm-lock.yaml

4
api/system/user/model/index.ts

@ -1,7 +1,6 @@
import type { PageParam } from '@/api';
import type { Role } from '../../role/model';
import type { Menu } from '../../menu/model';
import { Company } from '@/api/system/company/model';
/**
*
@ -16,6 +15,7 @@ export interface User {
// 密码
password?: string;
password2?: string;
oldPassword?: string;
// 昵称
nickname?: string;
openId?: string;
@ -75,7 +75,7 @@ export interface User {
tenantName?: string;
logo?: string;
companyId?: number;
companyInfo?: Company;
companyInfo?: any;
planId?: number;
code?: string;
smsCode?: string;

14
components/AppFooter.vue

@ -1,5 +1,5 @@
<template>
<footer class="overflow-hidden">
<footer class="overflow-hidden" v-if="!getPath().startsWith('/login')">
<div class="sm:h-[100px] h-[50px]"></div>
<div class=" bg-white my-3 w-3/4 m-auto" v-if="config.copyrightForDemoData">
<el-alert :title="config.copyrightForDemoData" center show-icon type="warning" />
@ -60,8 +60,6 @@
<a class="text-gray-400 hover:text-gray-400" href="https://beian.miit.gov.cn/" target="_blank"> 备案号{{ config?.icpNo }}</a>
</div>
<div class="tools flex gap-xl items-center opacity-80 hover:opacity-90">
<!-- <a href="https://github.com" class="sm:flex hidden items-center" target="_blank"><img src="@/assets/svg/github-mark-white.svg" alt="github" width="20" class="text-gray-400" /></a>-->
<!-- <a href="https://github.com" class="sm:hidden flex items-center" target="_blank"><img src="@/assets/svg/github-mark.svg" alt="github" width="20" class="text-gray-400" /></a>-->
<el-tooltip :content="`管理后台`" v-if="config.showAdminIcon">
<a :href="`https://${website.tenantId}.websoft.top`" target="_blank"><img src="@/assets/svg/websoft-mark-white.svg" alt="github" width="28" class="text-gray-400" /></a>
</el-tooltip>
@ -69,12 +67,20 @@
</div>
</div>
</footer>
<footer class="absolute bottom-0 w-full" v-if="getPath().startsWith('/login')">
<div class="w-full md:w-screen-xl w-full m-auto flex sm:flex-row flex-col-reverse sm:justify-center justify-center items-center sm:py-10 pt-6 pb-6 text-center p-2">
<div class="text-gray-400 sm:gap-xl leading-7 flex flex-col sm:flex-row">
<span>Copyright © {{ new Date().getFullYear() }} {{ config?.copyright }}</span>
<a class="text-gray-400 hover:text-gray-400" href="https://beian.miit.gov.cn/" target="_blank"> 备案号{{ config?.icpNo }}</a>
</div>
</div>
</footer>
<el-backtop></el-backtop>
</template>
<script setup lang="ts">
//
import {useConfigInfo} from "#imports";
import {getPath, useConfigInfo} from "#imports";
import { UserOutlined } from '@ant-design/icons-vue';
import {useSubMenu, useWebsite} from "~/composables/configState";
import Breadcrumb from "~/components/Breadcrumb.vue";

24
components/AppHeader.vue

@ -70,11 +70,12 @@
<ClientOnly>
<template v-if="token">
<el-dropdown @command="handleCommand">
<el-avatar class="cursor-pointer" :src="userInfo?.avatar" :size="30" />
<el-button circle :icon="ElIconUserFilled" color="#155FAA"></el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="user">个人中心</el-dropdown-item>
<el-dropdown-item divided command="logOut">退出</el-dropdown-item>
<el-dropdown-item divided command="logOut" @click="navigateTo('/user/logout')">退出</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
@ -98,15 +99,22 @@
useMenu,
useProductAffix,
useShowLogin,
useToken,
useToken, useUser,
useWebsite
} from "~/composables/configState";
import UnderMaintenance from "~/components/UnderMaintenance.vue";
import {useServerRequest} from "~/composables/useServerRequest";
import type {ApiResult} from "~/api";
import type {User} from "~/api/system/user/model";
import {navigateTo} from "#imports";
const route = useRoute();
//
const runtimeConfig = useRuntimeConfig();
const website = useWebsite()
const showPassport = ref<boolean>(false);
const searchValue = ref<string>();
const token = useToken();
const userInfo = ref<User>();
const showLogin = useShowLogin();
const navigations = useMenu();
const config = useConfigInfo();
@ -116,7 +124,7 @@
const visibleNumber = ref<number>(6);
config.value.elMenuMaxNumber = 8;
// index
const currentIndex = ref<string>('2');
const currentIndex = ref<string>('/');
function handleCommand(command: string) {
switch (command) {
@ -138,10 +146,6 @@
navigateTo('/login')
}
const reload = () => {
}
const handleSearch = (key: string, keyPath: string) => {
console.log(key, keyPath);
navigateTo('/search?keyword=' + searchValue.value);
@ -154,6 +158,12 @@
navigateTo(`${key}`);
}
const reload = async () => {
const {data: response} = await useServerRequest<ApiResult<User>>('/auth/user',{baseURL: runtimeConfig.public.apiServer})
userInfo.value = response.value?.data;
}
reload();
</script>
<style lang="scss">

2
components/UnderMaintenance.vue

@ -4,7 +4,7 @@
<el-result
:icon="website.statusIcon || 'info'"
:title="`${website.statusName || '404'}`"
:sub-title="website.statusText || '页面找不到了 :('"
:sub-title="website.statusText || '链接失败,请检查您的网络或与网站管理员联系'"
>
<template #extra>
<el-button type="primary" v-if="website.statusUrl" @click="navigateTo(`${website.statusUrl}`)">{{ website.statusBtnText }}</el-button>

61
components/Upload.vue

@ -0,0 +1,61 @@
<template>
<el-upload action="#" list-type="picture-card" :auto-upload="false">
<el-icon><Plus /></el-icon>
<template #file="{ file }">
<div>
<img class="el-upload-list__item-thumbnail" :src="file.url" alt="" />
<span class="el-upload-list__item-actions">
<span
class="el-upload-list__item-preview"
@click="handlePictureCardPreview(file)"
>
<el-icon><zoom-in /></el-icon>
</span>
<span
v-if="!disabled"
class="el-upload-list__item-delete"
@click="handleDownload(file)"
>
<el-icon><Download /></el-icon>
</span>
<span
v-if="!disabled"
class="el-upload-list__item-delete"
@click="handleRemove(file)"
>
<el-icon><Delete /></el-icon>
</span>
</span>
</div>
</template>
</el-upload>
<el-dialog v-model="dialogVisible">
<img :src="dialogImageUrl" alt="Preview Image" />
</el-dialog>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import { Delete, Download, Plus, ZoomIn } from '@element-plus/icons-vue'
import type { UploadFile } from 'element-plus'
const dialogImageUrl = ref('')
const dialogVisible = ref(false)
const disabled = ref(false)
const handleRemove = (file: UploadFile) => {
console.log(file)
}
const handlePictureCardPreview = (file: UploadFile) => {
dialogImageUrl.value = file.url!
dialogVisible.value = true
}
const handleDownload = (file: UploadFile) => {
console.log(file)
}
</script>

58
components/UserMenu.vue

@ -0,0 +1,58 @@
<template>
<el-menu
class="full-width-menu"
style="border: none; width: 100%"
:default-active="activeIndex"
@select="handleSelect"
>
<el-menu-item v-for="(item,index) in activities" :index="`${item.path}`" :key="index" class="sm:w-[170px] w-full" style="border-bottom: 1px solid #f3f3f3;">
<span class="text-center w-full" @click="navigateTo(item.path)">{{ item.name }}</span>
</el-menu-item>
</el-menu>
</template>
<script setup lang="ts">
import {navigateTo} from "#imports";
withDefaults(
defineProps<{
layout?: any;
activeIndex?: string;
}>(),
{}
);
const emit = defineEmits<{
(e: 'done', index: string): void;
(e: 'update:visible', visible: boolean): void;
}>();
const activities = [
{
name: '账号信息',
path: '/user'
},
{
name: '密码修改',
path: '/user/password'
},
{
name: '实名认证',
path: '/user/auth'
},
{
name: '订单列表',
path: '/user/order'
},
{
name: '退出登录',
path: '/user/logout'
},
]
const handleSelect = (index: string) => {
emit('done', index)
}
</script>

18
composables/configState.ts

@ -43,13 +43,17 @@ export const useProductAffix = () =>
export const useToken = () => useState('token', () => '');
// 用户信息
export const useUserInfo = () => useState('token', () => {
userId: 0;
nickname: '昵称';
username: '用户名';
phone: '手机号码';
email: '';
});
export const useUser = () =>
useState<User>('user', () => {
return {
userId: 0,
avatar: '',
phone: '',
nickname: '',
gradeId: 0,
gradeName: ''
};
});
// 是否显示登录弹窗
export const useShowLogin = () => useState('showLogin',() => false)

9
composables/useServerRequest.ts

@ -15,13 +15,20 @@ export const useServerRequest = <T>(url: string, opts?: UseFetchOptions<T, unkno
const baseUrl = ref('');
// 获取 Cookie
const token = useCookie('token');
console.log(token.value);
if(localStorage.getItem('token')){
const tokenStr = localStorage.getItem('token');
if(tokenStr){
token.value = JSON.parse(tokenStr)
console.log(token.value,'tokenStr')
}
}
baseUrl.value = runtimeConfig.public.apiBase;
// 开发环境
if(process.env.NODE_ENV === 'development'){
baseUrl.value = `${runtimeConfig.public.apiDev}`
}
const defaultOptions: UseFetchOptions<unknown> = {
baseURL: baseUrl.value,
onRequest({ options }) {

5
layouts/default.vue

@ -15,7 +15,7 @@
import {useServerRequest} from "~/composables/useServerRequest";
import type {ApiResult} from "~/api";
import type {Domain} from "~/api/cms/domain/model";
import {useConfigInfo, useForm, useMenu, useSubMenu, useWebsite} from "~/composables/configState";
import {useConfigInfo, useForm, useMenu, useSubMenu, useToken, useWebsite} from "~/composables/configState";
import type {Website} from "~/api/cms/website/model";
import type {Navigation} from "~/api/cms/navigation/model";
import type {Config} from "~/types/global";
@ -25,8 +25,9 @@
//
const loading = ref<boolean>(false)
const website = useWebsite()
const config = useConfigInfo();
const config = useConfigInfo()
const menu = useMenu()
const token = useToken()
const subMenu = useSubMenu()
//

8
nuxt.config.ts

@ -6,9 +6,12 @@ export default defineNuxtConfig({
css: [
'element-plus/dist/index.css',
'element-plus/theme-chalk/display.css',
'@/assets/css/main.css',
'@/assets/css/main.css'
],
ssr: false,
plugins: [
'@/plugins/element-ui'
],
app: {
head: {
viewport: 'width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no',
@ -32,12 +35,13 @@ export default defineNuxtConfig({
tenantId: '5',
// apiDev: 'http://127.0.0.1:9001/api',
apiDev: 'https://modules.gxwebsoft.com/api',
apiServerDev: 'http://127.0.0.1:9090/api',
// 以下一般不需要修改
apiBase: 'https://modules.gxwebsoft.com/api',
apiServer: 'https://server.gxwebsoft.com/api',
globalTitle: '网宿软件',
domain: 'websoft.top',
domain: 'websoft.top'
},
// 私有配置项
}

3
package.json

@ -33,7 +33,7 @@
"qrcode.vue": "^3.3.3",
"sass": "^1.57.1",
"vue": "latest",
"vue-i18n": "^9.2.2",
"vue-i18n": "^9.14.0",
"vue-router": "^4.1.5",
"xgplayer": "^3.0.5",
"xgplayer-mp4": "^3.0.5"
@ -44,6 +44,7 @@
"@nuxt/devtools": "^0.6.1",
"@nuxt/image": "^1.7.0",
"@nuxtjs/eslint-config-typescript": "^12.1.0",
"@nuxtjs/i18n": "^8.5.2",
"@typescript-eslint/parser": "^7.1.1",
"@unocss/nuxt": "^0.62.2",
"@unocss/preset-attributify": "^0.62.2",

79
pages/_____bak/[custom].vue

@ -1,79 +0,0 @@
<template>
<!-- Banner -->
<Banner :data="form" />
<!-- 容器 -->
<div class="container md:w-screen-xl m-auto">
<div class="flex flex-col">
<Breadcrumb :data="form" />
<div :class="form.design?.style" class="page-main w-full bg-white rounded-lg">
<div class="p-4 leading-7" v-html="form.design?.content">
</div>
</div>
</div>
</div>
<div class="mt-[60px]" v-if="!form.design">
<el-empty description="404 页面不存在"></el-empty>
</div>
</template>
<script setup lang="ts">
import type {ApiResult} from "~/api";
import {useServerRequest} from "~/composables/useServerRequest";
import {useConfigInfo, useForm, useToken, useWebsite} from "~/composables/configState";
import Breadcrumb from "~/components/Breadcrumb.vue";
import type {BreadcrumbItem} from "~/types/global";
import type {Navigation} from "~/api/cms/navigation/model";
import {getIdBySpm} from "~/utils/common";
//
const route = useRoute();
const website = useWebsite();
const config = useConfigInfo();
const token = useToken();
const form = useForm();
const breadcrumb = ref<BreadcrumbItem>();
//
const reload = async () => {
// spm()
const { data: nav } = await useServerRequest<ApiResult<Navigation>>('/cms/navigation/' + getIdBySpm(5))
if (nav.value?.data) {
form.value = nav.value.data
}
// spm
if(!form.value.navigationId){
const { data: nav } = await useServerRequest<ApiResult<Navigation>>('/cms/navigation/getNavigationByPath',{query: {path: route.path}})
if(nav.value?.data){
form.value = nav.value?.data;
}
}
// seo
useHead({
title: `${form.value.title} - ${website.value.websiteName}`,
meta: [{ name: form.value.design?.keywords, content: form.value.design?.description }],
bodyAttrs: {
class: "page-container",
},
script: [
{
children: `console.log(${JSON.stringify(form.value)})`,
},
],
});
//
breadcrumb.value = form.value
}
watch(
() => route.path,
(path) => {
if(path){
reload();
}
},
{ immediate: true }
);
</script>

53
pages/_____bak/a/index.vue

@ -1,53 +0,0 @@
<template>
<!-- Banner -->
<Banner :data="form" />
<div v-if="form" class="flex flex-col w-full md:w-screen-xl m-auto my-3">
<Breadcrumb :data="form" title="文章详情" />
<div class="m-3 bg-white p-3 mt-4 flex flex-col gap-xl rounded-lg">
<div class="article-title-box p-4">
<div class="sm:text-3xl text-xl sm:text-left text-center">{{ form.title }}</div>
<div class="text-sm pt-2 text-gray-4 flex gap-xl sm:text-left sm:justify-start justify-center">
<span>{{ form.createTime }}</span>
<span>浏览{{ form.actualViews }}</span>
</div>
</div>
<el-divider style="height: 1px " />
<div class="content leading-8 sm:text-xl px-3 text-gray-700" v-html="form.content"></div>
</div>
</div>
<div v-if="!form">
<el-empty description="404 页面不存在"></el-empty>
</div>
</template>
<script setup lang="ts">
import type {ApiResult} from "~/api";
import {useServerRequest} from "~/composables/useServerRequest";
import type {Goods} from "~/api/shop/goods/model";
import type {Article} from "~/api/cms/article/model";
import Breadcrumb from "~/components/Breadcrumb.vue";
import {getIdByParam} from "~/utils/common";
const route = useRoute();
const { params } = route;
const { id } = params;
//
const form = ref<Article | any>();
//
const { data: info } = await useServerRequest<ApiResult<Article>>('/cms/article/' + getIdByParam(id))
form.value = info.value?.data;
form.value.num = 1;
form.value.radio = '0'
</script>
<style scoped lang="scss">
.content *{
max-width: 100%;
overflow: hidden;
}
</style>

97
pages/_____bak/customs/index.vue

@ -1,97 +0,0 @@
<template>
<!-- Banner -->
<Banner :data="form" />
{{ form }}--
<!-- 容器 -->
<div class="container md:w-screen-xl m-auto" v-if="form">
<div class="flex flex-col">
<Breadcrumb :data="form" />
<div :class="form.design?.styles" class="page-main w-full bg-white rounded-lg">
<div class="p-4 leading-7" v-html="form.design?.content">
</div>
</div>
</div>
</div>
<div v-if="!form">
<el-empty description="404 页面不存在"></el-empty>
</div>
</template>
<script setup lang="ts">
import type {ApiResult} from "~/api";
import {useServerRequest} from "~/composables/useServerRequest";
import {useConfigInfo, useForm, useToken, useWebsite} from "~/composables/configState";
import Breadcrumb from "~/components/Breadcrumb.vue";
import type {BreadcrumbItem} from "~/types/global";
import type {Navigation} from "~/api/cms/navigation/model";
//
const route = useRoute();
const website = useWebsite();
const config = useConfigInfo();
const token = useToken();
const form = useForm();
const breadcrumb = ref<BreadcrumbItem>();
//
const reload = async () => {
//
if(!form.value.navigationId && !form.value.path){
return ;
}
// spm()
if(form.value.navigationId){
console.log('11111')
const { data: nav } = await useServerRequest<ApiResult<Navigation>>('/cms/navigation/' + form.value.navigationId)
if (nav.value?.data) {
form.value = nav.value.data
}
}
// spm
if(!form.value.navigationId){
console.log('2222')
const { data: nav } = await useServerRequest<ApiResult<Navigation>>('/cms/navigation/getNavigationByPath',{query: {path: form.value.path}})
if(nav.value?.data){
form.value = nav.value?.data;
}
}
// seo
useHead({
title: `${form.value.title} - ${website.value.websiteName}`,
meta: [{ name: form.value.design?.keywords, content: form.value.design?.description }],
bodyAttrs: {
class: "page-container",
},
script: [
{
children: `console.log(${JSON.stringify(form.value)})`,
},
],
});
//
breadcrumb.value = form.value
}
watch(
() => route.query.spm,
(spm) => {
// TODO ()spmID
if(spm){
const spmValue = String(spm).split('.')
if(spmValue[5]){
form.value.navigationId = Number(spmValue[5])
}
return reload();
}
// TODO paramsID
const { custom } = route.params
form.value.path = `/${custom}`;
reload();
},
{ immediate: true }
);
</script>

274
pages/_____bak/index_.vue

@ -1,274 +0,0 @@
<template>
<!-- 首页幻灯片 -->
<div class="banner sm:pt-[60px] pt-[46px]" v-if="banner">
<el-carousel :interval="5000" arrow="always" :height="screenWidth <= 768 ? `170px` : `750px`">
<el-carousel-item v-for="item in banner.data" :key="item.uid">
<nuxt-link v-if="item.path" :to="item.path">
<el-image :src="item.url" style="width: 100%" fit="cover"/>
</nuxt-link>
<a v-else :href="item.path">
<el-image :src="item.url" style="width: 100%" fit="cover"/>
</a>
</el-carousel-item>
</el-carousel>
</div>
<!-- 首页新闻栏目 -->
<div class="category md:w-screen-xl m-auto my-3 p-3 flex flex-col gap-xl">
<div class="category-item bg-white rounded-lg p-3 hover:shadow">
<div class="category-name text-lg text-gray-600 border-b border-gray-200 border-b-solid pb-2 mx-2">
<el-icon>
<ElIconLink/>
</el-icon>
<text class="ml-2">栏目名称</text>
</div>
<div class="flex flex-wrap px-2 py-4">
<div v-for="(item,index) in links" class="flex items-center">
<a :href="item.url" target="_blank">{{ item.name }}</a>
<el-divider v-if="index + 1 != links.length" direction="vertical" />
</div>
</div>
</div>
<div class="category-item bg-white rounded-lg p-3 hover:shadow">
<div class="category-name text-lg text-gray-600 border-b border-gray-200 border-b-solid pb-2 mx-2 flex items-center">
<el-icon>
<ElIconLink/>
</el-icon>
<text class="ml-2">友情链接</text>
</div>
<div class="flex flex-wrap px-2 py-4">
<div v-for="(item,index) in links" class="flex items-center">
<a :href="item.url" target="_blank">{{ item.name }}</a>
<el-divider v-if="index + 1 != links.length" direction="vertical" />
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import type {ApiResult} from "~/api";
import type {AdItem} from "~/api/cms/ad/model";
import type {Link} from "~/api/cms/link/model";
import {useServerRequest} from "~/composables/useServerRequest";
import {useWebsite} from "~/composables/configState";
import {getPath} from "~/utils/common";
const website = useWebsite();
const banner = ref<any>();
const links = ref<any>();
const movieList = ref<any>();
const screenWidth = window.innerWidth;
const screenHeight = window.innerHeight;
//
const reload = async () => {
await nextTick()
const form = useServerRequest('/cms/navigation/getNavigationByPath',{query: {path: getPath()}})
const getSlide = useServerRequest<ApiResult<AdItem[]>>('/cms/ad/side');
const getLink = useServerRequest<ApiResult<Link[]>>('/oa/link?linkType=友情链接');
const [{data: slide}, {data: link}] = await Promise.all([getSlide, getLink]);
console.log(slide.value)
banner.value = slide.value?.data;
links.value = link.value?.data;
}
reload();
</script>
<style lang="scss">
.index {
padding-top: 20px;
.banner {
.el-carousel__container {
height: 380px;
}
.el-image {
height: 380px;
}
@media (max-width: 768px) {
.el-carousel__container {
height: 200px;
}
.el-image {
height: 200px;
}
}
}
.friendly-link {
border-bottom: #eee solid 1px;
padding: 10px;
font-size: 18px;
> img {
margin-right: 10px;
}
&__content {
padding: 20px;
a {
padding-right: 15px;
}
}
}
}
.demonstration {
color: var(--el-text-color-secondary);
}
.col-pd {
li {
a {
font-size: 14px;
padding: 10px 0 10px;
border-bottom: dotted 1px #eeeeee;
.badge {
display: inline-block;
margin-right: 10px;
width: 18px;
height: 18px;
text-align: center;
line-height: 18px;
border-radius: 2px;
font-size: 12px;
background-color: #eee;
color: #333;
}
.text-muted {
color: #999;
}
}
&:nth-child(1) {
.badge {
background-color: #ff4a4a;
color: #fff;
}
}
&:nth-child(2) {
.badge {
background-color: #ff7701;
color: #fff;
}
}
&:nth-child(3) {
.badge {
background-color: #ffb400;
color: #fff;
}
}
}
}
.panel_hd {
border-bottom: #eeeeee solid 1px;
height: 46px;
margin-bottom: 15px;
.title {
font-size: 18px;
line-height: 24px;
img {
margin-right: 10px;
}
}
&__right {
li {
position: relative;
&::before {
content: '';
display: block;
width: 1px;
height: 10px;
background: #eee;
position: absolute;
top: 50%;
transform: translateY(-30%);
right: 0;
}
&:last-child::before {
display: none;
}
a {
padding: 0 10px;
color: #999;
font-size: 14px;
}
}
}
}
.video-list {
&__block {
padding: 10px 0;
&__img {
width: 100%;
height: 218px;
}
.img-box {
position: relative;
height: 218px;
display: block;
span {
position: absolute;
bottom: 0;
width: 100%;
height: 30px;
line-height: 30px;
left: 0;
display: inline-block;
background-image: linear-gradient(transparent, rgba(0, 0, 0, .5));
color: #fff;
font-size: 12px;
text-align: right;
padding-right: 10px;
box-sizing: border-box;
}
}
}
&__detail {
.title {
font-size: 14px;
color: #333;
padding-top: 10px;
}
p {
min-height: 19px;
font-size: 12px;
margin-bottom: 0;
margin-top: 5px;
color: #999;
}
}
}
@media only screen and (max-width: 991px) {
.video-list {
&__block {
&__img, .img-box {
height: 170px;
}
}
}
}
</style>

80
pages/_____bak/page/[id].vue

@ -1,80 +0,0 @@
<template>
<!-- Banner -->
<Banner :data="form" />
<!-- 容器 -->
<div class="container md:w-screen-xl m-auto" v-if="form">
<div class="flex flex-col">
<Breadcrumb :data="form" />
<div :class="form.design?.styles" class="page-main w-full bg-white rounded-lg">
<div class="p-4 leading-7" v-html="form.design?.content">
</div>
</div>
</div>
</div>
<div v-if="!form">
<el-empty description="404 页面不存在"></el-empty>
</div>
</template>
<script setup lang="ts">
import type {ApiResult} from "~/api";
import {useServerRequest} from "~/composables/useServerRequest";
import {useConfigInfo, useForm, useToken} from "~/composables/configState";
import Breadcrumb from "~/components/Breadcrumb.vue";
import type {BreadcrumbItem} from "~/types/global";
import type {Navigation} from "~/api/cms/navigation/model";
//
const route = useRoute();
const config = useConfigInfo();
const token = useToken();
const form = useForm();
const breadcrumb = ref<BreadcrumbItem>();
//
const reload = async () => {
const { data: nav } = await useServerRequest<ApiResult<Navigation>>('/cms/navigation/' + form.value.navigationId)
if (nav.value?.data) {
form.value = nav.value.data
// seo
useHead({
title: `${form.value.title} - ${config.value.siteName}`,
meta: [{ name: form.value.design?.keywords, content: form.value.design?.description }],
bodyAttrs: {
class: "page-container",
},
script: [
{
children: "console.log('Hello World====')",
},
],
});
//
breadcrumb.value = form.value
}
}
watch(
() => route.query.spm,
(spm) => {
console.log(spm,'spm>>')
// TODO spmID
const spmValue = String(spm).split('.')
if(spmValue[5]){
form.value.navigationId = Number(spmValue[5])
}
// TODO ()paramsID
console.log(route.params.id,'sdfsd');
// const str = String(custom).replaceAll(".html", "");
// const split = str.split('-');
// if(split[1]){
// console.log('paramsID=' + split[1])
// form.value.navigationId = Number(split[1])
// }
reload();
},
{ immediate: true }
);
</script>

67
pages/_____bak/spm/[spm].vue

@ -1,67 +0,0 @@
<template>
<el-card shadow="hover">
<template #header>
<div class="flex items-center gap-xs">
<el-icon>
<ElIconLink/>
</el-icon>
<span>推荐文章</span>
</div>
</template>
<div class="flex flex-wrap p-2">
<div v-for="(item,index) in links" class="flex items-center">
<a :href="item.url" target="_blank">{{ item.name }}</a>
<el-divider v-if="index + 1 != links.length" direction="vertical" />
</div>
</div>
</el-card>
<el-card shadow="hover">
<template #header>
<div class="flex items-center gap-xs">
<el-icon>
<ElIconLink/>
</el-icon>
<span>友情链接</span>
</div>
</template>
<div class="flex flex-wrap p-2">
<div v-for="(item,index) in links" class="flex items-center">
<a :href="item.url" target="_blank">{{ item.name }}</a>
<el-divider v-if="index + 1 != links.length" direction="vertical" />
</div>
</div>
</el-card>
</template>
<script setup lang="ts">
import type {ApiResult} from "~/api";
import type {AdItem} from "~/api/cms/ad/model";
import type {Link} from "~/api/cms/link/model";
import {useServerRequest} from "~/composables/useServerRequest";
const banner = ref<any>();
const links = ref<any>();
const movieList = ref<any>();
const screenWidth = window.innerWidth;
const screenHeight = window.innerHeight;
const route = useRoute();
const { params, query } = route;
const { spm } = query;
console.log(spm,'spm=')
//
const reload = async () => {
await nextTick()
const getSlide = useServerRequest<ApiResult<AdItem[]>>('/cms/ad/side');
const getLink = useServerRequest<ApiResult<Link[]>>('/oa/link?linkType=友情链接');
const [{data: slide}, {data: link}] = await Promise.all([getSlide, getLink]);
console.log(slide.value)
banner.value = slide.value?.data;
links.value = link.value?.data;
}
reload();
</script>

2
pages/index.vue

@ -7,7 +7,7 @@
<!-- 高级模式(自定义组件) -->
<div class="flex flex-col" v-if="layout?.showLayout">
{{ layout }}
</div>
</template>

183
pages/login.vue

@ -1,23 +1,25 @@
<template>
<div class="login flex justify-around mt-[120px] h-[550px]">
<div class="login flex justify-around py-24 h-[700px] items-center">
<div class="flash">
</div>
<el-card class="m-2 w-screen-sm sm:w-[430px] flex justify-around">
<el-card class="m-5 w-screen-sm sm:w-[430px] sm:h-[520px] flex justify-around">
<el-space class="tabs pt-5 text-xl flex justify-center">
<el-tabs v-model="activeName" class="demo-tabs">
<el-tab-pane label="账号登录" name="first">
<el-tab-pane label="账号登录" name="account">
<div class="custom-style my-4">
<el-form :model="form" label-width="auto" class="w-[330px]">
<el-form-item>
<el-input class="w-full" size="large" maxlength="11" placeholder="登录账号|手机号码" v-model="form.username" />
<el-input class="w-full" size="large" maxlength="11" placeholder="请输入手机号码" v-model="form.username">
<template #prepend>+86</template>
</el-input>
</el-form-item>
<el-form-item>
<el-input type="password" size="large" placeholder="登录密码" v-model="form.password" />
<el-input type="password" size="large" placeholder="登录密码" v-model="form.password" />
</el-form-item>
<el-form-item>
<el-space class="flex justify-between w-full">
<el-input size="large" placeholder="图形验证码" v-model="form.code" />
<el-input size="large" placeholder="图形验证码" maxlength="5" v-model="form.code" @keyup.enter.prevent="onSubmit" />
<el-image alt="" :src="captcha" @click="changeCaptcha" />
</el-space>
</el-form-item>
@ -31,75 +33,67 @@
</el-form>
</div>
</el-tab-pane>
<el-tab-pane label="短信登录" name="second">
<el-tab-pane label="短信登录" name="sms">
<div class="custom-style my-4">
<el-form :model="form" label-width="auto" class="w-[330px]">
<el-form-item>
<el-input class="w-full" size="large" placeholder="手机号码" v-model="form.username" />
<el-input class="w-full" size="large" maxlength="11" placeholder="请输入手机号码" v-model="form.phone">
<template #prepend>+86</template>
</el-input>
</el-form-item>
<el-form-item>
<el-input type="password" size="large" placeholder="验证码" v-model="form.password" />
<el-space class="flex justify-between w-full">
<el-input size="large" placeholder="短信验证码" class="w-full" v-model="form.code" @keyup.enter.prevent="onSubmitBySms" />
<el-button size="large" class="w-full" :disabled="!!countdownTime" @click="sendCode">
<span v-if="!countdownTime">发送验证码</span>
<span v-else>已发送 {{ countdownTime }} s</span>
</el-button>
</el-space>
</el-form-item>
<el-form-item>
<el-button type="primary" size="large" class="w-full" @click="onSubmit">登录</el-button>
<el-button type="primary" size="large" class="w-full" @click="onSubmitBySms">登录</el-button>
</el-form-item>
</el-form>
</div>
</el-tab-pane>
</el-tabs>
</el-space>
<div class="clearfix flex justify-center">
<el-divider>
其他登录方式
</el-divider>
</div>
<div class="clearfix flex justify-center">
<el-button circle :icon="ElIconUserFilled"></el-button>
</div>
<!-- <div class="clearfix flex justify-center">-->
<!-- <el-space>-->
<!-- <a class=" text-gray-400 cursor-pointer">忘记账号</a>-->
<!-- <a class=" text-gray-400 cursor-pointer">忘记密码</a>-->
<!-- <a class=" text-gray-400 cursor-pointer">登录异常帮助文档</a>-->
<!-- </el-space>-->
<!-- </div>-->
<!-- <el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClick">-->
<!-- <el-tab-pane label="登录" name="first">登录</el-tab-pane>-->
<!-- <el-tab-pane label="注册" name="second">注册</el-tab-pane>-->
<!-- </el-tabs>-->
<!-- 快捷登录 --->
<template v-if="activeName == 'account'">
<div class="clearfix flex justify-center">
<el-divider>
<span class="text-gray-400">其他登录方式</span>
</el-divider>
</div>
<div class="clearfix flex justify-center">
<el-button circle :icon="ElIconUserFilled"></el-button>
</div>
</template>
</el-card>
</div>
</template>
<script setup lang="ts">
import {useConfigInfo, useToken, useUserInfo, useWebsite} from "~/composables/configState";
import {useConfigInfo, useToken, useUser, useWebsite} from "~/composables/configState";
import useFormData from '@/utils/use-form-data';
import type { User } from '@/api/system/user/model';
import { ref } from 'vue'
import {getCaptcha} from "~/api/passport/login";
import {useServerRequest} from "~/composables/useServerRequest";
import type {ApiResult} from "~/api";
import type {Website} from "~/api/cms/website/model";
import type {CaptchaResult, LoginResult} from "~/api/passport/login/model";
//
const runtimeConfig = useRuntimeConfig();
const website = useWebsite()
const website = useWebsite();
const config = useConfigInfo();
const token = useToken();
const userInfo = useUserInfo();
const user = useUser();
const value = ref('登录')
const options = ['登录', '注册']
const activeName = ref('first')
const activeName = ref('account')
// base64
const captcha = ref('');
// ,
const text = ref('');
//
const visible = ref(false);
//
const imgCode = ref('');
// loading
@ -120,6 +114,41 @@ const { form } = useFormData<User>({
isAdmin: true
});
/* 显示发送短信验证码弹窗 */
const openImgCodeModal = () => {
if (!form.phone) {
ElMessage.error('请输入手机号码');
return;
}
// imgCode.value = text.value;
};
/* 发送短信验证码 */
const sendCode = async () => {
if (!form.phone) {
ElMessage.error('请输入手机号码');
return;
}
imgCode.value = text.value;
codeLoading.value = true;
const {data: smsCode } = await useServerRequest<ApiResult<CaptchaResult>>('/sendSmsCaptcha',{baseURL: runtimeConfig.public.apiServer,method: "post",body: {
phone: form.phone
}});
if(smsCode.value){
codeLoading.value = false;
countdownTime.value = 30;
//
countdownTimer = window.setInterval(() => {
if (countdownTime.value <= 1) {
countdownTimer && clearInterval(countdownTimer);
countdownTimer = null;
}
countdownTime.value--;
}, 1000);
}
};
const navigateTo = (url: string) => {
window.location.href = url;
@ -131,6 +160,7 @@ const changeCaptcha = async () => {
const captchaData = captchaInfo.value?.data
if(captchaData){
captcha.value = captchaData.base64;
text.value = captchaData.text;
}
//
@ -140,39 +170,70 @@ const changeCaptcha = async () => {
}
};
useHead({
title: `登录页 - ${config.value?.siteName || 'WEB应用开发平台'}`,
meta: [{ name: website.value.keywords, content: website.value.comments }]
});
/**
* 执行登录
*/
const onSubmit = async () => {
const {data: response} = await useServerRequest<ApiResult<LoginResult>>('/login',{baseURL: runtimeConfig.public.apiServer,method: "post",body: form})
console.log(response.value);
if (response.value?.code == 0) {
if(response.value.data){
const access_token = response.value.data?.access_token
const user = response.value.data?.user
if(access_token){
token.value = access_token;
}
if(user){
userInfo.value = user
}
}
ElMessage.success(response.value.message)
// setTimeout(() => {
// navigateTo('/')
// return;
// },1000)
//
if(response.value?.code == 0){
ElMessage.success(response.value?.message)
await doLogin(response.value.data)
}
if(response.value?.code !== 0){
if(response.value?.code != 0){
ElMessage.error(response.value?.message)
}
}
/**
* 短信验证码登录
*/
const onSubmitBySms = async () => {
const {data: response} = await useServerRequest<ApiResult<LoginResult>>('/loginBySms',{baseURL: runtimeConfig.public.apiServer,method: "post",body: {
phone: form.phone,
code: form.code,
tenantId: 5
}})
//
if(response.value?.code == 0){
ElMessage.success(response.value?.message)
await doLogin(response.value.data)
}
if(response.value?.code != 0){
ElMessage.error(response.value?.message)
}
}
//
const doLogin = async (data: any) => {
const access_token = data?.access_token
if(access_token){
token.value = access_token;
}
if(data.user){
user.value.userId = data.user.userId;
user.value.phone = data.user.phone;
user.value.gradeId = data.user.gradeId;
user.value.gradeName = data.user.gradeName;
user.value.avatar = data.user.avatar;
}
setTimeout(() => {
navigateTo('/user')
return;
},500)
}
changeCaptcha();
</script>
<style lang="less">
body{
background: url("https://oss.wsdns.cn/20240616/4fe7e2e00b7e43e7a5f189fe11b21196.jpeg");
.login{
background: url("https://oss.wsdns.cn/20240904/6f5dc87c37334c4da3453826352a37d1.jpg");
background-size: 100%;
}
</style>

184
pages/user/auth.vue

@ -0,0 +1,184 @@
<template>
<div class="login-layout mt-[100px] m-auto sm:w-screen-xl w-full">
<div class="mt-[100px] m-auto flex sm:flex-row flex-col sm:p-0 p-3">
<el-card shadow="hover" class="sm:w-[250px] sm:flex sm:mb-0 mb-5 justify-center w-full">
<div class="flex justify-center pb-4 flex-col justify-center items-center">
<el-avatar :src="userInfo?.avatar" :size="70" />
<text class="text-gray-400 py-1 text-sm">更换头像</text>
</div>
<!-- 用户菜单 -->
<UserMenu :activeIndex="activeIndex" />
</el-card>
<el-tabs type="border-card" class="flash bg-white hover:shadow w-full sm:ml-6 ml-0">
<el-tab-pane label="个人认证">
<el-form :model="form" label-width="auto" size="large" label-position="top" class="sm:w-screen-md w-full sm:px-6 sm:py-2">
<el-form-item label="真实姓名">
<el-input v-model="form.realName" placeholder="请输入真实姓名" />
</el-form-item>
<el-form-item label="证件号码">
<el-input v-model="form.idCard" placeholder="请输入证件号码" />
</el-form-item>
<el-form-item label="身份证(正面)">
<Upload />
</el-form-item>
<el-form-item label="身份证(反面)">
<Upload />
</el-form-item>
<el-form-item>
<el-button type="primary" size="large" @click="onSubmit">提交</el-button>
</el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane label="企业认证">
<el-form :model="form" label-width="auto" size="large" label-position="top" class="sm:w-screen-md w-full sm:px-6 sm:py-2">
<el-form-item label="企业名称">
<el-input v-model="form.companyName" placeholder="请输入企业名称" />
</el-form-item>
<el-form-item label="社会信用代码">
<el-input v-model="form.idCard" placeholder="请输入社会信用代码" />
</el-form-item>
<el-form-item label="营业执照">
<Upload />
</el-form-item>
<el-form-item label="所属行业">
<el-select v-model="form.city" placeholder="请选择所属行业">
</el-select>
</el-form-item>
<el-form-item label="网站信息">
<el-input v-model="form.idCard" placeholder="请输入网站信息" />
</el-form-item>
<el-form-item label="您的身份">
<el-radio-group v-model="form.sex">
<el-radio value="1">法定代表人</el-radio>
<el-radio value="2">被授权人</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="用户协议">
<el-checkbox v-model="form.status">请务必提供真实信息我司有权自行或委托第三方审查您提供的身份信息是否属真实有效若提供虚假信息由此的全部后果由您承担</el-checkbox>
</el-form-item>
<el-form-item>
<el-button type="primary" size="large" @click="onSubmit">提交</el-button>
</el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane label="个人认证成功">
<el-result
icon="success"
title="个人认证成功"
sub-title="认证完成时间 2024-09-30"
>
<template #extra>
<el-button type="text">修改认证信息</el-button>
</template>
</el-result>
</el-tab-pane>
<el-tab-pane label="企业认证成功">
<el-result
icon="success"
title="企业认证成功"
sub-title="认证完成时间 2024-09-30"
>
<template #extra>
<el-button type="text">修改认证信息</el-button>
</template>
</el-result>
</el-tab-pane>
</el-tabs>
</div>
</div>
</template>
<script setup lang="ts">
import {useConfigInfo, useToken, useWebsite} from "~/composables/configState";
import useFormData from '@/utils/use-form-data';
import type { User } from '@/api/system/user/model';
import { ref } from 'vue'
import { Plus } from '@element-plus/icons-vue'
import {useServerRequest} from "~/composables/useServerRequest";
import type {ApiResult} from "~/api";
import UserMenu from "~/components/UserMenu.vue";
//
const runtimeConfig = useRuntimeConfig();
const route = useRoute();
const activeIndex = ref('');
const website = useWebsite()
const config = useConfigInfo();
const token = useToken();
const userInfo = ref<User>();
//
const { form, assignFields } = useFormData<User>({
userId: undefined,
nickname: '',
username: '',
phone: '',
mobile: '',
sex: '',
sexName: '',
email: '',
password: '',
code: '',
smsCode: '',
comments: '',
remember: true,
isAdmin: true
});
useHead({
title: `用户中心 - ${config.value?.siteName}`,
meta: [{ name: website.value.keywords, content: website.value.comments }]
});
const onSubmit = async () => {
const {data: modify } = await useServerRequest<ApiResult<User>>('/auth/user',{
baseURL: runtimeConfig.public.apiServer,
method: 'put',
body: form
})
if(modify.value?.code == 0){
ElMessage.success('修改成功')
}
}
const handleAvatarSuccess: UploadProps['onSuccess'] = (
response,
uploadFile
) => {
imageUrl.value = URL.createObjectURL(uploadFile.raw!)
}
const beforeAvatarUpload: UploadProps['beforeUpload'] = (rawFile) => {
if (rawFile.type !== 'image/jpeg') {
ElMessage.error('Avatar picture must be JPG format!')
return false
} else if (rawFile.size / 1024 / 1024 > 2) {
ElMessage.error('Avatar picture size can not exceed 2MB!')
return false
}
return true
}
const reload = async () => {
const {data: response} = await useServerRequest<ApiResult<User>>('/auth/user',{baseURL: runtimeConfig.public.apiServer})
if(response.value?.data){
userInfo.value = response.value?.data;
assignFields(response.value?.data);
}
}
watch(
() => route.path,
(path) => {
activeIndex.value = path;
console.log(path,'=>Path')
reload();
},
{ immediate: true }
);
</script>
<style lang="less">
body{
background: url("https://oss.wsdns.cn/20240328/797a8e323bba4fc6827abf9d8b98ba45.png");
background-size: 100%;
}
</style>

170
pages/user/index.vue

@ -1,130 +1,116 @@
<template>
<div class="login-layout mt-[100px] m-auto w-screen-xl">
<el-container class="mt-[100px] m-auto p-">
<el-aside width="170px" class="bg-white p-3 hover:shadow">
<div class="flex justify-center pb-4">
<img src="https://oss.wsdns.cn/20240328/797a8e323bba4fc6827abf9d8b98ba45.png" class="w-[80px] h-[80px] rounded-full" />
</div>
<div class="menu">
<el-menu
default-active="1"
style="border: none"
>
<el-menu-item v-for="(item,index) in activities" :index="`${index+1}`" style="border-bottom: 1px solid #f3f3f3">
<span class="text-center">{{ item.name }}</span>
</el-menu-item>
</el-menu>
<div class="login-layout mt-[100px] m-auto sm:w-screen-xl w-full">
<div class="mt-[100px] m-auto flex sm:flex-row flex-col sm:p-0 p-3">
<el-card shadow="hover" class="sm:w-[250px] sm:flex sm:mb-0 mb-5 justify-center w-full">
<div class="flex justify-center pb-4 flex-col justify-center items-center">
<el-avatar :src="userInfo?.avatar" :size="70" />
<text class="text-gray-400 py-1 text-sm">更换头像</text>
</div>
</el-aside>
<div shadow="hover" class="flash bg-white hover:shadow h-[500px] w-full ml-6">
={{ userInfo }}=
</div>
</el-container>
<!-- 用户菜单 -->
<UserMenu :activeIndex="activeIndex" @done="onDone" />
</el-card>
<el-card shadow="hover" class="flash bg-white hover:shadow w-full sm:ml-6 ml-0">
<template #header>账号信息</template>
<el-form :model="form" label-width="auto" size="large" label-position="top" class="sm:w-screen-md w-full sm:px-6 sm:py-2">
<el-form-item label="手机号码">
<el-input disabled v-model="form.mobile" />
</el-form-item>
<el-form-item label="昵称">
<el-input v-model="form.nickname" />
</el-form-item>
<el-form-item label="邮箱账号">
<el-input v-model="form.email" placeholder="邮箱账号" />
</el-form-item>
<el-form-item label="性别">
<el-radio-group v-model="form.sex">
<el-radio value="1"></el-radio>
<el-radio value="2"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="个人签名">
<el-input v-model="form.comments" type="textarea" placeholder="个人签名" :rows="4" />
</el-form-item>
<el-form-item>
<el-button type="primary" size="large" @click="onSubmit">保存</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</div>
</template>
<script setup lang="ts">
import {useConfigInfo, useToken, useUserInfo, useWebsite} from "~/composables/configState";
import {useConfigInfo, useToken, useWebsite} from "~/composables/configState";
import useFormData from '@/utils/use-form-data';
import type { User } from '@/api/system/user/model';
import { ref } from 'vue'
import {getCaptcha} from "~/api/passport/login";
import {useServerRequest} from "~/composables/useServerRequest";
import type {ApiResult} from "~/api";
import type {Website} from "~/api/cms/website/model";
import type {CaptchaResult, LoginResult} from "~/api/passport/login/model";
import UserMenu from "~/components/UserMenu.vue";
//
const runtimeConfig = useRuntimeConfig();
const route = useRoute();
const website = useWebsite()
const config = useConfigInfo();
const token = useToken();
const userInfo = useUserInfo();
const userInfo = ref<User>();
const activeIndex = ref('');
const value = ref('登录')
const options = ['登录', '注册']
const activeName = ref('first')
const activities = [
{
name: '账号信息'
},
{
name: '实名认证'
},
{
name: '订单列表'
},
{
name: '退出登录'
},
]
const activeNames = ref(['1'])
const handleChange = (val: string[]) => {
console.log(val)
}
// base64
const captcha = ref('');
// ,
const text = ref('');
//
const visible = ref(false);
//
const imgCode = ref('');
// loading
const codeLoading = ref(false);
//
const countdownTime = ref(0);
//
let countdownTimer: number | null = null;
//
const { form } = useFormData<User>({
const { form, assignFields } = useFormData<User>({
userId: undefined,
nickname: '',
username: '',
phone: '',
mobile: '',
sex: '',
sexName: '',
email: '',
password: '',
code: '',
smsCode: '',
comments: '',
remember: true,
isAdmin: true
});
useHead({
title: `用户中心 - ${config.value?.siteName}`,
meta: [{ name: website.value.keywords, content: website.value.comments }]
});
const navigateTo = (url: string) => {
window.location.href = url;
const onDone = (index: string) => {
activeIndex.value = index;
}
/**
* 执行登录
*/
const onSubmit = async () => {
const {data: response} = await useServerRequest<ApiResult<LoginResult>>('/login',{baseURL: runtimeConfig.public.apiServer,method: "post",body: form})
if (response.value?.code == 0) {
if(response.value.data){
const access_token = response.value.data?.access_token
const user = response.value.data?.user
if(access_token){
token.value = access_token;
}
if(user){
userInfo.value = user;
}
}
ElMessage.success(response.value.message)
setTimeout(() => {
navigateTo('/')
return;
},1000)
const {data: modify } = await useServerRequest<ApiResult<User>>('/auth/user',{
baseURL: runtimeConfig.public.apiServer,
method: 'put',
body: form
})
if(modify.value?.code == 0){
ElMessage.success('修改成功')
}
console.log(response.value?.data,'>>>>>sfd')
ElMessage.error(response.value?.message)
return false;
}
useHead({
title: `用户中心 - ${config.value?.siteName}`,
meta: [{ name: website.value.keywords, content: website.value.comments }]
});
const reload = async () => {
const {data: response} = await useServerRequest<ApiResult<User>>('/auth/user',{baseURL: runtimeConfig.public.apiServer})
if(response.value?.data){
userInfo.value = response.value?.data;
assignFields(response.value?.data);
}
}
watch(
() => route.path,
(path) => {
activeIndex.value = path;
console.log(path,'=>Path')
reload();
},
{ immediate: true }
);
</script>
<style lang="less">
body{

13
pages/user/logout.vue

@ -0,0 +1,13 @@
<template>
</template>
<script setup lang="ts">
import {useToken} from "~/composables/configState";
const token = useToken();
token.value = '';
localStorage.clear();
setTimeout(() => {
navigateTo('/')
return;
}, 1000)
</script>

104
pages/user/order.vue

@ -0,0 +1,104 @@
<template>
<div class="login-layout mt-[100px] m-auto sm:w-screen-xl w-full">
<div class="mt-[100px] m-auto flex sm:flex-row flex-col sm:p-0 p-3">
<el-card shadow="hover" class="sm:w-[250px] sm:flex sm:mb-0 mb-5 justify-center w-full">
<div class="flex justify-center pb-4 flex-col justify-center items-center">
<el-avatar :src="userInfo?.avatar" :size="70" />
<text class="text-gray-400 py-1 text-sm">更换头像</text>
</div>
<!-- 用户菜单 -->
<UserMenu :activeIndex="activeIndex" />
</el-card>
<el-card shadow="hover" class="flash bg-white hover:shadow w-full sm:ml-6 ml-0">
<template #header>订单列表</template>
<el-table :data="tableData" style="width: 100%">
<el-table-column prop="date" label="下单日期" width="180" />
<el-table-column prop="name" label="产品名称" width="180" />
<el-table-column prop="address" label="订单信息" />
</el-table>
</el-card>
</div>
</div>
</template>
<script setup lang="ts">
import {useConfigInfo, useToken, useWebsite} from "~/composables/configState";
import useFormData from '@/utils/use-form-data';
import type { User } from '@/api/system/user/model';
import { ref } from 'vue'
import {getCaptcha} from "~/api/passport/login";
import {useServerRequest} from "~/composables/useServerRequest";
import type {ApiResult} from "~/api";
import type {Website} from "~/api/cms/website/model";
import type {CaptchaResult, LoginResult} from "~/api/passport/login/model";
import {getPath, navigateTo} from "#imports";
import UserMenu from "~/components/UserMenu.vue";
//
const runtimeConfig = useRuntimeConfig();
const route = useRoute();
const website = useWebsite()
const config = useConfigInfo();
const token = useToken();
const userInfo = ref<User>();
const activeIndex = ref('');
//
const { form, assignFields } = useFormData<User>({
userId: undefined,
nickname: '',
username: '',
phone: '',
mobile: '',
sex: '',
sexName: '',
email: '',
password: '',
code: '',
smsCode: '',
comments: '',
remember: true,
isAdmin: true
});
const tableData = ref<any[]>();
useHead({
title: `用户中心 - ${config.value?.siteName}`,
meta: [{ name: website.value.keywords, content: website.value.comments }]
});
const onSubmit = async () => {
const {data: modify } = await useServerRequest<ApiResult<User>>('/auth/user',{
baseURL: runtimeConfig.public.apiServer,
method: 'put',
body: form
})
if(modify.value?.code == 0){
ElMessage.success('修改成功')
}
}
const reload = async () => {
const {data: response} = await useServerRequest<ApiResult<User>>('/auth/user',{baseURL: runtimeConfig.public.apiServer})
if(response.value?.data){
userInfo.value = response.value?.data;
assignFields(response.value?.data);
}
}
watch(
() => route.path,
(path) => {
activeIndex.value = path;
console.log(path,'=>Path')
reload();
},
{ immediate: true }
);
</script>
<style lang="less">
body{
background: url("https://oss.wsdns.cn/20240328/797a8e323bba4fc6827abf9d8b98ba45.png");
background-size: 100%;
}
</style>

161
pages/user/password.vue

@ -0,0 +1,161 @@
<template>
<div class="login-layout mt-[100px] m-auto sm:w-screen-xl w-full">
<div class="mt-[100px] m-auto flex sm:flex-row flex-col sm:p-0 p-3">
<el-card shadow="hover" class="sm:w-[250px] sm:flex sm:mb-0 mb-5 justify-center w-full">
<div class="flex justify-center pb-4 flex-col justify-center items-center">
<el-avatar :src="userInfo?.avatar" :size="70" />
<text class="text-gray-400 py-1 text-sm">更换头像</text>
</div>
<!-- 用户菜单 -->
<UserMenu :activeIndex="activeIndex" />
</el-card>
<el-card shadow="hover" class="flash bg-white hover:shadow w-full sm:ml-6 ml-0">
<template #header>修改密码</template>
<el-form :model="form" label-width="auto" size="large" label-position="top" class="sm:w-screen-md w-full sm:px-6 sm:py-2">
<!-- <el-form-item label="手机号码">-->
<!-- <el-input disabled v-model="form.phone" />-->
<!-- </el-form-item>-->
<!-- <el-form-item label="短信验证码">-->
<!-- <el-space class="flex w-full">-->
<!-- <el-input size="large" placeholder="短信验证码" class="w-full" v-model="form.code" />-->
<!-- <el-button size="large" class="w-full" :disabled="!!countdownTime" @click="sendCode">-->
<!-- <span v-if="!countdownTime">发送验证码</span>-->
<!-- <span v-else>已发送 {{ countdownTime }} s</span>-->
<!-- </el-button>-->
<!-- </el-space>-->
<!-- </el-form-item>-->
<el-form-item label="旧密码">
<el-input v-model="form.oldPassword" placeholder="请输入旧密码" />
</el-form-item>
<el-form-item label="新密码">
<el-input v-model="form.password" type="password" placeholder="请输入新密码" />
</el-form-item>
<el-form-item label="确认密码">
<el-input v-model="form.password2" type="password" placeholder="请确认新密码" />
</el-form-item>
<el-form-item>
<el-button type="primary" size="large" @click="onSubmit">保存</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</div>
</template>
<script setup lang="ts">
import {useConfigInfo, useToken, useWebsite} from "~/composables/configState";
import useFormData from '@/utils/use-form-data';
import type { User } from '@/api/system/user/model';
import { ref } from 'vue'
import {useServerRequest} from "~/composables/useServerRequest";
import type {ApiResult} from "~/api";
import UserMenu from "~/components/UserMenu.vue";
import type {CaptchaResult} from "~/api/passport/login/model";
//
const runtimeConfig = useRuntimeConfig();
const route = useRoute();
const website = useWebsite()
const config = useConfigInfo();
const token = useToken();
const userInfo = ref<User>();
const activeIndex = ref('');
// base64
const captcha = ref('');
// ,
const text = ref('');
//
const imgCode = ref('');
// loading
const codeLoading = ref(false);
//
const countdownTime = ref(0);
//
let countdownTimer: number | null = null;
//
const { form, assignFields } = useFormData<User>({
userId: undefined,
nickname: '',
username: '',
phone: '',
mobile: '',
sex: '',
sexName: '',
email: '',
oldPassword: '',
password: '',
password2: '',
code: '',
smsCode: '',
comments: '',
remember: true,
isAdmin: true
});
useHead({
title: `用户中心 - ${config.value?.siteName}`,
meta: [{ name: website.value.keywords, content: website.value.comments }]
});
/* 发送短信验证码 */
const sendCode = async () => {
if (!form.phone) {
ElMessage.error('请输入手机号码');
return;
}
imgCode.value = text.value;
codeLoading.value = true;
const {data: smsCode } = await useServerRequest<ApiResult<CaptchaResult>>('/sendSmsCaptcha',{baseURL: runtimeConfig.public.apiServer,method: "post",body: {
phone: form.phone
}});
if(smsCode.value){
codeLoading.value = false;
countdownTime.value = 30;
//
countdownTimer = window.setInterval(() => {
if (countdownTime.value <= 1) {
countdownTimer && clearInterval(countdownTimer);
countdownTimer = null;
}
countdownTime.value--;
}, 1000);
}
};
const onSubmit = async () => {
const {data: modify } = await useServerRequest<ApiResult<User>>('/auth/password',{
baseURL: runtimeConfig.public.apiServer,
method: 'put',
body: form
})
if(modify.value?.code == 0){
ElMessage.success('修改成功')
}
}
const reload = async () => {
const {data: response} = await useServerRequest<ApiResult<User>>('/auth/user',{baseURL: runtimeConfig.public.apiServer})
if(response.value?.data){
userInfo.value = response.value?.data;
assignFields(response.value?.data);
}
}
watch(
() => route.path,
(path) => {
activeIndex.value = path;
reload();
},
{ immediate: true }
);
</script>
<style lang="less">
body{
background: url("https://oss.wsdns.cn/20240328/797a8e323bba4fc6827abf9d8b98ba45.png");
background-size: 100%;
}
</style>

6
plugins/element-ui.js

@ -0,0 +1,6 @@
import Vue from 'vue'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import locale from 'element-ui/lib/locale/lang/zh-CN'
Vue.use(ElementUI, { locale })

261
pnpm-lock.yaml

@ -66,7 +66,7 @@ importers:
specifier: latest
version: 3.4.38(typescript@5.5.4)
vue-i18n:
specifier: ^9.2.2
specifier: ^9.14.0
version: 9.14.0(vue@3.4.38(typescript@5.5.4))
vue-router:
specifier: ^4.1.5
@ -87,6 +87,9 @@ importers:
'@nuxt/image':
specifier: ^1.7.0
version: 1.7.0(ioredis@5.4.1)(magicast@0.3.4)(rollup@4.21.0)
'@nuxtjs/i18n':
specifier: ^8.5.2
version: 8.5.2(magicast@0.3.4)(rollup@4.21.0)(vue@3.4.38(typescript@5.5.4))
'@unocss/nuxt':
specifier: ^0.62.2
version: 0.62.2(magicast@0.3.4)(postcss@8.4.41)(rollup@4.21.0)(vite@3.2.10(@types/node@22.4.2)(less@4.2.0)(sass@1.77.8)(terser@5.31.6))(webpack@5.93.0(esbuild@0.23.1))
@ -895,10 +898,30 @@ packages:
'@iconify/utils@2.1.31':
resolution: {integrity: sha512-WCu65iVaFRXyGU+op12XVbDZgIov0vzMIlUokZ1WR42cU2wwYMks/pZY8v0tE72W8ShXVaprO79Jv6EjYm3Sjw==, tarball: https://registry.npmmirror.com/@iconify/utils/-/utils-2.1.31.tgz}
'@intlify/bundle-utils@7.5.1':
resolution: {integrity: sha512-UovJl10oBIlmYEcWw+VIHdKY5Uv5sdPG0b/b6bOYxGLln3UwB75+2dlc0F3Fsa0RhoznQ5Rp589/BZpABpE4Xw==, tarball: https://registry.npmmirror.com/@intlify/bundle-utils/-/bundle-utils-7.5.1.tgz}
engines: {node: '>= 14.16'}
peerDependencies:
petite-vue-i18n: '*'
vue-i18n: '*'
peerDependenciesMeta:
petite-vue-i18n:
optional: true
vue-i18n:
optional: true
'@intlify/core-base@9.14.0':
resolution: {integrity: sha512-zJn0imh9HIsZZUtt9v8T16PeVstPv6bP2YzlrYJwoF8F30gs4brZBwW2KK6EI5WYKFi3NeqX6+UU4gniz5TkGg==, tarball: https://registry.npmmirror.com/@intlify/core-base/-/core-base-9.14.0.tgz}
engines: {node: '>= 16'}
'@intlify/core@9.14.0':
resolution: {integrity: sha512-lPZ78GkDFcppC9Ol8oruyPGJbBWvTYDTEAJBebDtGmDIeggDJAiR+XMbCPZAOeW4/XszcIeiGYKEx0BvQDjVTw==, tarball: https://registry.npmmirror.com/@intlify/core/-/core-9.14.0.tgz}
engines: {node: '>= 16'}
'@intlify/h3@0.5.0':
resolution: {integrity: sha512-cgfrtD3qu3BPJ47gfZ35J2LJpI64Riic0K8NGgid5ilyPXRQTNY7mXlT/B+HZYQg1hmBxKa5G5HJXyAZ4R2H5A==, tarball: https://registry.npmmirror.com/@intlify/h3/-/h3-0.5.0.tgz}
engines: {node: '>= 18'}
'@intlify/message-compiler@9.14.0':
resolution: {integrity: sha512-sXNsoMI0YsipSXW8SR75drmVK56tnJHoYbPXUv2Cf9lz6FzvwsosFm6JtC1oQZI/kU+n7qx0qRrEWkeYFTgETA==, tarball: https://registry.npmmirror.com/@intlify/message-compiler/-/message-compiler-9.14.0.tgz}
engines: {node: '>= 16'}
@ -907,6 +930,25 @@ packages:
resolution: {integrity: sha512-r+N8KRQL7LgN1TMTs1A2svfuAU0J94Wu9wWdJVJqYsoMMLIeJxrPjazihfHpmJqfgZq0ah3Y9Q4pgWV2O90Fyg==, tarball: https://registry.npmmirror.com/@intlify/shared/-/shared-9.14.0.tgz}
engines: {node: '>= 16'}
'@intlify/unplugin-vue-i18n@3.0.1':
resolution: {integrity: sha512-q1zJhA/WpoLBzAAuKA5/AEp0e+bMOM10ll/HxT4g1VAw/9JhC4TTobP9KobKH90JMZ4U2daLFlYQfKNd29lpqw==, tarball: https://registry.npmmirror.com/@intlify/unplugin-vue-i18n/-/unplugin-vue-i18n-3.0.1.tgz}
engines: {node: '>= 14.16'}
peerDependencies:
petite-vue-i18n: '*'
vue-i18n: '*'
vue-i18n-bridge: '*'
peerDependenciesMeta:
petite-vue-i18n:
optional: true
vue-i18n:
optional: true
vue-i18n-bridge:
optional: true
'@intlify/utils@0.12.0':
resolution: {integrity: sha512-yCBNcuZQ49iInqmWC2xfW0rgEQyNtCM8C8KcWKTXxyscgUE1+48gjLgZZqP75MjhlApxwph7ZMWLqyABkSgxQA==, tarball: https://registry.npmmirror.com/@intlify/utils/-/utils-0.12.0.tgz}
engines: {node: '>= 18'}
'@ioredis/commands@1.2.0':
resolution: {integrity: sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==, tarball: https://registry.npmmirror.com/@ioredis/commands/-/commands-1.2.0.tgz}
@ -953,6 +995,11 @@ packages:
resolution: {integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==, tarball: https://registry.npmmirror.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz}
hasBin: true
'@miyaneee/rollup-plugin-json5@1.2.0':
resolution: {integrity: sha512-JjTIaXZp9WzhUHpElrqPnl1AzBi/rvRs065F71+aTmlqvTMVkdbjZ8vfFl4nRlgJy+TPBw69ZK4pwFdmOAt4aA==, tarball: https://registry.npmmirror.com/@miyaneee/rollup-plugin-json5/-/rollup-plugin-json5-1.2.0.tgz}
peerDependencies:
rollup: ^1.20.0 || ^2.0.0 || ^3.0.0 || ^4.0.0
'@netlify/functions@2.8.1':
resolution: {integrity: sha512-+6wtYdoz0yE06dSa9XkP47tw5zm6g13QMeCwM3MmHx1vn8hzwFa51JtmfraprdkL7amvb7gaNM+OOhQU1h6T8A==, tarball: https://registry.npmmirror.com/@netlify/functions/-/functions-2.8.1.tgz}
engines: {node: '>=14.0.0'}
@ -1078,6 +1125,10 @@ packages:
peerDependencies:
eslint: ^8.23.0
'@nuxtjs/i18n@8.5.2':
resolution: {integrity: sha512-x5AZAd2sfvL3cYfpwCMQn7DyiwWCTPZSciiMWcfWQunin1V5toyzQRKjztvA6lh2iVOyeZF9bJpCkHG+UkOlkA==, tarball: https://registry.npmmirror.com/@nuxtjs/i18n/-/i18n-8.5.2.tgz}
engines: {node: ^14.16.0 || >=16.11.0}
'@parcel/watcher-android-arm64@2.4.1':
resolution: {integrity: sha512-LOi/WTbbh3aTn2RYddrO8pnapixAziFl6SMxHM69r3tvdSm94JtCenaKgk1GRg5FJ5wpMCpHeW+7yqPlvZv7kg==, tarball: https://registry.npmmirror.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.4.1.tgz}
engines: {node: '>= 10.0.0'}
@ -1239,6 +1290,15 @@ packages:
rollup:
optional: true
'@rollup/plugin-yaml@4.1.2':
resolution: {integrity: sha512-RpupciIeZMUqhgFE97ba0s98mOFS7CWzN3EJNhJkqSv9XLlWYtwVdtE6cDw6ASOF/sZVFS7kRJXftaqM2Vakdw==, tarball: https://registry.npmmirror.com/@rollup/plugin-yaml/-/plugin-yaml-4.1.2.tgz}
engines: {node: '>=14.0.0'}
peerDependencies:
rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
peerDependenciesMeta:
rollup:
optional: true
'@rollup/pluginutils@4.2.1':
resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==, tarball: https://registry.npmmirror.com/@rollup/pluginutils/-/pluginutils-4.2.1.tgz}
engines: {node: '>= 8.0.0'}
@ -1892,6 +1952,9 @@ packages:
peerDependencies:
vue: '>=3.2.0'
any-promise@1.3.0:
resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==, tarball: https://registry.npmmirror.com/any-promise/-/any-promise-1.3.0.tgz}
anymatch@3.1.3:
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==, tarball: https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz}
engines: {node: '>= 8'}
@ -2267,6 +2330,10 @@ packages:
commander@2.20.3:
resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==, tarball: https://registry.npmmirror.com/commander/-/commander-2.20.3.tgz}
commander@4.1.1:
resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==, tarball: https://registry.npmmirror.com/commander/-/commander-4.1.1.tgz}
engines: {node: '>= 6'}
commander@7.2.0:
resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==, tarball: https://registry.npmmirror.com/commander/-/commander-7.2.0.tgz}
engines: {node: '>= 10'}
@ -2971,6 +3038,11 @@ packages:
resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==, tarball: https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz}
engines: {node: '>=12'}
escodegen@2.1.0:
resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==, tarball: https://registry.npmmirror.com/escodegen/-/escodegen-2.1.0.tgz}
engines: {node: '>=6.0'}
hasBin: true
eslint-config-prettier@9.1.0:
resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==, tarball: https://registry.npmmirror.com/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz}
hasBin: true
@ -3749,6 +3821,9 @@ packages:
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==, tarball: https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz}
engines: {node: '>=0.10.0'}
is-https@4.0.0:
resolution: {integrity: sha512-FeMLiqf8E5g6SdiVJsPcNZX8k4h2fBs1wp5Bb6uaNxn58ufK1axBqQZdmAQsqh0t9BuwFObybrdVJh6MKyPlyg==, tarball: https://registry.npmmirror.com/is-https/-/is-https-4.0.0.tgz}
is-inside-container@1.0.0:
resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==, tarball: https://registry.npmmirror.com/is-inside-container/-/is-inside-container-1.0.0.tgz}
engines: {node: '>=14.16'}
@ -3932,6 +4007,10 @@ packages:
engines: {node: '>=6'}
hasBin: true
jsonc-eslint-parser@2.4.0:
resolution: {integrity: sha512-WYDyuc/uFcGp6YtM2H0uKmUwieOuzeE/5YocFJLnLfclZ4inf3mRn8ZVy1s7Hxji7Jxm6Ss8gqpexD/GlKoGgg==, tarball: https://registry.npmmirror.com/jsonc-eslint-parser/-/jsonc-eslint-parser-2.4.0.tgz}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
jsonfile@6.1.0:
resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==, tarball: https://registry.npmmirror.com/jsonfile/-/jsonfile-6.1.0.tgz}
@ -4257,6 +4336,9 @@ packages:
ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==, tarball: https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz}
mz@2.7.0:
resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==, tarball: https://registry.npmmirror.com/mz/-/mz-2.7.0.tgz}
nanoid@3.3.7:
resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==, tarball: https://registry.npmmirror.com/nanoid/-/nanoid-3.3.7.tgz}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
@ -4653,6 +4735,10 @@ packages:
resolution: {integrity: sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==, tarball: https://registry.npmmirror.com/pinkie/-/pinkie-2.0.4.tgz}
engines: {node: '>=0.10.0'}
pirates@4.0.6:
resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==, tarball: https://registry.npmmirror.com/pirates/-/pirates-4.0.6.tgz}
engines: {node: '>= 6'}
pkg-types@1.1.3:
resolution: {integrity: sha512-+JrgthZG6m3ckicaOB74TwQ+tBWsFl3qVQg7mN8ulwSOElJ7gBhKzj2VkCPnZ4NlF6kEquYU+RIYNVAvzd54UA==, tarball: https://registry.npmmirror.com/pkg-types/-/pkg-types-1.1.3.tgz}
@ -5513,6 +5599,11 @@ packages:
peerDependencies:
postcss: ^8.4.31
sucrase@3.35.0:
resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==, tarball: https://registry.npmmirror.com/sucrase/-/sucrase-3.35.0.tgz}
engines: {node: '>=16 || 14 >=14.17'}
hasBin: true
superjson@2.2.1:
resolution: {integrity: sha512-8iGv75BYOa0xRJHK5vRLEjE2H/i4lulTjzpUXic3Eg8akftYjkmQDa8JARQ42rlczXyFR3IeRoeFCc7RxHsYZA==, tarball: https://registry.npmmirror.com/superjson/-/superjson-2.2.1.tgz}
engines: {node: '>=16'}
@ -5619,6 +5710,13 @@ packages:
text-table@0.2.0:
resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==, tarball: https://registry.npmmirror.com/text-table/-/text-table-0.2.0.tgz}
thenify-all@1.6.0:
resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==, tarball: https://registry.npmmirror.com/thenify-all/-/thenify-all-1.6.0.tgz}
engines: {node: '>=0.8'}
thenify@3.3.1:
resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==, tarball: https://registry.npmmirror.com/thenify/-/thenify-3.3.1.tgz}
through@2.3.8:
resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==, tarball: https://registry.npmmirror.com/through/-/through-2.3.8.tgz}
@ -5655,6 +5753,10 @@ packages:
resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==, tarball: https://registry.npmmirror.com/toidentifier/-/toidentifier-1.0.1.tgz}
engines: {node: '>=0.6'}
tosource@2.0.0-alpha.3:
resolution: {integrity: sha512-KAB2lrSS48y91MzFPFuDg4hLbvDiyTjOVgaK7Erw+5AmZXNq4sFRVn8r6yxSLuNs15PaokrDRpS61ERY9uZOug==, tarball: https://registry.npmmirror.com/tosource/-/tosource-2.0.0-alpha.3.tgz}
engines: {node: '>=10'}
totalist@3.0.1:
resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==, tarball: https://registry.npmmirror.com/totalist/-/totalist-3.0.1.tgz}
engines: {node: '>=6'}
@ -5668,6 +5770,9 @@ packages:
peerDependencies:
typescript: '>=4.2.0'
ts-interface-checker@0.1.13:
resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==, tarball: https://registry.npmmirror.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz}
tsconfig-paths@3.15.0:
resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==, tarball: https://registry.npmmirror.com/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz}
@ -6273,6 +6378,10 @@ packages:
yallist@4.0.0:
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==, tarball: https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz}
yaml-eslint-parser@1.2.3:
resolution: {integrity: sha512-4wZWvE398hCP7O8n3nXKu/vdq1HcH01ixYlCREaJL5NUMwQ0g3MaGFUBNSlmBtKmhbtVG/Cm6lyYmSVTEVil8A==, tarball: https://registry.npmmirror.com/yaml-eslint-parser/-/yaml-eslint-parser-1.2.3.tgz}
engines: {node: ^14.17.0 || >=16.0.0}
yaml@2.5.0:
resolution: {integrity: sha512-2wWLbGbYDiSqqIKoPjar3MPgB94ErzCtrNE1FdqGuaO0pi2JGjmE8aW8TDZwzU7vuxcGRdL/4gPQwQ7hD5AMSw==, tarball: https://registry.npmmirror.com/yaml/-/yaml-2.5.0.tgz}
engines: {node: '>= 14'}
@ -7034,11 +7143,36 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@intlify/bundle-utils@7.5.1(vue-i18n@9.14.0(vue@3.4.38(typescript@5.5.4)))':
dependencies:
'@intlify/message-compiler': 9.14.0
'@intlify/shared': 9.14.0
acorn: 8.12.1
escodegen: 2.1.0
estree-walker: 2.0.2
jsonc-eslint-parser: 2.4.0
magic-string: 0.30.11
mlly: 1.7.1
source-map-js: 1.2.0
yaml-eslint-parser: 1.2.3
optionalDependencies:
vue-i18n: 9.14.0(vue@3.4.38(typescript@5.5.4))
'@intlify/core-base@9.14.0':
dependencies:
'@intlify/message-compiler': 9.14.0
'@intlify/shared': 9.14.0
'@intlify/core@9.14.0':
dependencies:
'@intlify/core-base': 9.14.0
'@intlify/shared': 9.14.0
'@intlify/h3@0.5.0':
dependencies:
'@intlify/core': 9.14.0
'@intlify/utils': 0.12.0
'@intlify/message-compiler@9.14.0':
dependencies:
'@intlify/shared': 9.14.0
@ -7046,6 +7180,28 @@ snapshots:
'@intlify/shared@9.14.0': {}
'@intlify/unplugin-vue-i18n@3.0.1(rollup@4.21.0)(vue-i18n@9.14.0(vue@3.4.38(typescript@5.5.4)))':
dependencies:
'@intlify/bundle-utils': 7.5.1(vue-i18n@9.14.0(vue@3.4.38(typescript@5.5.4)))
'@intlify/shared': 9.14.0
'@rollup/pluginutils': 5.1.0(rollup@4.21.0)
'@vue/compiler-sfc': 3.4.38
debug: 4.3.6
fast-glob: 3.3.2
js-yaml: 4.1.0
json5: 2.2.3
pathe: 1.1.2
picocolors: 1.0.1
source-map-js: 1.2.0
unplugin: 1.12.2
optionalDependencies:
vue-i18n: 9.14.0(vue@3.4.38(typescript@5.5.4))
transitivePeerDependencies:
- rollup
- supports-color
'@intlify/utils@0.12.0': {}
'@ioredis/commands@1.2.0': {}
'@isaacs/cliui@8.0.2':
@ -7110,6 +7266,12 @@ snapshots:
- encoding
- supports-color
'@miyaneee/rollup-plugin-json5@1.2.0(rollup@4.21.0)':
dependencies:
'@rollup/pluginutils': 5.1.0(rollup@4.21.0)
json5: 2.2.3
rollup: 4.21.0
'@netlify/functions@2.8.1':
dependencies:
'@netlify/serverless-functions-api': 1.19.1
@ -7546,6 +7708,38 @@ snapshots:
- eslint-import-resolver-webpack
- supports-color
'@nuxtjs/i18n@8.5.2(magicast@0.3.4)(rollup@4.21.0)(vue@3.4.38(typescript@5.5.4))':
dependencies:
'@intlify/h3': 0.5.0
'@intlify/shared': 9.14.0
'@intlify/unplugin-vue-i18n': 3.0.1(rollup@4.21.0)(vue-i18n@9.14.0(vue@3.4.38(typescript@5.5.4)))
'@intlify/utils': 0.12.0
'@miyaneee/rollup-plugin-json5': 1.2.0(rollup@4.21.0)
'@nuxt/kit': 3.12.4(magicast@0.3.4)(rollup@4.21.0)
'@rollup/plugin-yaml': 4.1.2(rollup@4.21.0)
'@vue/compiler-sfc': 3.4.38
debug: 4.3.6
defu: 6.1.4
estree-walker: 3.0.3
is-https: 4.0.0
knitwork: 1.1.0
magic-string: 0.30.11
mlly: 1.7.1
pathe: 1.1.2
scule: 1.3.0
sucrase: 3.35.0
ufo: 1.5.4
unplugin: 1.12.2
vue-i18n: 9.14.0(vue@3.4.38(typescript@5.5.4))
vue-router: 4.4.3(vue@3.4.38(typescript@5.5.4))
transitivePeerDependencies:
- magicast
- petite-vue-i18n
- rollup
- supports-color
- vue
- vue-i18n-bridge
'@parcel/watcher-android-arm64@2.4.1':
optional: true
@ -7671,6 +7865,14 @@ snapshots:
optionalDependencies:
rollup: 4.21.0
'@rollup/plugin-yaml@4.1.2(rollup@4.21.0)':
dependencies:
'@rollup/pluginutils': 5.1.0(rollup@4.21.0)
js-yaml: 4.1.0
tosource: 2.0.0-alpha.3
optionalDependencies:
rollup: 4.21.0
'@rollup/pluginutils@4.2.1':
dependencies:
estree-walker: 2.0.2
@ -8549,6 +8751,8 @@ snapshots:
vue-types: 3.0.2(vue@3.4.38(typescript@5.5.4))
warning: 4.0.3
any-promise@1.3.0: {}
anymatch@3.1.3:
dependencies:
normalize-path: 3.0.0
@ -9035,6 +9239,8 @@ snapshots:
commander@2.20.3: {}
commander@4.1.1: {}
commander@7.2.0: {}
commander@8.3.0: {}
@ -9790,6 +9996,14 @@ snapshots:
escape-string-regexp@5.0.0: {}
escodegen@2.1.0:
dependencies:
esprima: 4.0.1
estraverse: 5.3.0
esutils: 2.0.3
optionalDependencies:
source-map: 0.6.1
eslint-config-prettier@9.1.0(eslint@8.57.0):
dependencies:
eslint: 8.57.0
@ -10750,6 +10964,8 @@ snapshots:
dependencies:
is-extglob: 2.1.1
is-https@4.0.0: {}
is-inside-container@1.0.0:
dependencies:
is-docker: 3.0.0
@ -10903,6 +11119,13 @@ snapshots:
json5@2.2.3: {}
jsonc-eslint-parser@2.4.0:
dependencies:
acorn: 8.12.1
eslint-visitor-keys: 3.4.3
espree: 9.6.1
semver: 7.6.3
jsonfile@6.1.0:
dependencies:
universalify: 2.0.1
@ -11258,6 +11481,12 @@ snapshots:
ms@2.1.3: {}
mz@2.7.0:
dependencies:
any-promise: 1.3.0
object-assign: 4.1.1
thenify-all: 1.6.0
nanoid@3.3.7: {}
nanoid@5.0.7: {}
@ -11854,6 +12083,8 @@ snapshots:
pinkie@2.0.4: {}
pirates@4.0.6: {}
pkg-types@1.1.3:
dependencies:
confbox: 0.1.7
@ -12721,6 +12952,16 @@ snapshots:
postcss: 8.4.41
postcss-selector-parser: 6.1.2
sucrase@3.35.0:
dependencies:
'@jridgewell/gen-mapping': 0.3.5
commander: 4.1.1
glob: 10.4.5
lines-and-columns: 1.2.4
mz: 2.7.0
pirates: 4.0.6
ts-interface-checker: 0.1.13
superjson@2.2.1:
dependencies:
copy-anything: 3.0.5
@ -12862,6 +13103,14 @@ snapshots:
text-table@0.2.0: {}
thenify-all@1.6.0:
dependencies:
thenify: 3.3.1
thenify@3.3.1:
dependencies:
any-promise: 1.3.0
through@2.3.8: {}
tiny-invariant@1.3.3: {}
@ -12887,6 +13136,8 @@ snapshots:
toidentifier@1.0.1: {}
tosource@2.0.0-alpha.3: {}
totalist@3.0.1: {}
tr46@0.0.3: {}
@ -12895,6 +13146,8 @@ snapshots:
dependencies:
typescript: 5.5.4
ts-interface-checker@0.1.13: {}
tsconfig-paths@3.15.0:
dependencies:
'@types/json5': 0.0.29
@ -13600,6 +13853,12 @@ snapshots:
yallist@4.0.0: {}
yaml-eslint-parser@1.2.3:
dependencies:
eslint-visitor-keys: 3.4.3
lodash: 4.17.21
yaml: 2.5.0
yaml@2.5.0: {}
yargs-parser@21.1.1: {}

Loading…
Cancel
Save