乐意购项目前端开发 #6

一、商品详情页面

 代码模版

创建Detail文件夹, 然后创建index.vue文件

<script setup>
import { getDetail } from "@/api/goods/index";
import { ref, onMounted } from "vue";
import { useRoute } from "vue-router";
import { useCartStore } from '@/store/cartStore';const cartStore = useCartStore()
const route = useRoute();
const goods = ref({});
const category = ref({});
const seller = ref({});
const imageList = [require(`@/assets/img/hot/hotgoods1.jpg`),require(`@/assets/img/hot/hotgoods2.jpg`),require(`@/assets/img/hot/hotgoods3.jpg`),require(`@/assets/img/hot/hotgoods4.jpg`),
];
// const imageList = []const getGoods = async () => {const res = await getDetail(route.params.id);goods.value = res.data.good;category.value = res.data.category;seller.value = res.data.seller;console.log(res.data.pictureList)imageList.value = res.data.pictureList
};
//count
const count = ref(1)
const countChange = (count) => {console.log(count);
}
//添加购物车
const addCart = () => {//console.log(goods)cartStore.addCart({id: goods.value.id,name: goods.value.goodsName,picture: goods.value.picture1,price: goods.value.price,count: count.value,// attrsText: skuObj.specsText,selected: true})}onMounted(() => {getGoods();
});
console.log(imageList);
// console.log(data);
</script><template><div class="lyg-goods-page"><div class="container"><div class="bread-container"><el-breadcrumb separator=">"><el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item><el-breadcrumb-item :to="{ path: `/category/sub/${category.id}` }">{{ category.categoryName }}</el-breadcrumb-item><el-breadcrumb-item :to="{ path: '/' }">{{ goods.goodsName }}</el-breadcrumb-item></el-breadcrumb></div><!-- 商品信息 --><div class="info-container"><div><div class="goods-info"><div class="media"><!-- 图片预览区 --><!--                 :src="require(`@/assets/img/${goods.picture1}.jpg`)" --><!-- <img class="goods-img" :alt="goods.alt" /> --><LygImageView :image-list="imageList"/></div><div class="spec"><!-- 商品信息区 --><p class="g-desc">{{ goods.goodsName }}</p><p class="g-name">{{ goods.goodsDetail }}</p><p class="g-price"><span>{{ goods.price }}</span><span> {{ goods.originalPrice }}</span></p><div class="g-service"><!-- <dl><dt>促销</dt><dd>12月好物放送,App领券购买直降120元</dd></dl> --><dl><dt>服务</dt><dd><span>无忧退货</span><span>快速退款</span><span>免费包邮</span><a href="javascript:;">了解详情</a></dd></dl></div><!-- 统计数量 --><ul class="goods-sales"><li><p>商品数量</p><p>{{ goods.goodsNumber }}</p><p><i class="iconfont icon-comment-filling"></i>查看</p></li><li><p>人气数值</p><p>{{ goods.heat }}</p><p><i class="iconfont icon-task-filling"></i>销量人气</p></li><li><p>卖家信誉</p><p>{{ seller.reputation }}</p><p><i class="iconfont icon-dynamic-filling"></i>卖家主页</p></li></ul><!-- 数据组件 --><el-input-number :min="1" v-model="count" @change="countChange" /><!-- 按钮组件 --><div><el-button size="large" class="btn" @click="addCart"> 加入购物车 </el-button></div><!--  --></div></div></div></div></div></div>
</template><style scoped lang='scss'>
.lyg-goods-page {border-bottom: solid 0.5px #666;.goods-info {min-height: 600px;background: #fff;display: flex;.media {width: 580px;height: 600px;padding: 30px 100px;}.spec {flex: 1;padding: 30px 160px 30px 0;}}.goods-footer {display: flex;margin-top: 20px;.goods-article {width: 940px;margin-right: 20px;}.goods-aside {width: 280px;min-height: 1000px;}}.goods-tabs {min-height: 600px;background: #fff;}.goods-warn {min-height: 600px;background: #fff;margin-top: 20px;}.number-box {display: flex;align-items: center;.label {width: 60px;color: #999;padding-left: 10px;}}.g-name {width: 520px;font-size: 22px;text-align: left;}.g-desc {color: #000000;font-size: 25px;margin-bottom: 10px;margin-top: 10px;}.g-price {margin-top: 10px;span {&::before {content: "¥";font-size: 14px;}&:first-child {color: $priceColor;margin-right: 10px;font-size: 22px;}&:last-child {color: #999;text-decoration: line-through;font-size: 16px;}}}.g-service {background: #f5f5f5;width: 500px;padding: 20px 10px 0 10px;margin-top: 10px;dl {padding-bottom: 20px;display: flex;align-items: center;dt {width: 50px;color: #999;}dd {color: #666;&:last-child {span {margin-right: 10px;&::before {content: "•";color: $lygColor;margin-right: 2px;}}a {color: $lygColor;}}}}}.goods-sales {display: flex;width: 400px;align-items: center;text-align: center;height: 140px;li {flex: 1;position: relative;~ li::after {position: absolute;top: 10px;left: 0;height: 60px;border-left: 1px solid #e4e4e4;content: "";}p {&:first-child {color: #999;}&:nth-child(2) {color: $priceColor;margin-top: 10px;}&:last-child {color: #666;margin-top: 10px;i {color: $lygColor;font-size: 14px;margin-right: 2px;}&:hover {color: $lygColor;cursor: pointer;}}}}}
}.goods-tabs {min-height: 600px;background: #fff;nav {height: 70px;line-height: 70px;display: flex;border-bottom: 1px solid #f5f5f5;a {padding: 0 40px;font-size: 18px;position: relative;> span {color: $priceColor;font-size: 16px;margin-left: 10px;}}}
}.goods-detail {padding: 40px;.attrs {display: flex;flex-wrap: wrap;margin-bottom: 30px;li {display: flex;margin-bottom: 10px;width: 50%;.dt {width: 100px;color: #999;}.dd {flex: 1;color: #666;}}}> img {width: 100%;}
}.btn {margin-top: 20px;
}.bread-container {padding: 25px 0;
}
</style>

封装接口

创建文件

import http from "@/utils/http"//获取商品信息
export function getDetail (id) {return http({url: '/goods',method: 'get',params: {id}})
}

配置路由

商品详情页面也是二级页面

{path: "/category/new",component: () => import("@/views/Category/New.vue"),},{path: "category/sub/:id",component: SubCategory,},{path: "/detail/:id",component: Detail,},
}

链接跳转

将之前页面商品的跳转链接修改

<RouterLink :to="`/detail/${item.id}`"></RouterLink>

二、详情页面图片显示组件

创建文件

index.js在 components 文件夹下, index.vue 在ImgView文件夹下

代码模版

index.vue 

<script setup>
import { ref, watch } from "vue";
import { useMouseInElement } from "@vueuse/core";//const imageList = [// require(`@/assets/img/hot/hotgoods1.jpg`),// require(`@/assets/img/hot/hotgoods2.jpg`),// require(`@/assets/img/hot/hotgoods3.jpg`),// require(`@/assets/img/hot/hotgoods4.jpg`),
//];
const image1List = [require(`@/assets/img/hot/hotgoods1.jpg`),require(`@/assets/img/hot/hotgoods2.jpg`),require(`@/assets/img/hot/hotgoods3.jpg`),require(`@/assets/img/hot/hotgoods4.jpg`),
];
// 图片列表
// const imageList = []
const props = defineProps({imageList: {type: Array,default: () => []}
})
// const props = defineProps({
//   imageList: Array,
// });
const imgList = props.imageList//记录激活下标
const activeIndex = ref(0);
//鼠标划过事件
const enterhandler = (i) => {activeIndex.value = i;
};console.log(imgList);
console.log(image1List);
</script>
<!---->
<template><div class="goods-image"><!-- 左侧大图--><div class="middle" ref="target"><img class="middle-img" :src="imgList[activeIndex]" alt="" /></div><!-- 小图列表 --><ul class="small"><liv-for="(img, i) in imgList":key="i"@mouseenter="enterhandler(i)":class="{ active: i === activeIndex }"><img :src="img" alt="" /></li></ul></div>
</template><style scoped lang="scss">
.goods-image {width: 480px;height: 400px;position: relative;display: flex;.middle {width: 400px;height: 400px;background: #f5f5f5;border: solid 1px #f6f6f6;.middle-img {width: 400px;height: 400px;}}.small {width: 80px;li {width: 68px;height: 68px;margin-left: 12px;margin-bottom: 15px;border: solid 1px #dad6d6;cursor: pointer;img {width: 68px;height: 68px;}&:hover,&.active {border: 2px solid $lygColor;}}}
}
</style>

 index.js

// 通过插件的方式把components中的所有组件都进行全局化注册
import ImageView from './ImageView/index.vue'
export const componentPlugin ={install(app){// app.component('组件名字',组件配置对象)app.component('LygImageView',ImageView)}
}

三、登录页面

代码模版

创建文件

<script setup>
import {useUserStore} from '@/store/user'
import { ref } from "vue";
import { useRouter } from 'vue-router';// 1.准备表单对象
const form = ref({username: "",password: "",agree: true,
});
// 2. 校验规则对象
const rules = {username: [{ required: true, message: "用户名不能为空", trigger: "blur" }],password: [{ required: true, message: "密码不能为空", trigger: "blur" },{ min: 6, max: 24, message: "密码长度要求6-14个字符", trigger: "blur" },],agree: [{validator: (rule, value, callBack) => {console.log(value);//自定义校验逻辑// 勾选协议通过,不勾选不通过if (value) {callBack();} else {callBack(new Error("请勾选协议"));}},},],
};
// 3.获取 form 实例做统一校验
const router = useRouter()
const formRef = ref(null)
const userStore =  useUserStore()
const doLogin = () => {const { username, password } = form.value// 调用实例方法formRef.value.validate(async (valid) => {// valid: 所有表单都通过校验  才为trueconsole.log(valid)console.log(username,password)// 以valid做为判断条件 如果通过校验才执行登录逻辑if (valid) {// TODO LOGINawait userStore.getUserInfo({ username, password })// 1. 提示用户ElMessage({ type: 'success', message: '登录成功' })// 2. 跳转首页router.replace({ path: '/' })}})
}
// TODO LOGIN</script><template><div class="wrap"><header class="login-header"><div class="container m-top-20"><h1 class="logo"><a href="/">乐易购</a></h1><RouterLink class="entry" to="/">进入网站首页<i class="iconfont icon-angle-right"></i><i class="iconfont icon-angle-right"></i></RouterLink></div></header><section class="login-section"><div class="wrapper"><nav><a href="javascript:;">账户登录</a></nav><div class="username-box"><div class="form"><el-form ref="formRef" label-position="right" :model="form":rules="rules"label-width="60px" status-icon><el-form-item prop="username" label="账户"><el-input v-model="form.username" /></el-form-item><el-form-item prop="password" label="密码"><el-input v-model="form.password" /></el-form-item><el-form-item prop="agree" label-width="22px"><el-checkbox size="large" v-model="form.agree">我已同意隐私条款和服务条款</el-checkbox></el-form-item><el-button size="large" class="subBtn" @click="doLogin">点击登录</el-button></el-form></div></div></div></section><footer class="login-footer"><div class="container"><p><a href="javascript:;">关于我们</a><a href="javascript:;">帮助中心</a><a href="javascript:;">售后服务</a><a href="javascript:;">配送与验收</a><a href="javascript:;">商务合作</a><a href="javascript:;">搜索推荐</a><a href="javascript:;">友情链接</a></p><p>CopyRight &copy; 乐易购</p></div></footer></div>
</template><style scoped lang='scss'>.login-header {background: #fff;border-bottom: 1px solid #e4e4e4;.container {display: flex;align-items: flex-end;justify-content: space-between;}.logo {width: 300px;height: 132px;text-align: right;line-height: 132px;text-shadow: 5px 5px 2px #251818;font-size: 45px;letter-spacing: 0.2em;font-family: Microsoft YaHei;a {height: 132px;width: 100%;text-indent: -9999px;color: $lygColor;}}.sub {flex: 1;font-size: 24px;font-weight: normal;margin-bottom: 38px;margin-left: 20px;color: #666;}.entry {color: #000;width: 120px;margin-bottom: 38px;font-size: 16px;i {font-size: 14px;color: $warnColor;letter-spacing: -5px;}}
}.login-section {background: url('@/assets/login.png')  no-repeat center / cover;height: 488px;position: relative;.wrapper {width: 380px;background: #fff;position: absolute;left: 50%;top: 54px;transform: translate3d(100px, 0, 0);box-shadow: 0 0 10px rgba(0, 0, 0, 0.15);nav {font-size: 14px;height: 55px;margin-bottom: 20px;border-bottom: 1px solid #f5f5f5;display: flex;padding: 0 40px;text-align: right;align-items: center;a {color: #000;flex: 1;line-height: 1;display: inline-block;font-size: 18px;position: relative;text-align: center;}}}
}.login-footer {padding: 30px 0 50px;background: #fff;p {text-align: center;color: #999;padding-top: 20px;a {line-height: 1;padding: 0 10px;color: #999;display: inline-block;~ a {border-left: 1px solid #ccc;}}}
}.username-box {.toggle {padding: 15px 40px;text-align: right;a {color: $lygColor;i {font-size: 14px;}}}.form {padding: 0 20px 20px 20px;&-item {margin-bottom: 28px;.input {position: relative;height: 36px;> i {width: 34px;height: 34px;background: #cfcdcd;color: #fff;position: absolute;left: 1px;top: 1px;text-align: center;line-height: 34px;font-size: 18px;}input {padding-left: 44px;border: 1px solid #cfcdcd;height: 36px;line-height: 36px;width: 100%;&.error {border-color: $priceColor;}&.active,&:focus {border-color: $lygColor;}}.code {position: absolute;right: 1px;top: 1px;text-align: center;line-height: 34px;font-size: 14px;background: #f5f5f5;color: #666;width: 90px;height: 34px;cursor: pointer;}}> .error {position: absolute;font-size: 12px;line-height: 28px;color: $priceColor;i {font-size: 14px;margin-right: 2px;}}}.agree {a {color: #069;}}.btn {display: block;width: 100%;height: 40px;color: #fff;text-align: center;line-height: 40px;background: $lygColor;&.disabled {background: #cfcdcd;}}}.action {padding: 20px 40px;display: flex;justify-content: space-between;align-items: center;.url {a {color: #999;margin-left: 10px;}}}
}.subBtn {background: $lygColor;width: 100%;color: #fff;
}
</style>

封装接口

创建文件

编写代码("username" , "password" 要和你数据库的属性对应上)

import http from '@/utils/http'export function loginAPI ({ username,password}) {return http({url: '/login',method: 'POST',data:{"username": username,"password": password},})
}

配置路由

登录页面是一级页面

const routes = [{// Home 页面是首页下的二级页面,所以要配置在首页路径下path: "/",component: Layout,children: [...//省略},{path: "/login",component: Login,},
];

用户数据持久化

要先安装pinia

安装pinia持久化插件 pinia-plugin-persistedstate

npm i pinia-plugin-persistedstate  

在main.js中注册插件

import { createPinia } from 'pinia'
import piniaPersist from 'pinia-plugin-persistedstate'const pinia = createPinia()
pinia.use(piniaPersist)
const app = createApp(App)app.use(pinia)

创建文件

// 管理用户数据相关
import { defineStore } from "pinia";
import { ref } from "vue";
import { loginAPI } from "@/api/login/index";
import { useCartStore } from "./cartStore";export const useUserStore = defineStore("user",() => {const cartStore = useCartStore();// 1. 定义管理用户数据的stateconst userInfo = ref({});// 2. 定义获取接口数据的action函数const getUserInfo = async ({ username, password }) => {const res = await loginAPI({ username, password });//console.log(res.data.code)// console.log(res.data.token)userInfo.value = res.data;//window.sessionStorage.setItem('token', res.data.token);//获取最新的购物车列表cartStore.updateNewList();};// 退出时清除用户信息const clearUserInfo = () => {userInfo.value = {};//window.sessionStorage.clear;//执行清除购物车的actioncartStore.clearCart;};// 3. 以对象的格式把state和action returnreturn {userInfo,getUserInfo,clearUserInfo,};},{persist: true,}
);

修改LayoutNav.vue

获取pinia中的用户数据

import { useUserStore } from '@/store/user'const userStore = useUserStore()

 根据是否登录状态来显示

<template v-if="userStore.userInfo.token"><li><a href="javascript:;" @click="$router.push('/my')"><i class=" iconfont icon-user"></i>{{ userStore.userInfo.user.username }}</a></li><li><el-popconfirm @confirm="confirm" title="确认退出吗?"  cancel-button-text="取消" confirm-button-text="确认"><template #reference><a href="javascript:;">退出登录</a></template></el-popconfirm></li><li><a href="javascript:;">我的订单</a></li></template><template v-else><li><a href="javascript:;" @click="router.push('/login')">请先登录</a></li><li><a href="javascript:;">帮助中心</a></li><li><a href="javascript:;">关于我们</a></li></template>

 整体代码

<script setup>
import { useUserStore } from '@/store/user'
import { useRouter } from 'vue-router'const userStore = useUserStore()
const router = useRouter()
const confirm = () => {console.log('用户要退出登录了')// 退出登录业务逻辑实现// 1.清除用户信息 触发actionuserStore.clearUserInfo()// 2.跳转到登录页router.push('/login')
}
console.log(userStore)
</script><template><nav class="app-topnav"><div class="container"><ul><template v-if="userStore.userInfo.token"><li><a href="javascript:;" @click="$router.push('/my')"><i class=" iconfont icon-user"></i>{{ userStore.userInfo.user.username }}</a></li><li><el-popconfirm @confirm="confirm" title="确认退出吗?"  cancel-button-text="取消" confirm-button-text="确认"><template #reference><a href="javascript:;">退出登录</a></template></el-popconfirm></li><li><a href="javascript:;">我的订单</a></li></template><template v-else><li><a href="javascript:;" @click="router.push('/login')">请先登录</a></li><li><a href="javascript:;">帮助中心</a></li><li><a href="javascript:;">关于我们</a></li></template></ul></div></nav>
</template><style scoped lang="scss">
.app-topnav {background: #333;ul {display: flex;height: 53px;justify-content: flex-end;align-items: center;li {a {padding: 0 15px;color: #cdcdcd;line-height: 1;display: inline-block;i {font-size: 14px;margin-right: 2px;}&:hover {color: $lygColor;}}~li {a {border-left: 2px solid #666;}}}}
}
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/451978.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

正点原子--STM32定时器学习笔记(2)

书接上文&#xff0c;本篇是对基本定时器实验部分进行总结&#xff01; 实验目标&#xff1a;通过TIM6基本定时器定时500ms&#xff0c;让LED0每隔500ms闪烁。 解决思路&#xff1a;使用定时器6&#xff0c;实现500ms产生一次定时器更新中断&#xff0c;在中断里执行“翻转LED0…

【FX110网】日交所发布1月交易数据:衍生品交易额达历年1月最高!

日本交易所集团&#xff08;日交所&#xff0c;JPX&#xff09;发布了其2024年1月的交易数据概览。数据显示&#xff0c;该交易所当月衍生品交易额创新历年来的1月交易数据最高纪录。2024年1月共有19个交易日。 2024年1月交易概览现货股票市场 2024年1月&#xff0c;该交易所主…

浅谈QT的几种线程的使用和区别。

简介&#xff1a; 线程是操作系统中的基本执行单元&#xff0c;是一个独立的执行路径。每个线程都有自己的栈空间&#xff0c;用于存储本地变量和函数调用的上下文。多个线程可以在同一进程中并发执行&#xff0c;从而实现并发处理&#xff0c;提高程序的性能和响应能力。 与进…

2020年通信工程师初级专业实务真题

文章目录 一、第1章 现代通信网概述&#xff1a;信令网、同步网、管理网。第10章 通信业务&#xff1a;通信产业链&#xff0c;通信终端的分类&#xff0c;通信业务的定义及分类二、第3章 接入网&#xff1a;无线接入网的优点&#xff0c;接入网的接口&#xff08;UNI&#xff…

13.从桥接模式细品人生的几座桥

“物理学不存在了&#xff0c;今后也不会存在。”——《三体》 在《三体》中&#xff0c;有这样一个桥段&#xff0c;顶级的物理学家杨冬在三体文明超级计算机“智子”的干扰和误导下&#xff0c;得出了物理实验的结果在实验之前就会被某种力量确定的结论&#xff0c;导致自己…

Linux进程信号处理:深入理解与应用(2​​)

&#x1f3ac;慕斯主页&#xff1a;修仙—别有洞天 ♈️今日夜电波&#xff1a;its 6pm but I miss u already.—bbbluelee 0:01━━━━━━️&#x1f49f;──────── 3:18 &#x1f504; ◀️…

MySQL数据库①_MySQL入门(概念+使用)

目录 1. 数据库的概念 1.1 数据库的存储介质 1.2 主流数据库 2. MySQL的基本使用 2.1 链接数据库 2.2 服务器管理 2.3 数据库&#xff0c;服务器和表关系 2.4 简单MySQL语句 3. MySQL架构 4. SQL分类 5. 存储引擎 本篇完。 1. 数据库的概念 数据库是按照数据结构来…

华为nova12系列:图片HDR显示,让你的照片全面升级!

你是不是也想给自己的照片加点料&#xff0c;让它们看起来更真实、捕捉到更多的细节和光影&#xff1f;不用愁&#xff0c;华为nova12系列就为你量身打造了图片HDR显示技术&#xff0c;让你的照片从此焕发绚丽光芒&#xff01; 回忆一下&#xff0c;在节日的夜晚想拍下绚丽的灯…

【Java】Redis入门

1. Redis入门 1.1 Redis简介 Redis是一个基于内存的key-value结构数据库。Redis 是互联网技术领域使用最为广泛的存储中间件。 **官网&#xff1a;**https://redis.io **中文网&#xff1a;**https://www.redis.net.cn/ key-value结构存储&#xff1a; 主要特点&#xff1a…

数据结构(C语言)代码实现(六)——单链表的实现

目录 参考、格式 头文件LinkList.h 一、将函数的小括号写成中括号 二、读取权限冲突 三、L->Last指针没有移动 四、函数指针的使用 头文件完整代码 测试函数&#xff08;主函数&#xff09;test.cpp 测试结果 参考、格式 数据结构课本2.3节&#xff08;严蔚敏版&a…

开发知识点-swoole高性能Php异步编程框架

swoole高性能Php异步编程框架 介绍主要特性应用场景使用Swoole的优势 介绍 Swoole 是一个高性能的 PHP 异步编程框架&#xff0c; 它允许PHP开发者编写高并发、实时、异步的网络服务器和应用。 Swoole 通过提供了一套在PHP中使用异步I/O、协程&#xff08;Coroutine&#xff…

「递归算法」:验证二叉搜索树

一、题目 给你一个二叉树的根节点 root &#xff0c;判断其是否是一个有效的二叉搜索树。 有效 二叉搜索树定义如下&#xff1a; 节点的左子树只包含 小于 当前节点的数。节点的右子树只包含 大于 当前节点的数。所有左子树和右子树自身必须也是二叉搜索树。 示例 1&#xff…