黑马商城实战项目

项目搭建
  • 利用HBuilder X创建基本项目结构
  • 运行项目
  • 整理基本项目结构,并修改窗口外观
  1. "globalStyle": {
  2. "navigationBarTextStyle": "white",
  3. "navigationBarTitleText": "黑马商城",
  4. "navigationBarBackgroundColor": "#1989fa",
  5. "backgroundColor": "#F8F8F8"
  6. }
配置tabbar
  • 创建tabbar对应的四个页面和图标准备好
  • 将页面路径配置到pages.json中的pages数组中
  1. "pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
  2. {
  3. "path": "pages/index/index"
  4. },
  5. {
  6. "path": "pages/member/member"
  7. },
  8. {
  9. "path": "pages/cart/cart"
  10. },
  11. {
  12. "path": "pages/search/search"
  13. }
  14. ]
  • 配置tabbar
  1. {
  2. "tabBar": {
  3. "list": [
  4. {
  5. "pagePath":"pages/index/index",
  6. "text":"首页",
  7. "iconPath":"static/icon/home.png",
  8. "selectedIconPath":"static/icon/home-active.png"
  9. },
  10. {
  11. "pagePath":"pages/member/member",
  12. "text":"会员",
  13. "iconPath":"static/icon/member.png",
  14. "selectedIconPath":"static/icon/member-active.png"
  15. },
  16. {
  17. "pagePath":"pages/cart/cart",
  18. "text":"购物车",
  19. "iconPath":"static/icon/cart.png",
  20. "selectedIconPath":"static/icon/cart-active.png"
  21. },
  22. {
  23. "pagePath":"pages/search/search",
  24. "text":"搜索",
  25. "iconPath":"static/icon/search.png",
  26. "selectedIconPath":"static/icon/search-active.png"
  27. }
  28. ]
  29. }
  30. }
获取轮播图数据
  • 封装uni.request请求,并挂在到全局

  • 创建util》api.js

  1. // 封装get请求
  2. const baseUrl = "http://localhost:8082"
  3. export const myRequest = (options)=>{
  4. return new Promise((resolve,reject)=>{
  5. uni.request({
  6. method: options.method,
  7. data: options.data,
  8. url: baseUrl+options.url,
  9. success(res) {
  10. if(res.data.status !== 0) {
  11. return uni.showToast({
  12. title: '获取数据失败'
  13. })
  14. }
  15. resolve(res)
  16. },
  17. fail(err) {
  18. uni.showToast({
  19. title: '获取数据失败'
  20. })
  21. reject(err)
  22. }
  23. })
  24. })
  25. }
  • 在main.js中导入并挂载到全局
  1. import { myRequest } from './util/api.js'
  2. Vue.prototype.$myRequest = myReques
  • 获取轮播图的数据

  • 定义获取轮播图的方法

  1. methods: {
  2. async getSwipers () {
  3. const res = await this.$myRequest({
  4. method: 'GET',
  5. url: '/api/getlunbo'
  6. })
  7. this.swipers = res.data.message
  8. }
  9. }
  • 在onLoad中调用该方法
  1. this.getSwipers()
实现轮播图的结构和数据渲染
  • 定义轮播图的基本结构
  1. <swiper class="swiper" indicator-dots :autoplay="true" :interval="2000" circular>
  2. <swiper-item v-for="item in swipers" :key="item.id">
  3. <image :src="item.img"></image>
  4. </swiper-item>
  5. </swiper>
  • 样式,在工具中安装scss
  1. <style lang="scss">
  2. .home{
  3. swiper{
  4. height: 380rpx;
  5. image{
  6. width: 750rpx;
  7. height: 380rpx;
  8. }
  9. }
  10. }
  11. </style>
实现菜单导航结构
引入字体图标初始化样式
  1. <style>
  2. @font-face {font-family: "iconfont";
  3. src: url('~@/static/fonts/iconfont.eot?t=1576335469449'); /* IE9 */
  4. src: url('~@/static/fonts/iconfont.eot?t=1576335469449#iefix') format('embedded-opentype'), /* IE6-IE8 */
  5. url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAVMAAsAAAAAChwAAAT/AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCDKAqHQIYhATYCJAMUCwwABCAFhG0HUBurCFGUTk6f7EeBu6eIlFFajBY208XLlGHx/6/hiYfvx76d+2xFJLlUn75pp4tCoyQ8NgiJUJjuJfzNpSrpXIDoeDbs3GjyH7Lfv+zTy1mok+GyEhYAW8AKaPvz/6eeG1QvDUo6SQW8bE4KYTDSMRj1R/5x7/TP88Dmo7Jc/misYXOelwWYYEBjbGxrAQ7KEvQW8SwuJBO4nkCvRQ21q/PbB5hXQLdAvDU6gHmbRQnhhG598xlb1lEvsu70PvEBfFU/H/+hF1IkNYO+149nMRz+6v7/fwrv4JAqI6T5uXCbRMYWUIhnX8+tKT22ZUrvJ3PaCdCNryR+Sb2RfRPT+c9kbdF0s788siIRDWj6GXkWL/ySEsVkfsmyftoYKiIZsiQfJ+Y2ll7MgdgDiE/AMF+CumLWsCSpdFXpjsNkefoatW8WSY8xapRhreWm1a9vWvVEM6edaQdPapImTzTLLG4BwEBG/HJsK4bVEKkStJbxbUYy3othvO2ofJA7jBARcUZ4illDuEL29TB2C/3AGWj4WUfGYbG25LTICaEk1jWBR8KWC0bqQJ+EjJYmOnvisiHzqtifvZ08X7NYzBaJWAJBglDIRIi3Fd/C3cbZjqEKvkh9Qmx2SKCgMAhD15VHVkCQMCWOP72YnLB0euHUpROzJ/PEYiR6wxK8ShC+ZsryVibGlbIzorOjqRKnhwyOKTIlMRoPSLgMlYvInDIKdwsQ8K2YbO2PReRsyxzZvrK0dWxUYUrIE2sPj8+sHNw6tUVhj0AgslvelUfhDnNG4k0YwgedbyX/ovPRL8SnqwOt/atMII/RGGMacyXGJOaq+MtGYDFMOL1Hk8OSJdfqARNMZkPmnDVh5DP0bjDpx6XAB6RO0JkqM4F0fNSNZMKXB20QqI//n+swdzCvnv1/JszOzMEszPsskD8RZtrarNB14gGhubjWlduokVnWD9e9PyCQd1jtvPjStKTGs6VZFT/xjhhW0hpkG6PoC0wW8iGUrPK9prFY4AM+9GF8APhKfIvr5tct+IoAxU/eE3ILa4yrDxTgUfkrG0lPTtshU4pGtXT9UJqywWkDhzyi/5ka15D+WlPn1Gd+QYF7s0j3dXTuS0oStZLum6on86NOwIbPcydIP3+STiDFaYk1Z6WFLPt7b7M6EhMA+Gr+qtDilfia4mPFr0p8JZUS973QHulzYJxsPjJ6xdeW7Wd75WHHrZsGDXaUb1hacCuQVmrOkt1DjdgTxSeY3EcWDqCzRLxB9PmH2DNplYhzhCDbNLfzY7dKy1jb/dqNFfBTXbWg8vhWsH1JP9IN0VQmIcif4isHay1vtVDzNIiyxeirbpZ0JKEXg3QAAqN+8jFMLN9E6DZhQtJlAbJuS2ght6DqcwRNt1Potelicp8xbEqUHmz4IBCGfUIy6DtkwyZoIb+hmvQLzXAQoddNbM3ZZyWKFk+YAqEYwxGayJZKsMuiVnxH2ucBW+Uq94m4MkHstjrO5AWVxHkMqPq6J6JQsS3wnGxGeW7RsU0pklYi4rbbbeV3plZkC1h0iZGAIDEUGkFGxCopubpYdL3/DtG8XIBDSn4knxBWMbWjrpZODPRCU8YqWZfGlT6tRwjFOynMKqBz6iI5xizk/FulSES0JCkizrY2q6Tialrl64ppLD0VysKcNVLkKFGjae8S40w5K14OB+WS9lKjkR/YgsrZsRnZzAAA') format('woff2'),
  6. url('~@/static/fonts/iconfont.woff?t=1576335469449') format('woff'),
  7. url('~@/static/fonts/iconfont.ttf?t=1576335469449') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
  8. url('~@/static/fonts/iconfont.svg?t=1576335469449#iconfont') format('svg'); /* iOS 4.1- */
  9. }
  10. .iconfont {
  11. font-family: "iconfont" !important;
  12. font-size: 16px;
  13. font-style: normal;
  14. -webkit-font-smoothing: antialiased;
  15. -moz-osx-font-smoothing: grayscale;
  16. }
  17. .icon-shipin:before {
  18. content: "\f0024";
  19. }
  20. .icon-tupian:before {
  21. content: "\e650";
  22. }
  23. .icon-guanyuwomen:before {
  24. content: "\e608";
  25. }
  26. .icon-ziyuan:before {
  27. content: "\e60d";
  28. }
  29. </style>
完成菜单导航基本结构
  1. <view class="nav">
  2. <view class="item">
  3. <view class="iconfont icon-ziyuan"></view>
  4. <text>黑马超市</text>
  5. </view>
  6. <view class="item">
  7. <view class="iconfont icon-tupian"></view>
  8. <text>联系我们</text>
  9. </view>
  10. <view class="item">
  11. <view class="iconfont icon-guanyuwomen"></view>
  12. <text>社区图片</text>
  13. </view>
  14. <view class="item">
  15. <view class="iconfont icon-shipin"></view>
  16. <text>视频专区</text>
  17. </view>
  18. </view>
菜单导航样式
  1. .nav{
  2. display: flex;
  3. align-items: center;
  4. .item{
  5. width: 25%;
  6. text-align: center;
  7. view{
  8. background: $shop-color;
  9. line-height: 120rpx;
  10. width: 120rpx;
  11. height: 120rpx;
  12. border-radius: 90px;
  13. margin:10px auto;
  14. }
  15. text{
  16. font-size: 15px;
  17. }
  18. }
  19. .iconfont{
  20. font-size: 25px;
  21. color: #fff;
  22. height: 50px;
  23. }
  24. .icon-tupian{
  25. font-size: 20px;
  26. }
  27. }
实现推荐商品列表
定义基本结构
  1. <view class="hot_goods">
  2. <view class="tit">推荐商品</view>
  3. <!-- 一般用法 -->
  4. <view class="goods_list">
  5. <view class="goods_item">
  6. <image></image>
  7. <view class="price">
  8. <text>1299</text>
  9. <text>12990</text>
  10. </view>
  11. <view class="name">华为(HUAWEI)荣耀6Plus 16G双4G版</view>
  12. </view>
  13. </view>
  14. </view>
美化样式
  1. .hot_goods {
  2. background: #eee;
  3. .tit{
  4. border-top: 2px solid #eee;
  5. border-bottom: 2px solid #eee;
  6. margin-top: 20px;
  7. margin-bottom: 3px;
  8. color: $shop-color;
  9. height: 50px;
  10. line-height: 50px;
  11. text-align: center;
  12. letter-spacing: 20px;
  13. background: #fff;
  14. }
  15. .goods_list {
  16. display: flex;
  17. padding: 0 15rpx;
  18. justify-content: space-between;
  19. overflow: hidden;
  20. flex-wrap: wrap;
  21. .goods_item {
  22. width: 355rpx;
  23. margin-bottom: 15rpx;
  24. background: #fff;
  25. padding: 10px;
  26. box-sizing: border-box;
  27. image{
  28. height: 150px;
  29. width: auto;
  30. mix-width:160px;
  31. margin: 10px auto;
  32. }
  33. .price{
  34. font-size: 18px;
  35. color: red;
  36. padding: 8px 0;
  37. text:nth-child(2){
  38. color: #ccc;
  39. text-decoration: line-through;
  40. margin-left: 10px;
  41. font-size: 13px;
  42. }
  43. }
  44. .name {
  45. font-size: 14px;
  46. }
  47. }
  48. }
  49. }
获取数据
  • 定义获取数据的方法
  1. // 获取推荐商品
  2. async getGoods () {
  3. const res = await this.$myRequest({
  4. url: '/api/getgoods?pageindex=1'
  5. })
  6. this.goods = res.data.message
  7. }
  • 在onLoad生命周期中调用该方法
  1. this.getGoods()
渲染数据
  • 通过v-for渲染数据
  1. <view class="hot_goods">
  2. <view class="tit">推荐商品</view>
  3. <!-- 一般用法 -->
  4. <view class="goods_list">
  5. <view class="goods_item" v-for="item in goods" :key="item.id">
  6. <image :src="item.img_url"></image>
  7. <view class="price">
  8. <text>{{item.sell_price}}</text>
  9. <text>{{item.market_price}}</text>
  10. </view>
  11. <view class="name">{{item.title}}</view>
  12. </view>
  13. </view>
  14. </view>
完成黑马超市页面
改造导航菜单
  • 定义数据
  1. navs: [
  2. {
  3. icons: "iconfont icon-ziyuan",
  4. title: "黑马超市",
  5. path: "/pages/goods/list"
  6. },
  7. {
  8. icons: "iconfont icon-tupian",
  9. title: "社区图片",
  10. path: "/pages/pics/pics"
  11. },
  12. {
  13. icons: "iconfont icon-guanyuwomen",
  14. title: "联系我们",
  15. path: "/pages/contact/contact"
  16. },
  17. {
  18. icons: "iconfont icon-shipin",
  19. title: "学习视频",
  20. path: "/pages/videos/videos"
  21. }
  22. ]
  • 渲染数据
  1. <view class="nav">
  2. <view class="item" v-for="(item,index) in navs" :key="index">
  3. <view :class="item.icons"></view>
  4. <text>{{item.title}}</text>
  5. </view>
  6. </view>
  • 给导航菜单注册点击事件
  1. <view class="goods_item" v-for="item in goods" :key="item.id">
  • 定义跳转的方法
  1. goNavigator (url) {
  2. uni.navigateTo({
  3. url
  4. })
  5. }
创建黑马超市页面
  • 创建页面,goods>list.vue
  • 将页面路劲配置到pages文件中,修改标题

    封装商品列表组件
  • 在components下面创建goods>list.vue

  1. <template>
  2. <view class="goods_list">
  3. <view class="goods_item" v-for="item in goods" :key="item.id">
  4. <image :src="item.img_url"></image>
  5. <view class="price">
  6. <text>{{item.sell_price}}</text>
  7. <text>{{item.market_price}}</text>
  8. </view>
  9. <view class="name">{{item.title}}</view>
  10. </view>
  11. </view>
  12. </template>
  13. <script>
  14. export default {
  15. props:{
  16. goods:Array
  17. }
  18. }
  19. </script>
  20. <style lang="scss">
  21. .goods_list {
  22. display: flex;
  23. padding: 0 15rpx;
  24. justify-content: space-between;
  25. overflow: hidden;
  26. flex-wrap: wrap;
  27. .goods_item {
  28. width: 355rpx;
  29. margin-bottom: 15rpx;
  30. background: #fff;
  31. padding: 10px;
  32. box-sizing: border-box;
  33. image{
  34. height: 150px;
  35. width: 150px;
  36. display: block;
  37. margin: 10px auto;
  38. }
  39. .price{
  40. font-size: 18px;
  41. color: red;
  42. padding: 8px 0;
  43. text:nth-child(2){
  44. color: #ccc;
  45. text-decoration: line-through;
  46. margin-left: 10px;
  47. font-size: 13px;
  48. }
  49. }
  50. .name {
  51. font-size: 14px;
  52. }
  53. }
  54. }
  55. </style>
  • 在首页引入该组件
  1. import goodsList from "../../components/goods-list/index.vue"
  2. components: {
  3. "goods-list":goodsList
  4. }
  • 使用组件并将数据传递到组件内部
  1. <goods-list :goods="goods"></goods-list>
渲染商品列表
  • 定义获取商品列表数据的方法并调用
  1. <script>
  2. export default {
  3. data () {
  4. return {
  5. goods: []
  6. }
  7. },
  8. methods: {
  9. async getGoods () {
  10. const res = await this.$myRequest({
  11. url: '/api/getgoods?pageindex=1'
  12. })
  13. this.goods = res.data.message
  14. },
  15. },
  16. onLoad () {
  17. this.getGoods()
  18. }
  19. }
  20. </script>
  • 引入商品组件并使用
  1. <template>
  2. <view class="goods_list">
  3. <goods-list :goods="goods"></goods-list>
  4. </view>
  5. </template>
  6. <script>
  7. import goodsList from "../../components/goods-list/index.vue"
  8. export default {
  9. components: {
  10. "goods-list": goodsList
  11. }
  12. }
  13. </script>
实现上拉加载更多
  • 通过onReachBottom来监听触底
  1. onReachBottom () {
  2. this.pageindex++
  3. this.getGoods()
  4. }
  • 修改给goods赋值
  1. this.goods = [...this.goods,...res.data.message]
  • 动态显示底线

  • 通过onReachBottom监听是否还有更多

  1. if(this.pageindex*10>this.goods.length) return this.flag = true
  • 通过v-if控制底线
  1. <view class="over_line" v-if="flag">----------我是有底线的----------</view>
实现下拉刷新
  • 通过onPullDownRefresh进行下拉刷新的操作
  1. onPullDownRefresh() {
  2. this.goods = []
  3. this.pageindex = 1
  4. this.flag = false
  5. setTimeout(()=>{
  6. this.getGoods(()=>{
  7. uni.stopPullDownRefresh()
  8. })
  9. },1000)
  10. }
关于我们
实现社区图片
实现资讯列表
实现列表项的结构和样式

结构

  1. <view class="news_item">
  2. <image src="../../static/logo.png"></image>
  3. <view class="content">
  4. <view class="tit">1季度多家房企利润跌幅超50% 去库存促销战打响</view>
  5. <view class="info">
  6. <text>发表时间:2019-12-23</text>
  7. <text>浏览:1次</text>
  8. </view>
  9. </view>
  10. </view>

样式

  1. .news{
  2. .news_item{
  3. display: flex;
  4. padding: 5px 10px;
  5. border-bottom: 3px solid $shop-color;
  6. image{
  7. width: 300rpx;
  8. height: 150rpx;
  9. }
  10. .content {
  11. padding: 5px 10px;
  12. position: relative;
  13. .tit{
  14. font-size: 30rpx;
  15. }
  16. .info{
  17. font-size: 26rpx;
  18. position: absolute;
  19. left: 10px;
  20. bottom: 5px;
  21. text:nth-child(2){
  22. margin-left: 20px;
  23. }
  24. }
  25. }
  26. }
  27. }
封装为组件

创建news-item.vue

  1. <template>
  2. <view>
  3. <view class="news_item" v-for="item in data" :key="item.id">
  4. <image :src="item.img_url"></image>
  5. <view class="content">
  6. <view class="tit">{{item.title}}</view>
  7. <view class="info">
  8. <text>发表时间:{{item.add_time | formatDate}}</text>
  9. <text>浏览:{{item.click+123}}次</text>
  10. </view>
  11. </view>
  12. </view>
  13. </view>
  14. </template>
  15. <script>
  16. export default {
  17. props: ['data'],
  18. filters:{
  19. formatDate(data){
  20. const date = new Date(data)
  21. console.log(date)
  22. const day = date.getMonth().toString().padStart(2,'0')+'-'+date.getDay().toString().padStart(2,'0')
  23. return date.getFullYear()+'-'+day
  24. }
  25. }
  26. }
  27. </script>
  28. <style lang="scss">
  29. .news_item{
  30. display: flex;
  31. padding: 5px 10px;
  32. border-bottom: 1rpx solid $shop-color;
  33. image{
  34. max-width: 200rpx;
  35. min-width: 200rpx;
  36. height: 150rpx;
  37. }
  38. .content {
  39. padding: 5px 10px;
  40. position: relative;
  41. .tit{
  42. font-size: 30rpx;
  43. }
  44. .info{
  45. font-size: 26rpx;
  46. position: absolute;
  47. left: 10px;
  48. bottom: 5px;
  49. text:nth-child(2){
  50. margin-left: 20px;
  51. }
  52. }
  53. }
  54. }
  55. </style>
在新闻页面导入并使用
  1. <template>
  2. <view class="news">
  3. <new-item :data="newsList"></new-item>
  4. </view>
  5. </template>
  6. <script>
  7. import newItem from '../../components/new-item/new-item.vue'
  8. export default {
  9. data() {
  10. return {
  11. newsList: []
  12. }
  13. },
  14. methods: {
  15. async getNewsList () {
  16. const res = await this.$myRequest({
  17. url:'/api/getnewslist'
  18. })
  19. this.newsList = res.data.message
  20. }
  21. },
  22. onLoad () {
  23. this.getNewsList()
  24. },
  25. components: {
  26. "new-item":newItem
  27. }
  28. }
  29. </script>
  30. <style lang="scss">
  31. </style>
点击列表进入详情
  • 点击子组件列表项通过this.$emit触发父组件的方法
  1. navigatorTo (item) {
  2. this.$emit('clickItem',item)
  3. }
  • 父组件通过注册clickItem事件及事件函数实现跳转操作
  1. <template>
  2. <view class="news">
  3. <new-item :data="newsList" @clickItem="goDetail"></new-item>
  4. </view>
  5. </template>
  6. <script>
  7. export default {
  8. methods: {
  9. goDetail (data) {
  10. console.log(data)
  11. uni.navigateTo({
  12. url: '/pages/news-detail/news-detail'
  13. })
  14. }
  15. }
  16. }
  17. </script>
  • 新建/pages/news-detail/news-detail页面
实现资讯详情
实现基本结构
  1. <template>
  2. <view class="news_detail">
  3. <view class="news_title">
  4. 标题
  5. </view>
  6. <view class="info">
  7. <text>发表时间:</text>
  8. <text>浏览:</text>
  9. </view>
  10. <view class="content">
  11. </view>
  12. </view>
  13. </template>
  14. <script>
  15. export default {
  16. data() {
  17. return {
  18. }
  19. },
  20. methods: {
  21. }
  22. }
  23. </script>
  24. <style lang="scss">
  25. .news_detail {
  26. padding: 15rpx;
  27. .news_title{
  28. text-align: center;
  29. font-size: 32rpx;
  30. }
  31. .info{
  32. font-size: 28rpx;
  33. display: flex;
  34. justify-content: space-between;
  35. }
  36. }
  37. </style>
获取详情的数据
  1. methods: {
  2. async getNewsDetail(id){
  3. const res = await this.$myRequest({
  4. url: '/api/getnew/'+id
  5. })
  6. console.log(res)
  7. this.newsDetail = res.data.message[0]
  8. }
  9. },
  10. onLoad (options){
  11. this.getNewsDetail(options.id)
  12. }
实现详情的渲染
  1. <view class="news_title">
  2. {{newsDetail.title}}
  3. </view>
  4. <view class="info">
  5. <text>发表时间:{{newsDetail.add_time | formatDate}}</text>
  6. <text>浏览:{{newsDetail.click}}</text>
  7. </view>
  8. <view class="content">
  9. <rich-text :nodes="newsDetail.content"></rich-text>
  10. </view>
实现商品详情页
商品列表注册点击事件
<template>
    <view class="goods_list">
        <view @click="itemClick(item)" class="goods_item" v-for="item in goods" :key="item.id">
        </view>
    </view>
</template>
<script>
    export default {
        props:{
            goods:Array
        },
        methods: {
            itemClick(item) {
                this.$emit('itemClick',item)
            }
        }
    }
</script>
父组件绑定自定义事件进行跳转
<goods-list @itemClick="godetail" :goods="goods"></goods-list>
godetail (item) {
  uni.navigateTo({
      url: '/pages/goods-detail/goods-detail?id='+item.id
  })
}
创建商品详情页
获取详情轮播图数据
methods: {
  async getDetail(){
    const res = await this.$myRequest({
      url:'/api/getthumimages/'+this.id
    })
    console.log(res)
  }
},
onLoad(options){
    this.id = options.id
    this.getDetail()
}
渲染轮播图
<swiper indicator-dots>
  <swiper-item v-for="item in swipers" :key="item.src">
    <image :src="item.src"></image>
  </swiper-item>
</swiper>
<style lang="scss">
.goods_detail{
    swiper{
        height: 700rpx;
        image{
            width: 100%;
            height: 100%;
        }
    }
}
</style>
获取商品信息
methods: {
  async getDetailInfo () {
    const res = await this.$myRequest({
      url:'/api/goods/getinfo/'+this.id
    })
    this.info = res.data.message[0]
  }
},
onLoad(options){
    this.id = options.id
    this.getDetailInfo()
}
完成商品信息结构和渲染