feat: 数据库迁移

This commit is contained in:
橙子
2023-10-07 17:51:05 +08:00
parent ca5697fb9c
commit c271f3005a
68 changed files with 222 additions and 18 deletions

16
Yi.App.Vue3/src/App.vue Normal file
View File

@@ -0,0 +1,16 @@
<template>
<!-- <HelloWorld msg="123"></HelloWorld> -->
<router-view></router-view>
</template>
<script setup lang="ts">
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,11 @@
import myaxios from '@/utils/myaxios'
export default {
operate(data:any) {
return myaxios({
url: `/agree/operate`,
method: 'get',
params: {articleId:data}
})
},
}

View File

@@ -0,0 +1,19 @@
import myaxios from '@/utils/myaxios'
import { ArticleEntity } from '@/type/interface/ArticleEntity'
export default {
add(data:any) {
return myaxios({
url: `/article/add`,
method: 'post',
data: data
})
},
pageList(data:any) {
return myaxios({
url: '/article/pageList',
method: 'get',
params: data
})
}
}

View File

@@ -0,0 +1,17 @@
import myaxios from '@/utils/myaxios'
export default {
add(data:any) {
return myaxios({
url: `/comment/add`,
method: 'post',
data: data
})
},
getListByArticleId(articleId:any) {
return myaxios({
url: `/comment/GetListByArticleId/${articleId}`,
method: 'get',
})
}
}

View File

@@ -0,0 +1,12 @@
import myaxios from '@/utils/myaxios'
export default{
upload(type:string,data:any){
return myaxios({
url: `/file/upload/${type}`,
headers:{"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"},
method: 'POST',
data:data
});
}
}

View File

@@ -0,0 +1,59 @@
import myaxios from '@/utils/myaxios'
// 登录方法
export function login(username:string, password:string, code:string, uuid:string) {
const data = {
username,
password,
code,
uuid
}
return myaxios({
url: '/account/login',
headers: {
isToken: false
},
method: 'post',
data: data
})
}
// 注册方法
export function register(data:any) {
return myaxios({
url: '/register',
headers: {
isToken: false
},
method: 'post',
data: data
})
}
// 获取用户详细信息
export function getInfo() {
return myaxios({
url: '/account/getUserAllInfo',
method: 'get'
})
}
// 退出方法
export function logout() {
return myaxios({
url: '/account/logout',
method: 'post'
})
}
// 获取验证码
export function getCodeImg() {
return myaxios({
url: '/account/captchaImage',
headers: {
isToken: false
},
method: 'get',
timeout: 20000
})
}

View File

@@ -0,0 +1,18 @@
import myaxios from '@/utils/myaxios'
export default {
add(data:any) {
return myaxios({
url: `/sku/add`,
method: 'post',
data: data
})
},
pageList(data:any) {
return myaxios({
url: '/sku/pageList',
method: 'get',
params: data
})
}
}

View File

@@ -0,0 +1,25 @@
import myaxios from '@/utils/myaxios'
export default {
add(data: any) {
return myaxios({
url: `/spu/add`,
method: 'post',
data: data
})
},
pageList(data: any) {
return myaxios({
url: '/spu/pageList',
method: 'get',
params: data
})
},
getById(id: any) {
return myaxios({
url: `/spu/GetById/${id}`,
method: 'get',
})
},
}

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>

After

Width:  |  Height:  |  Size: 496 B

View File

@@ -0,0 +1,61 @@
<template>
<div class="out-div">
<div class="card-div">
<van-image
radius="1rem"
width="100%"
height="100%"
src="https://unpkg.com/@vant/assets/cat.jpeg"
/>
</div>
<div class="bottom-div">
<div v-for="i of 10" :key="i" class="card-div-inside">
<van-image
radius="1rem"
width="4rem"
height="4rem"
src="https://unpkg.com/@vant/assets/cat.jpeg"
/>
<br>
79.0
</div>
</div>
</div>
</template>
<style scoped>
.out-div {
width: 100%;
min-height: 10rem;
border-radius: 1rem;
background-color: #ffffff;
}
.card-div {
height: 6rem;
width: 100%;
}
.bottom-div {
display: flex;
/* justify-content: center; */
width: 100%;
height: 6rem;
overflow: hidden;
overflow-x: auto;
white-space: nowrap;
}
.card-div-inside {
flex: none;
display: block;
margin-top: 0.5rem;
margin-left: 0.5rem;
margin-right: 0.5rem;
}
</style>

View File

@@ -0,0 +1,37 @@
<template>
<span class="subtitle">{{ showTime }}</span>
</template>
<style scoped>
</style>
<script setup lang="ts">
import { ref, computed } from "vue";
const props = defineProps<{ time: string }>();
const showTime = computed(() => {
var dataTime=new Date(Date.parse(props.time));
const hour:number= getHour(dataTime,new Date())
if(hour<=0)
{
return "刚刚"
}
if(hour<=6)
{
return hour+"小时前"
}
return props.time;
});
const getHour=(s1:Date, s2:Date)=> {
var ms = s2.getTime() - s1.getTime();
if (ms < 0) return 0;
return Math.floor(ms / 1000 / 60 / 60); //小时
}
</script>
<style scoped>
.subtitle{
color: #CBCBCB;
}
</style>

View File

@@ -0,0 +1,34 @@
<template>
<van-row>
<van-col span="24">
<p > {{data.head}}</p>
</van-col>
<van-col class="col-body" span="6" v-for="item in data.body"><van-icon :name="item.icon" size="2rem" /> <br>{{item.title}}</van-col>
</van-row>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import {AppGridData} from '@/type/class/AppGridData'
defineProps<{ data: AppGridData }>()
const count = ref(0)
</script>
<style scoped>
.col-body
{
text-align: center;
font-size: small;
}
.col-body .van-icon
{
color: #FF689B;
margin-bottom: 0.6rem;
}
p{
font-size: large;
font-weight:bold;
}
</style>

View File

@@ -0,0 +1,35 @@
<template>
<van-image
round
:width="width"
:height="height"
:src="url+(src??'0')"
/>
</template>
<script setup lang="ts">
import { type } from 'os';
import { ref } from 'vue'
const props= defineProps({
src: {type:String,default:'null',required:false},
width:{type:String,default:'3rem',required:false},
height:{type:String,default:'3rem',required:false}
})
const url = `${import.meta.env.VITE_APP_BASE_API}/file/`;
</script>
<style scoped>
.col-body
{
text-align: center;
}
.col-body .van-icon
{
margin-bottom: 0.6rem;
}
p{
font-size: large;
font-weight:bold;
}
</style>

View File

@@ -0,0 +1,19 @@
<script setup lang="ts">
import { ref } from 'vue'
defineProps<{ msg: string }>()
const count = ref(0)
</script>
<template>
<h1>{{ msg }}</h1>
</template>
<style scoped>
.read-the-docs {
color: #888;
}
</style>

View File

@@ -0,0 +1,76 @@
<template>
<van-tabbar v-model="active" active-color="#FF689B" inactive-color="#9C9C9C" @change="onChange" z-index="100">
<van-tabbar-item v-for="item in tabbar.slice(0,2)" :to="item.to" :icon="item.icon">{{item.title}}</van-tabbar-item>
<van-tabbar-item @click="show = true">
<template #icon="props">
<van-icon class="add" name="add-square" color="#FF689B" size="3rem" />
<!-- <img :src="props.active ? icon.active : icon.inactive" /> -->
</template>
</van-tabbar-item>
<van-tabbar-item v-for="item in tabbar.slice(3)" :to="item.to" :icon="item.icon">{{item.title}}</van-tabbar-item>
</van-tabbar>
<van-action-sheet v-model:show="show" >
<router-link to="/imageText"> <van-button class="btn1 btn " style="background-color: #5FBC76;" >发图文</van-button></router-link>
<van-button class="btn" style="background-color: #FF689B;">(暂未开放)发视频</van-button>
<van-button class="btn" style="background-color: #F7A63A;">(暂未开放)发文章</van-button>
<van-button class="btn" style="background-color: #6AB5EE;">(暂未开放)发二手</van-button>
<van-icon class="icon" name="cross" size="1.5rem" @click="show=false"/>
</van-action-sheet>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const active = ref(0);
const show = ref(false);
let tabbar=ref([
{icon:"wap-home",to:"/",title:"主页"},
{icon:"location-o",to:"",title:"发现"},
{icon:"",to:"",title:""},
{icon:"friends-o",to:"/shopIndex",title:"商城"},
// {icon:"friends-o",to:"",title:"商城"},
{icon:"setting-o",to:"/my",title:"我的"},
])
const onChange=(index:number)=>{
tabbar.value=[
{icon:"wap-home-o",to:"/",title:"主页"},
{icon:"location-o",to:"",title:"发现"},
{icon:"",to:"",title:""},
{icon:"friends-o",to:"/shopIndex",title:"商城"},
// {icon:"friends-o",to:"",title:"商城"},
{icon:"setting-o",to:"/my",title:"我的"},
];
tabbar.value[index].icon=tabbar.value[index].icon.replace("-o","")
}
</script>
<style>
.btn1
{
margin-top: 3rem !important;
}
.btn{
border-radius:0.5rem;
height: 4rem;
width:90%;
margin: 0.5rem 1rem 0.5rem 1rem;
color: #FFFFFF;
}
.content {
padding: 16px 16px 160px;
}
.icon{
margin-bottom: 1.5rem;
}
.add{
margin-top: 0.2rem;
}
</style>

View File

@@ -0,0 +1,9 @@
<template>
<van-nav-bar
title="标题"
left-text="返回"
left-arrow
/>
<br>
<router-view />
</template>

View File

@@ -0,0 +1,13 @@
<script setup lang="ts">
import { ref } from 'vue'
import AppBottom from './bottom/index.vue'
</script>
<template>
<router-view></router-view>
<app-bottom/>
</template>

View File

@@ -0,0 +1,12 @@
<template>
<app-tab />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import AppTab from '@/layout/tab/index.vue'
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,45 @@
<template >
<van-sticky :offset-top="0">
<van-row class="row" >
<van-col span="4" class="icon"><van-icon name="sign" size="1.6rem"/></van-col>
<van-col span="16">
<van-tabs v-model:active="active" class="tabs" sticky swipeable color="#FF689B">
<van-tab v-for="item in tabs" :title="item.title" :to="item.to" class="tab" :style="{fontSize: 0 + 'px'}" ></van-tab>
</van-tabs>
</van-col>
<van-col span="4" class="icon"><van-icon name="search" size="1.6rem" /></van-col>
</van-row>
</van-sticky>
<router-view />
<div style=""></div>
</template>
<script setup lang="ts">
import { ref } from "vue";
const active = ref(1);
const tabs=ref([
{title:"关注",to:"/follow"},
{title:"推荐",to:"/recommend"},
{title:"广场",to:"/square"},
])
</script>
<style scoped>
.row{
background-color: #FFFFFF;
min-width: 24rem;
}
.tab{
}
.icon{
padding-top: 0.6rem;
}
.tabs {
width: 100%;
}
.icon .van-icon
{
color:#FF689B;
}
</style>

17
Yi.App.Vue3/src/main.ts Normal file
View File

@@ -0,0 +1,17 @@
import { createApp } from 'vue'
import './style.css'
import 'vant/es/image-preview/style';
import 'vant/es/toast/style';
import 'vant/es/dialog/style';
import 'vant/es/notify/style';
import router from './router'
import store from './store'
import './permission'
import { Lazyload } from 'vant';
import App from './App.vue'
const app=createApp(App)
app.use(router)
app.use(store)
app.use(Lazyload);
app.mount('#app');

View File

@@ -0,0 +1,52 @@
import router from './router'
// import { ElMessage } from 'element-plus'
// import NProgress from 'nprogress'
// import 'nprogress/nprogress.css'
import { getToken } from '@/utils/auth'
// import { isHttp } from '@/utils/validate'
import useUserStore from '@/store/modules/user'
import { isRelogin } from '@/utils/myaxios'
// import useSettingsStore from '@/store/modules/settings'
// import usePermissionStore from '@/store/modules/permission'
// NProgress.configure({ showSpinner: false });
const whiteList = ['/login', '/auth-redirect', '/bind', '/register'];
router.beforeEach((to, from, next) => {
// NProgress.start()
if (getToken()) {
// to.meta.title && useSettingsStore().setTitle(to.meta.title)
/* has token*/
if (to.path === '/login') {
next({ path: '/' })
// NProgress.done()
} else {
if (useUserStore().roles.length === 0) {
isRelogin.show = true
useUserStore().getInfo().then((response: any) => {
next()
});
}
else
{
next()
}
}
} else {
// 没有token
if (whiteList.indexOf(to.path) !== -1) {
// 在免登录白名单,直接进入
next()
} else {
next(`/login?redirect=${to.fullPath}`) // 否则全部重定向到登录页
// NProgress.done()
}
}
})
router.afterEach(() => {
// NProgress.done()
})

View File

@@ -0,0 +1,92 @@
import { createWebHistory, createRouter } from 'vue-router';
import Layout from '@/layout/index.vue';
import HeadLayout from '@/layout/head/index.vue'
export const constantRoutes = [
{
name:'Layout',
path: '/',
component: Layout,
redirect:"/recommend",
children: [
{
path: '/shopIndex',
component: () => import('@/view/shop/shopIndex.vue'),
name: 'ShopIndex',
},
{
path: '/my',
component: () => import('@/view/my.vue'),
name: 'My',
},
{
path: '/main',
component: () => import('@/layout/main/index.vue'),
name: 'Main',
children:[
{
path: '/recommend',
component: () => import('@/view/main/recommend.vue'),
name: 'Recommend',
},
{
path: '/follow',
component: () => import('@/view/main/follow.vue'),
name: 'Follow',
},
{
path: '/square',
component: () => import('@/view/main/square.vue'),
name: 'Square',
},
]
}
]
},
{
path: '/imageText',
component: () => import('@/view/send/imageText.vue'),
name: 'ImageText',
},
{
path: '/login',
component: () => import('@/view/login.vue'),
name: 'Login',
},
{
name:'Shop',
path: '/shop',
component: HeadLayout,
redirect:"/shopIndex",
children: [
{
path: '/shopDetails',
component: () => import('@/view/shop/shopDetails.vue'),
name: 'ShopDetails',
},
{
path: '/shopSearch',
component: () => import('@/view/shop/shopSearch.vue'),
name: 'ShopSearch',
},
]
}
];
const router = createRouter({
history: createWebHistory(),
routes: constantRoutes,
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else {
return { top: 0 }
}
},
});
export default router;

View File

@@ -0,0 +1,4 @@
import { createPinia } from 'pinia'
const store = createPinia()
export default store

View File

@@ -0,0 +1,85 @@
import { login, logout, getInfo } from '@/api/login'
import { getToken, setToken, removeToken } from '@/utils/auth'
// import defAva from '@/assets/images/profile.jpg'
import {defineStore} from 'pinia'
const useUserStore = defineStore(
'user',
{
state: () => ({
token: getToken(),
user:{username:"",nick:"",icon:""},
roles: [],
permissions: []
}),
actions: {
// 登录
login(userInfo:any ) {
const username = userInfo.username.trim()
const password = userInfo.password
const code = userInfo.code
const uuid = userInfo.uuid
return new Promise((resolve, reject) => {
login(username, password, code, uuid).then(res => {
if(!res.status)
{
reject(res)
}
setToken(res.data.token);
this.token = res.data.token;
resolve(res);
}).catch(error => {
reject(error)
})
})
},
// 获取用户信息
getInfo() {
return new Promise((resolve, reject) => {
getInfo().then(response => {
const res=response.data;
const user = res.user
// const avatar = (user.avatar == "" || user.avatar == null) ? defAva : import.meta.env.VITE_APP_BASE_API + user.avatar;
if (res.roleCodes && res.roleCodes.length > 0) { // 验证返回的roles是否是一个非空数组
this.roles = res.roleCodes
this.permissions = res.permissionCodes
// this.roles = ["admin"];
// this.permissions=["*:*:*"]
} else {
this.roles = ["ROLE_DEFAULT"] as never[]
}
// this.roles = ["admin"];
// this.permissions=["*:*:*"]
this.user.username = user.userName;
this.user.nick=user.nick
this.user.icon = user.icon;
resolve(res)
}).catch(error => {
reject(error)
})
})
},
// 退出系统
logOut() {
return new Promise((resolve, reject) => {
//this.token
logout().then((response) => {
this.token = ''
this.roles = []
this.permissions = []
removeToken()
resolve(response)
}).catch(error => {
reject(error)
})
})
}
}
})
export default useUserStore

87
Yi.App.Vue3/src/style.css Normal file
View File

@@ -0,0 +1,87 @@
#app {
max-width: 1280px;
margin: 0 auto;
padding: 0rem;
text-align: center;
}
/* :root {
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
font-size: 16px;
line-height: 24px;
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
}
a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
}
a:hover {
color: #535bf2;
}
body {
margin: 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
}
h1 {
font-size: 3.2em;
line-height: 1.1;
}
button {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
cursor: pointer;
transition: border-color 0.25s;
}
button:hover {
border-color: #646cff;
}
button:focus,
button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
}
.card {
padding: 2em;
}
#app {
max-width: 1280px;
margin: 0 auto;
padding: 0rem;
text-align: center;
}
@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #ffffff;
}
a:hover {
color: #747bff;
}
button {
background-color: #f9f9f9;
}
} */

View File

@@ -0,0 +1,11 @@
export class AppGridData{
head!: string;
body!: AppGridBody[];
}
class AppGridBody{
icon!: string;
title!: string;
}

View File

@@ -0,0 +1,8 @@
export interface ArticleEntity{
title: string;
content: string;
images:string[];
isDeleted:boolean;
createTime:string;
}
// import { ArticleEntity } from '@/type/interface/ArticleEntity'

View File

@@ -0,0 +1,13 @@
const TokenKey = 'Admin-Token'
export function getToken() {
return localStorage.getItem(TokenKey)
}
export function setToken(token:string) {
return localStorage.setItem(TokenKey, token)
}
export function removeToken() {
return localStorage.removeItem(TokenKey)
}

View File

@@ -0,0 +1,81 @@
import axios from 'axios'
// import store from '../store/index'
// import vm from '../main'
import JsonBig from 'json-bigint'
import { getToken } from '@/utils/auth'
import { useRouter } from "vue-router";
import useUserStore from '@/store/modules/user'
import { Notify } from 'vant';
// import VuetifyDialogPlugin from 'vuetify-dialog/nuxt/index';
export let isRelogin = { show: false };
const myaxios = axios.create({
// baseURL:'/'//
baseURL: import.meta.env.VITE_APP_BASE_API, // /dev-apis
timeout: 50000,
headers: {
'Authorization': 'Bearer ' + ""
},
//雪花id精度问题
transformResponse: [ data => {
const json = JsonBig({
storeAsString: true
})
return json.parse(data)
}],
})
// 请求拦截器
myaxios.interceptors.request.use(function(config:any) {
const isToken = (config.headers || {}).isToken === false
// 是否需要防止数据重复提交
const isRepeatSubmit = (config.headers || {}).repeatSubmit === false
if (getToken() && !isToken) {
config.headers['Authorization'] = 'Bearer ' + getToken()
}
// store.dispatch("openLoad");
return config;
}, function(error) {
return Promise.reject(error);
});
// 响应拦截器
myaxios.interceptors.response.use(async function(response) {
//成功
const resp = response.data
if(resp.code==401)
{
Notify({ type: 'warning', message: '登录过期' });
//登出
useUserStore().logOut().then(() => {
location.href = '/';
})
isRelogin.show = false;
}
// store.dispatch("closeLoad");
return resp;
}, async function(error) {
//未授权、失败
if(error.response==undefined)
{
Notify({ type: 'danger', message: `服务器异常:${error.message}` });
return Promise.reject(error);;
}
const resp = error.response.data
if (resp.code == undefined && resp.message == undefined) {
Notify({ type: 'danger', message: '未知错误' });
} else if (resp.code == 401) {
// if (!isRelogin.show) {
Notify({ type: 'warning', message: '登录过期' });
//登出
useUserStore().logOut().then(() => {
location.href = '/';
})
isRelogin.show = false;
// }
} else if (resp.code !== 200) {
Notify({ type: 'danger', message: `错误代码:${resp.code},原因:${resp.message}` });
}
return Promise.reject(error);
});
export default myaxios

View File

@@ -0,0 +1,138 @@
<template>
<div class="div-top">
<span class="title">意框架</span>
<br />
<span class="subtitle">有幸相遇不负未来</span>
</div>
<div class="div-bottom">
<h5>密码登录</h5>
<van-field
class="van-field-username"
v-model="loginForm.username"
label="用户"
placeholder="请输入用户名"
/>
<van-field
class="van-field-password"
v-model="loginForm.password"
label="密码"
type="password"
placeholder="请输入密码"
/>
<van-button type="primary" @click="login">进入意框架</van-button>
<p>其他方式登录<van-icon name="arrow" /></p>
<van-row class="row-bottom" style="margin-top: 6rem">
<van-col span="24"><p>第三方登录</p></van-col>
<van-col span="2"></van-col>
<van-col span="5"><van-icon name="smile-o" size="2rem" /></van-col>
<van-col span="5"><van-icon name="smile-o" size="2rem" /></van-col>
<van-col span="5"><van-icon name="smile-o" size="2rem" /></van-col>
<van-col span="5"><van-icon name="smile-o" size="2rem" /></van-col>
<van-col span="2"></van-col>
</van-row>
</div>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { useRouter } from "vue-router";
import useUserStore from "@/store/modules/user";
import { Toast } from "vant";
const router = useRouter();
const redirect = ref(undefined);
const loginForm = ref({
username: "",
password: "",
rememberMe: false,
code: "",
uuid: "",
});
const userStore = useUserStore();
const login = () => {
// 调用action的登录方法
userStore
.login(loginForm.value)
.then((response: any) => {
Toast({
message: response.message,
position: "bottom",
});
router.push({ path: redirect.value || "/" });
})
.catch((response:any) => {
loginForm.value.password="";
Toast({
message: response.message,
position: "bottom",
});
// loading.value = false;
// // 重新获取验证码
// if (captchaEnabled.value) {
// getCode();
// }
});
};
</script>
<style scoped>
.div-top {
background-color: #FF689B;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 60%;
}
.div-bottom {
background-color: #FFFFFF;
position: absolute;
top: 25%;
left: 0;
right: 0;
bottom: 0;
border-radius: 3rem 3rem 0rem 0rem;
padding: 1rem 2rem 2rem 2rem;
/* min-height: 70%; */
}
.title {
position: absolute;
top: 15%;
transform: translateX(-50%);
font-size: 1.8rem;
font-weight: bolder;
color: #FFFFFF;
}
.subtitle {
transform: translateX(-50%);
position: absolute;
top: 30%;
font-weight: lighter;
color: #FFFFFF;
}
.van-field-username {
margin-top: 2rem;
}
.van-field-password {
margin-top: 1rem;
}
h5 {
text-align: left;
font-size: 1.1rem;
font-weight: bolder;
}
.div-bottom .van-button {
margin-top: 1rem;
width: 100%;
border-radius: 0.4rem;
background-color: #FF689B;
border: 0;
}
.div-bottom p {
text-align: center;
color: #666666;
}
.row-bottom {
color: #FF689B;
}
</style>

View File

@@ -0,0 +1,16 @@
<template >
<van-row >
<van-col span="24">
<div class="test">这里是关注页面</div>
</van-col>
</van-row>
</template>
<style scoped>
.test
{
}
</style>

View File

@@ -0,0 +1,299 @@
<template>
<van-pull-refresh v-model="refreshing" @refresh="onRefresh">
<van-list
class="list"
v-model:loading="loading"
:finished="finished"
finished-text="没有更多了"
@load="onLoad"
>
<van-row v-for="(item, index) in articleList" :key="index" class="row">
<van-col span="4" class="leftCol">
<AppUserIcon
width="3rem"
height="3rem"
:src="item.user == null ? null : item.user.icon"
/>
</van-col>
<van-col span="14" class="centerTitle">
<span class="justtitle">{{
item.user == null ? "-" : item.user.nick ?? item.user.username
}}</span>
<br />
<app-createTime :time="item.createTime" />
</van-col>
<van-col span="6" class="down">
<van-icon name="arrow-down" @click="show = true" />
</van-col>
<van-col class="rowBody" span="24">{{ item.content }}</van-col>
<van-col
span="8"
v-for="(image, imageIndex) in item.images"
:key="imageIndex"
class="imageCol"
@click="openImage(item.images, imageIndex)"
>
<van-image
lazy-load
fit="cover"
width="100%"
height="7rem"
:src="url + image + '/true'"
radius="5"
/>
<template v-slot:loading>
<van-loading type="spinner" size="20" />
</template>
</van-col>
<van-col span="24" class="bottomRow">
<van-grid direction="horizontal" :column-num="3">
<van-grid-item icon="share-o" text="分享" />
<van-grid-item
icon="comment-o"
text="评论"
@click="openComment(item.id)"
/>
<van-grid-item
icon="good-job-o"
:text="`点赞:${item.agreeNum}`"
@click="aggreeHand(item.id)"
/>
</van-grid>
</van-col>
</van-row>
</van-list>
</van-pull-refresh>
<!-- 功能页面 -->
<van-action-sheet
v-model:show="show"
:actions="actions"
cancel-text="取消"
close-on-click-action
/>
<!-- 图片预览 -->
<van-image-preview
v-model:show="imageShow"
:images="imagesPreview"
:startPosition="startIndex"
@change="onChange"
:closeable="true"
>
<template v-slot:index>{{ index + 1 }}</template>
</van-image-preview>
<!-- 评论面板 -->
<van-action-sheet v-model:show="commentShow" title="共10条评论">
<van-row v-for="i in commentList" :key="i" class="commentContent">
<van-col span="4">头像</van-col>
<van-col span="16">{{ i.content }}</van-col>
<van-col span="4">点赞</van-col>
</van-row>
<van-cell-group inset>
<van-field v-model="commentData.content" placeholder="请输入评论" >
<template #button>
<van-button size="small" type="primary" @click="sendComment()">发布</van-button>
</template>
</van-field>
</van-cell-group>
</van-action-sheet>
</template>
<script setup lang="ts">
import { ref, onMounted, reactive, toRefs } from "vue";
import { ImagePreview, Toast } from "vant";
import AppCreateTime from "@/components/AppCreateTime.vue";
import AppUserIcon from "@/components/AppUserIcon.vue";
import articleApi from "@/api/articleApi";
import agreeApi from "@/api/agreeApi";
import commentApi from "@/api/commentApi";
import { ArticleEntity } from "@/type/interface/ArticleEntity";
const VanImagePreview = ImagePreview.Component;
const url = `${import.meta.env.VITE_APP_BASE_API}/file/`;
const data = reactive({
queryParams: {
pageNum: 1,
pageSize: 10,
// dictName: undefined,
// dictType: undefined,
isDeleted: false,
},
});
const commentData = reactive({
content: "",
articleId:0
});
const sendComment=()=>{
commentData.articleId=openCommentId.value;
commentApi.add(commentData).then(()=>{
getCommentList(openCommentId.value);
commentData.content="";
})
}
const { queryParams } = toRefs(data);
const {content}=toRefs(commentData);
const articleList = ref<any[]>([]);
const commentList = ref<any[]>([]);
const totol = ref<Number>(0);
const imageShow = ref(false);
const commentShow = ref<any>(false);
const index = ref(0);
let imagesPreview = ref<string[]>([]);
const openCommentId=ref(0);
const openComment = (id: any) => {
commentShow.value = true;
openCommentId.value=id;
getCommentList(id);
};
const onChange = (newIndex: any) => {
index.value = newIndex;
};
const list = ref<Number[]>([]);
const loading = ref(false);
const finished = ref(false);
const refreshing = ref(false);
const startIndex = ref(0);
const show = ref(false);
const actions = [{ name: "取消关注" }, { name: "将TA拉黑" }, { name: "举报" }];
const onLoad = async () => {
if (refreshing.value) {
articleList.value = [];
refreshing.value = false;
}
// 异步更新数据
// setTimeout 仅做示例,真实场景中一般为 ajax 请求
articleApi.pageList(queryParams.value).then((response: any) => {
if (response.data.data.length == 0) {
console.log("结束");
finished.value = true;
} else {
console.log("执行");
articleList.value.push(...response.data.data);
totol.value = response.data.totol;
queryParams.value.pageNum += 1;
}
loading.value = false;
console.log(loading.value);
});
};
const onRefresh = () => {
finished.value = false;
// 重新加载数据
// 将 loading 设置为 true表示处于加载状态
loading.value = true;
queryParams.value.pageNum = 1;
onLoad();
};
const openImage = (imagesUrl: string[], imageIndex: any) => {
imagesPreview.value = imagesUrl.map((i) => url + i);
startIndex.value = imageIndex;
imageShow.value = true;
};
onMounted(() => {
articleList.value = [];
// getList();
});
const getCommentList = (id: any) => {
commentApi.getListByArticleId(id).then((response: any) => {
commentList.value = response.data;
});
};
const getList = () => {
articleApi.pageList(queryParams.value).then((response: any) => {
articleList.value.push(...response.data.data);
totol.value = response.data.totol;
});
};
const aggreeHand = (articleId: any) => {
agreeApi.operate(articleId).then((response: any) => {
//更改显示的值
if (response.status) {
articleList.value.filter((p) => p.id == articleId)[0].agreeNum += 1;
} else {
articleList.value.filter((p) => p.id == articleId)[0].agreeNum -= 1;
}
Toast({
message: response.message,
position: "bottom",
});
});
};
</script>
<style scoped>
.list {
background-color: #f4f4f4;
}
.row {
background-color: white;
padding-top: 1rem;
padding-left: 1rem;
padding-right: 1rem;
margin-bottom: 0.6rem;
}
.rowBody {
text-align: left;
background-color: white;
margin-top: 1rem;
margin-bottom: 1rem;
}
.title {
padding-top: 1rem;
min-height: 3rem;
text-align: left;
}
.leftCol {
align-content: left;
text-align: left;
}
.centerTitle {
text-align: left;
}
.imageCol {
padding: 0.1rem 0.1rem 0.1rem 0.1rem;
}
.subtitle {
color: #cbcbcb;
}
.justtitle {
font-size: large;
}
.bottomRow {
color: #999999;
}
.down {
text-align: right;
padding-right: 0.5rem;
}
.commentContent {
margin-bottom: 4rem;
}
</style>

View File

@@ -0,0 +1 @@
<template>这里是广场</template>

256
Yi.App.Vue3/src/view/my.vue Normal file
View File

@@ -0,0 +1,256 @@
<template>
<van-row class="headRow">
<van-col span="2"><van-icon name="scan" size="1.5rem" /></van-col>
<van-col span="20"></van-col>
<van-col span="2"
><van-icon name="setting-o" size="1.5rem" @click="show = true"
/></van-col>
</van-row>
<van-row class="bodyRow">
<van-col span="6" class="leftCol">
<AppUserIcon width="4rem" height="4rem" :src="user.icon"></AppUserIcon>
</van-col>
<van-col span="12" class="title"><span>{{user.nick}}</span></van-col>
<van-col span="6" class="subtitle"
><span>个人主页<van-icon name="arrow" /></span
></van-col>
<van-col span="6" class="bodyCol"
><div><span>6</span><br />关注</div></van-col
>
<van-col span="6" class="bodyCol"
><div><span>3</span><br />粉丝</div></van-col
>
<van-col span="6" class="bodyCol"
><div><span>0</span><br />人气</div></van-col
>
<van-col span="6" class="bodyCol"
><div><span>3</span><br />钱钱</div></van-col
>
<van-col span="24">
<van-row class="btnRow">
<van-col span="12">
<van-button class="btn">
<van-icon name="bag" size="1.8rem" /> <span>我的购物</span><van-icon
name="arrow"
size="1.2rem" /></van-button
></van-col>
<van-col span="12">
<van-button class="btn"
><van-icon name="send-gift" size="1.8rem" /> <span>我的签到</span><van-icon
name="arrow"
size="1.2rem" /></van-button
></van-col>
</van-row>
</van-col>
<AppGrid class="grid" :data="data1"></AppGrid>
<AppGrid class="grid" :data="data2"></AppGrid>
<AppGrid class="grid" :data="data3"></AppGrid>
</van-row>
<van-popup
v-model:show="show"
position="right"
:style="{ height: '100%', width: '100%', backgroundColor: '#F8F8F8' }"
>
<van-nav-bar
title="设置"
left-text="返回"
left-arrow
@click-left="show = false"
/>
<van-cell-group>
<van-cell title="账户与安全" is-link />
<van-cell title="黑名单" is-link />
<van-cell title="推送设置" is-link />
<van-cell title="隐私管理" is-link />
<van-cell title="通用设置" is-link />
</van-cell-group>
<van-cell-group class="group">
<van-cell title="家庭入驻" is-link />
<van-cell title="社区入驻" is-link />
</van-cell-group>
<van-cell-group class="group">
<van-cell title="清理缓存" is-link />
<van-cell title="检测更新" is-link />
<van-cell title="关于我们" is-link />
<van-cell title="给个好评" is-link />
</van-cell-group>
<van-button type="danger" @click="outLog">退出登录</van-button>
</van-popup>
<!-- <van-popup v-model:show="show" position="right">
<div class="body-div"> 内容</div>
</van-popup> -->
</template>
<script setup lang="ts">
import AppGrid from "@/components/AppGrid.vue";
import { AppGridData } from "@/type/class/AppGridData";
import { ref } from "vue";
import { Dialog } from "vant";
import useUserStore from "@/store/modules/user";
import { storeToRefs } from 'pinia';
import AppUserIcon from "@/components/AppUserIcon.vue";
const show = ref<boolean>(false);
let data1: AppGridData = {
head: "个人中心",
body: [
{
title: "我的消息",
icon: "comment-o",
},
{
title: "我的聊天",
icon: "chat-o",
},
{
title: "我的喜欢",
icon: "like-o",
},
{
title: "我的关注",
icon: "user-o",
},
],
};
let data2: AppGridData = {
head: "功能",
body: [
{
title: "排行榜",
icon: "medal-o",
},
{
title: "活动报名",
icon: "balance-list-o",
},
{
title: "钱钱兑换",
icon: "gem-o",
},
{
title: "全网上新",
icon: "gift-card-o",
},
],
};
let data3: AppGridData = {
head: "服务",
body: [
{
title: "客服",
icon: "service-o",
},
{
title: "小黑屋",
icon: "wap-home-o",
},
{
title: "邀请好友",
icon: "friends-o",
},
],
};
const userStore=useUserStore();
const {user}=storeToRefs(useUserStore());
const outLog = () => {
Dialog.confirm({
title: "提示",
message: "确定退出当前用户吗?",
})
.then(() => {
userStore
.logOut()
.then((response: any) => {
location.href = "/";
});
})
.catch(() => {
// on cancel
});
};
</script>
<style scoped>
.bodyCol
{
color: #9B9B9B;
}
.bodyCol span{
color: black;
font-size:larger;
font-weight: 500;
}
.btn .van-icon{
color: #FF689B;
}
.btn span{
font-size:medium;
font-weight:600;
}
.grid {
width: 100%;
margin-top: 1rem;
}
.headRow {
padding: 0.5rem 0.5rem 0.5rem 0.5rem;
}
.bodyRow {
text-align: left;
margin-top: 1.5rem;
padding-left: 1.5rem;
padding-right: 1.5rem;
padding-bottom: 4rem;
}
.title {
font-size: 1.5rem;
line-height: 4rem;
}
.subtitle {
line-height: 4rem;
color: #9B9B9B;
}
.bodyCol {
text-align: center;
font-size: 1.2rem;
margin-top: 1rem;
}
.btn {
width: 100%;
margin: 0;
padding: 0;
background-color: #ffffff;
border: none;
color: black;
}
.btnRow {
margin-top: 1.5rem;
box-shadow: 0rem 0rem 0.2rem 0.2rem #f3f3f3;
}
.btnRow .van-button {
font-size: 1.2rem;
}
.van-icon-send-gift {
margin-right: 0.5rem;
}
.btnRow .van-icon-arrow {
margin-left: 0.45rem;
}
.van-cell {
text-align: left;
}
.group {
margin-top: 1rem;
}
.van-popup .van-button {
width: 90%;
margin-top: 2rem;
}
</style>

View File

@@ -0,0 +1,185 @@
<template>
<transition name="van-slide-right">
<div v-show="visible">
<van-sticky>
<van-row class="head-row">
<van-col span="3">
<router-link to="/recommend">
<van-icon name="arrow-left" size="1.5rem" />
</router-link>
</van-col>
<van-col span="18"><span>发图文</span></van-col>
<van-col
span="3"
@click="send"
:style="{ color: isSend ? '#FE70A0' : '#979797' }"
>发布</van-col
>
</van-row>
</van-sticky>
<van-cell-group>
<van-field
rows="5"
autosize
type="textarea"
v-model="content"
label-width="0"
:show-word-limit="true"
maxlength="500"
placeholder="大于5字每一天都是为了下一天"
/>
</van-cell-group>
<van-row class="body-row">
<van-col span="10">
<van-icon name="share-o" size="1.5rem" /><span>发布到去其他</span>
</van-col>
<van-col span="4"></van-col>
<van-col span="10"
><span class="right-span">选择更多人看到</span>
<van-icon name="arrow" size="1.2rem" />
</van-col>
</van-row>
<van-divider />
<van-row>
<van-col class="img-col" span="24">
<van-uploader
accept="image/*"
:after-read="afterRead"
v-model="fileList"
multiple
/>
</van-col>
</van-row>
</div>
</transition>
</template>
<script setup lang="ts">
import { ref, onMounted, reactive, toRefs, watch } from "vue";
import { ArticleEntity } from "@/type/interface/ArticleEntity";
import fileApi from "@/api/fileApi";
import articleApi from "@/api/articleApi";
import { Toast } from "vant";
import { useRouter } from "vue-router";
const router = useRouter();
const form = reactive<any>({
title: "",
content: "",
images: [],
isDeleted: false,
});
const isSend = ref(false);
const { images, content } = toRefs(form);
const fileList = ref([]);
const visible = ref<boolean>(false);
onMounted(() => {
visible.value = true;
});
const afterRead = (file: any) => {
file.status = "uploading";
file.message = "上传中...";
var formData = new FormData();
//一个文件
if (file.length == undefined) {
formData.append("file", file.file);
} else {
//多个文件
file.forEach((f: any) => {
formData.append("file", f.file);
f.status = "uploading";
f.message = "上传中...";
});
Toast({
message: "全部文件正在上传",
position: "bottom",
});
}
fileApi.upload("image", formData).then((response: any) => {
images.value.push(...response.data);
if (file.length == undefined) {
file.status = "done";
file.message = "成功";
} else {
//多个文件
file.forEach((f: any) => {
f.status = "done";
f.message = "成功";
});
Toast({
message: "全部文件上传成功",
position: "bottom",
});
}
});
};
const send = () => {
if (form.content.length < 5) {
Toast({
message: "请输入至少5个字符",
position: "bottom",
});
} else {
articleApi.add(form).then((response: any) => {
router.push({ path: "/recommend" });
});
}
};
watch(
() => form.content,
(newValue, oldValue) => {
if (newValue.length < 5) {
isSend.value = false;
} else {
isSend.value = true;
}
}
);
</script>
<style scoped>
.head-row {
background-color: #f8f8f8;
padding: 1.2rem 1rem 0.8rem 1rem;
}
.head-row span {
font-size: large;
}
.van-field-5-label {
display: none;
}
.body-row {
margin-top: 1rem;
}
.preview-cover {
position: absolute;
bottom: 0;
box-sizing: border-box;
width: 100%;
padding: 4px;
color: #fff;
font-size: 12px;
text-align: center;
background: rgba(0, 0, 0, 0.3);
}
.van-uploader {
margin: 0 1.2rem 0 1.2rem;
}
.img-col {
text-align: left;
}
.right-span {
color: #979797;
}
</style>

View File

@@ -0,0 +1,44 @@
<template>
这里是商品详情页当所有规格组全部选择完后就会匹配sku列表匹配成功才显示价格即绑定好的sku
<div >商品名称{{spuItem.spuName}}</div>
<div v-for="spec in spuItem.specsSpuAllInfo" :key="spec">规格组 {{spec.specsGroupName}}
<div v-for="name in spec.specsNames" :key="name">规格值 {{name}}</div>
</div>
<hr>
<div v-for="sku in spuItem.skus" :key="sku"> 价格{{sku.price}}<br>Sku{{sku}}
<hr>
</div>
<br>
<router-link to="/shopIndex">返回商品首页</router-link>
<van-action-bar>
<van-action-bar-icon icon="chat-o" text="客服" dot />
<van-action-bar-icon icon="cart-o" text="购物车" badge="5" />
<van-action-bar-icon icon="shop-o" text="店铺" badge="12" />
<van-action-bar-button type="warning" text="加入购物车" />
<van-action-bar-button type="danger" text="立即购买" />
</van-action-bar>
</template>
<script lang="ts" setup>
import {ref,onMounted} from 'vue'
import { useRouter } from 'vue-router'
import spuApi from "@/api/spuApi";
const router = useRouter();
const spuItem=ref<any>({});
onMounted(() => {
const spuId=router.currentRoute.value.query.spuId;
// 打印
spuApi.getById(spuId).then((response:any)=>{
spuItem.value=response.data
})
})
</script>>

View File

@@ -0,0 +1,79 @@
<template >
<div class="back">
<van-row class="top-div"> <van-col span="3"><van-icon name="circle" size="2rem" /></van-col>
<van-col span="18"> <router-link to="/shopSearch"><van-cell-group inset>
<van-field label="搜索" placeholder="搜索" />
</van-cell-group> </router-link></van-col>
<van-col span="3"><van-icon name="circle" size="2rem" /></van-col></van-row>
<div class="head-div">
<van-swipe
height="100"
class="my-swipe"
:autoplay="3000"
indicator-color="white"
>
<van-swipe-item>1</van-swipe-item>
<van-swipe-item>2</van-swipe-item>
<van-swipe-item>3</van-swipe-item>
<van-swipe-item>4</van-swipe-item>
</van-swipe>
</div>
<van-row class="body-row">
<van-col span="24">
<van-grid :column-num="4">
<van-grid-item
v-for="value in 8"
:key="value"
icon="photo-o"
text="文字"
/>
</van-grid>
</van-col>
<van-col span="24"> <AppCard /></van-col>
<van-col span="24"> <AppCard /></van-col>
<van-col span="24"> <AppCard /></van-col>
</van-row>
</div>
</template>
<script setup lang="ts">
import AppCard from "@/components/AppCard.vue";
</script>
<style>
.my-swipe .van-swipe-item {
color: #fff;
font-size: 20px;
line-height: 150px;
text-align: center;
background-color: #39a9ed;
}
.body-row {
padding: 1rem;
padding-top: 0.2rem;
margin-top: 1rem;
}
.van-swipe {
/* margin-top: 10rem;
margin-left: 1rem;
margin-right: 1rem; */
}
.head-div {
padding: 1rem;
padding-bottom: 0;
}
.body-row .van-col {
margin-bottom: 2rem;
}
.top-div
{
padding: 1rem;
padding-bottom: 0;
}
.back{
background-color: #f4f4f4;
}
</style>

View File

@@ -0,0 +1,39 @@
<template>
这里是商品搜索页
<br>
<br>
<router-link to="/shopIndex">返回商品首页</router-link>
这个是spu:
<div v-for="item in spuList" :key="item.id">商品名称{{item.spuName}}
<router-link :to="`/shopDetails?spuId=${item.id}`">点击进入该商品详情</router-link>
<div v-for="spec in item.specsSpuAllInfo" :key="spec">规格组 {{spec.specsGroupName}}
<div v-for="name in spec.specsNames" :key="name">规格值 {{name}}
</div>
</div>
<hr>
</div>
</template>
<script setup lang="ts">
import {ref,reactive,toRefs} from 'vue'
import spuApi from "@/api/spuApi";
const data = reactive({
queryParams: {
pageNum: 1,
pageSize: 10,
// dictName: undefined,
// dictType: undefined,
isDeleted: false,
},
});
const spuList = ref<any[]>([]);
const { queryParams } = toRefs(data);
spuApi.pageList(queryParams.value).then((response:any)=>{
spuList.value=response.data.data;
})
</script>

7
Yi.App.Vue3/src/vite-env.d.ts vendored Normal file
View File

@@ -0,0 +1,7 @@
/// <reference types="vite/client" />
declare module '*.vue' {
import type { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
}