💩 产品功能代码
This commit is contained in:
parent
0e58df5ff5
commit
5ca5bcb18b
158
components/common/product/list.vue
Normal file
158
components/common/product/list.vue
Normal file
@ -0,0 +1,158 @@
|
||||
<template>
|
||||
<div class="wrapper">
|
||||
<el-row class="row-cards row-deck" :gutter="20">
|
||||
<el-col :span="8" v-for="product in products.products" :key="product.idProduct" style="margin-right: 20px;">
|
||||
<el-card :body-style="{ padding: '20px' }">
|
||||
<el-col :span="24">
|
||||
<el-image :src="product.productImgUrl"
|
||||
style="border-radius: 10px;background: #f5f7fa;border: #f5f7fa solid 1px;" fit="cover"></el-image>
|
||||
</el-col>
|
||||
<el-col style="padding-top: 20px;font-size: 16px;line-height: 22px;font-weight: 500;margin-bottom: 4px;">
|
||||
<span v-html="product.productTitle"></span>
|
||||
</el-col>
|
||||
<el-col style="padding-top: 30px;text-align: right;">
|
||||
<!-- <el-button type="text" class="button">立即购买</el-button>-->
|
||||
<el-button type="text" class="button" @click="handleClick(product.idProduct)">查看详情</el-button>
|
||||
</el-col>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "ProductList",
|
||||
props: {
|
||||
products: {
|
||||
type: Object
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleClick(idProduct) {
|
||||
this.$router.push({
|
||||
path: `/product/${idProduct}`
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
body {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.card {
|
||||
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
|
||||
position: relative;
|
||||
margin-bottom: 1.5rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.card {
|
||||
position: relative;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-ms-flex-direction: column;
|
||||
flex-direction: column;
|
||||
min-width: 0;
|
||||
word-wrap: break-word;
|
||||
background-color: #fff;
|
||||
background-clip: border-box;
|
||||
border: 1px solid rgba(0, 40, 100, 0.12);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.card-profile .card-header {
|
||||
height: 20rem;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.card-header:first-child {
|
||||
border-radius: calc(3px - 1px) calc(3px - 1px) 0 0;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
background: none;
|
||||
padding: 0.5rem 1.5rem;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
min-height: 3.5rem;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
padding: 1.5rem 1.5rem;
|
||||
margin-bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.03);
|
||||
border-bottom: 1px solid rgba(0, 40, 100, 0.12);
|
||||
}
|
||||
|
||||
.card-body {
|
||||
-ms-flex: 1;
|
||||
flex: 1;
|
||||
margin: 0;
|
||||
padding: 1.5rem 1.5rem;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.card-profile-img {
|
||||
max-width: 6rem;
|
||||
margin-top: -5rem;
|
||||
margin-bottom: 1rem;
|
||||
border: 3px solid #fff;
|
||||
border-radius: 100%;
|
||||
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.card-img-top {
|
||||
border-top-left-radius: 3px;
|
||||
border-top-right-radius: 3px;
|
||||
}
|
||||
|
||||
.card-img-top {
|
||||
width: 100%;
|
||||
border-top-left-radius: calc(3px - 1px);
|
||||
border-top-right-radius: calc(3px - 1px);
|
||||
}
|
||||
|
||||
.mb-3, .my-3 {
|
||||
margin-bottom: 0.75rem !important;
|
||||
}
|
||||
|
||||
h3, .h3 {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.mb-4, .my-4 {
|
||||
margin-bottom: 1rem !important;
|
||||
}
|
||||
|
||||
.article-header-md {
|
||||
position: relative;
|
||||
line-height: 1.4em;
|
||||
height: 1.4em;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.article-header-md a {
|
||||
font-weight: bold;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.article-summary-md {
|
||||
position: relative;
|
||||
line-height: 1.4em;
|
||||
height: 4.2em;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.el-col-6 {
|
||||
padding-right: 0.75rem;
|
||||
padding-left: 0.75rem;
|
||||
}
|
||||
|
||||
</style>
|
56
pages/product/_product_id.vue
Normal file
56
pages/product/_product_id.vue
Normal file
@ -0,0 +1,56 @@
|
||||
<template>
|
||||
<el-row class="article__wrapper">
|
||||
<el-col>
|
||||
<el-card>
|
||||
<div class="card-body d-flex flex-column article">
|
||||
<div class="article__item">
|
||||
<h1 class="list__title">
|
||||
{{ product.productTitle }}
|
||||
</h1>
|
||||
</div>
|
||||
<div class="pt-7 pipe-content__reset vditor-reset" id="articleContent" v-html="product.productContent"
|
||||
style="overflow: hidden;"></div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapState} from 'vuex';
|
||||
|
||||
export default {
|
||||
name: "ProductDetail",
|
||||
validate({params, store}) {
|
||||
return params.product_id && !isNaN(Number(params.product_id))
|
||||
},
|
||||
fetch({store, params, error}) {
|
||||
return Promise.all([
|
||||
store
|
||||
.dispatch('product/fetchDetail', params)
|
||||
.catch(err => error({statusCode: 404}))
|
||||
])
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
product: state => state.product.detail.data
|
||||
})
|
||||
},
|
||||
mounted() {
|
||||
this.$store.commit('setActiveMenu', 'product');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
@import "~vditor/src/assets/less/index.less";
|
||||
|
||||
.article__wrapper {
|
||||
max-width: 980px;
|
||||
margin: 20px auto;
|
||||
display: block;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
</style>
|
@ -1,35 +1,52 @@
|
||||
<template>
|
||||
<el-row class="product__wrapper">
|
||||
<el-col :span="8">
|
||||
<el-card :body-style="{ padding: '0px' }">
|
||||
<img src="https://static.rymcu.com/article/1648960741563.jpg"
|
||||
class="image">
|
||||
<div style="padding: 14px;">
|
||||
<span>Nebula Pi</span>
|
||||
<div class="bottom clearfix">
|
||||
<el-button type="text" class="button">立即购买</el-button>
|
||||
<el-button type="text" class="button" @click="handleClick">相关内容</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
<el-col>
|
||||
<product-list :products="products" @currentChange="currentChangeProduct"></product-list>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapState} from "vuex";
|
||||
import ProductList from "~/components/common/product/list";
|
||||
|
||||
export default {
|
||||
name: "products",
|
||||
components: {ProductList},
|
||||
fetch({store, query, error}) {
|
||||
return Promise.all([
|
||||
store
|
||||
.dispatch('product/fetchList', {page: query.page || 1})
|
||||
.catch(err => error({statusCode: 404}))
|
||||
])
|
||||
},
|
||||
watch: {
|
||||
'$route.query': function () {
|
||||
this.$store.dispatch('product/fetchList', {page: this.$route.query.page || 1})
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
products: state => state.product.list.data
|
||||
})
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
currentDate: new Date().toLocaleString()
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
handleClick() {
|
||||
currentChangeProduct(page) {
|
||||
this.$router.push({
|
||||
path: "/nebula-pi"
|
||||
});
|
||||
name: 'products',
|
||||
query: {
|
||||
page: page
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$store.commit('setActiveMenu', 'products');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
152
store/product.js
152
store/product.js
@ -0,0 +1,152 @@
|
||||
import Vue from 'vue';
|
||||
import { isBrowser } from '~/environment';
|
||||
|
||||
export const BASE_API_PATH = '/api/console'
|
||||
export const PRODUCT_API_PATH = '/api/product'
|
||||
|
||||
const getDefaultListData = () => {
|
||||
return {
|
||||
products: [],
|
||||
pagination: {}
|
||||
}
|
||||
}
|
||||
|
||||
export const state = () => {
|
||||
return {
|
||||
list: {
|
||||
fetching: false,
|
||||
data: getDefaultListData()
|
||||
},
|
||||
detail: {
|
||||
fetching: false,
|
||||
data: {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const mutations = {
|
||||
// 作品集列表
|
||||
updateListFetching(state, action) {
|
||||
state.list.fetching = action
|
||||
},
|
||||
updateListData(state, action) {
|
||||
state.list.data = action
|
||||
},
|
||||
updateExistingListData(state, action) {
|
||||
state.list.data.data.push(...action.data)
|
||||
state.list.data.pagination = action.pagination
|
||||
},
|
||||
|
||||
// 作品集详情
|
||||
updateDetailFetching(state, action) {
|
||||
state.detail.fetching = action
|
||||
},
|
||||
updateDetailData(state, action) {
|
||||
state.detail.data = action.product
|
||||
},
|
||||
// 更新作品集阅读全文状态
|
||||
updateDetailRenderedState(state, action) {
|
||||
Vue.set(
|
||||
state.detail.data,
|
||||
'isRenderedFullContent',
|
||||
action == null ? true : action
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export const actions = {
|
||||
// 获取作品集列表
|
||||
fetchList({commit, state}, params = {}) {
|
||||
|
||||
// 清空已有数据
|
||||
commit('updateListFetching', true)
|
||||
let currentData = JSON.parse(JSON.stringify(state)).list.data
|
||||
if (Number(params.page) === currentData.pagination.currentPage) {
|
||||
commit('updateListFetching', false)
|
||||
return
|
||||
}
|
||||
let data = {
|
||||
page: params.page,
|
||||
topicUri: params.topic_uri
|
||||
}
|
||||
commit('updateListData', getDefaultListData())
|
||||
|
||||
return this.$axios
|
||||
.$get(`${BASE_API_PATH}/products`, {
|
||||
params: data
|
||||
})
|
||||
.then(response => {
|
||||
commit('updateListFetching', false)
|
||||
commit('updateListData', response)
|
||||
if (isBrowser) {
|
||||
Vue.nextTick(() => {
|
||||
window.scrollTo(0,0);
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch(error => commit('updateListFetching', false))
|
||||
},
|
||||
|
||||
// 获取作品集详情
|
||||
fetchDetail({ commit }, params = {}) {
|
||||
// const delay = fetchDelay(
|
||||
// isBrowser
|
||||
// )
|
||||
// if (isBrowser) {
|
||||
// Vue.nextTick(() => {
|
||||
// window.scrollTo(0, 300);
|
||||
// })
|
||||
// }
|
||||
commit('updateDetailFetching', true)
|
||||
// commit('updateDetailData', {})
|
||||
return this.$axios
|
||||
.$get(`${BASE_API_PATH}/product/${params.product_id}`)
|
||||
.then(response => {
|
||||
return new Promise(resolve => {
|
||||
commit('updateDetailData', response)
|
||||
commit('updateDetailFetching', false)
|
||||
resolve(response)
|
||||
// delay(() => {
|
||||
// resolve(response)
|
||||
// })
|
||||
})
|
||||
})
|
||||
.catch(error => {
|
||||
commit('updateDetailFetching', false)
|
||||
return Promise.reject(error)
|
||||
})
|
||||
},
|
||||
fetchPostDetail({ commit }, params = {}) {
|
||||
// const delay = fetchDelay(
|
||||
// isBrowser
|
||||
// )
|
||||
// if (isBrowser) {
|
||||
// Vue.nextTick(() => {
|
||||
// window.scrollTo(0, 300);
|
||||
// })
|
||||
// }
|
||||
|
||||
if (typeof params.product_id === 'undefined') {
|
||||
commit('updateDetailData', getDefaultListData())
|
||||
return;
|
||||
}
|
||||
commit('updateDetailFetching', true)
|
||||
// commit('updateDetailData', {})
|
||||
return this.$axios
|
||||
.$get(`${PRODUCT_API_PATH}/detail/${params.product_id}`)
|
||||
.then(response => {
|
||||
return new Promise(resolve => {
|
||||
commit('updateDetailData', response)
|
||||
commit('updateDetailFetching', false)
|
||||
resolve(response)
|
||||
// delay(() => {
|
||||
// resolve(response)
|
||||
// })
|
||||
})
|
||||
})
|
||||
.catch(error => {
|
||||
commit('updateDetailFetching', false)
|
||||
return Promise.reject(error)
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user