feat(商品管理)

This commit is contained in:
Ankkaya 2024-06-05 18:58:12 +08:00
parent 30f3a4c2c4
commit 50e03907e7
11 changed files with 380 additions and 116 deletions

View File

@ -50,7 +50,7 @@
"quickapp" : {},
/* */
"mp-weixin" : {
"appid": "",
"appid" : "wx64387dc8bba916ec",
"setting" : {
"urlCheck" : false
},

View File

@ -15,7 +15,7 @@
:data="item"
:topRadius="10"
:bottomRadius="10"
@click="peach.$router.go('/pages/goods/index', { id: item.id })"
@click="peach.$router.go('/pages/product/manageGoods', { id: item.id, mark: 'detail' })"
/>
</view>
</view>

View File

@ -0,0 +1,78 @@
<template>
<view class="property-detail">
<template v-for="item in goodsPropertyList" :key="item.id">
<view class="property-item ss-p-40 ss-gap-40" v-if="item.checked">
<view class="property-name ss-flex ss-gap-40 ss-m-b-20">
<view class="property-name-text">规格名</view>
<view class="property-name-value">{{ item.name }}</view>
</view>
<view class="property-value ss-flex ss-gap-40">
<view class="property-value-text">规格值</view>
<view class="property-value-value ss-flex ss-gap-10">
<view
v-for="sitem in item.propertyValues"
@tap="chooseProperty(item)"
:class="['item', item.checked ? 'active' : '']"
>{{ sitem.name }}</view
>
</view>
</view>
</view>
</template>
</view>
</template>
<script setup>
import { defineProps, ref, computed } from 'vue'
const props = defineProps({
modelValue: {
type: Array,
default: () => [],
},
goodsPropertyList: {
type: Array,
default: () => [],
},
})
function chooseProperty(item) {
item.checked = !item.checked
}
</script>
<style lang="scss" scoped>
.property-detail {
.property-item {
margin: 0 40rpx 20rpx;
background-color: #f9f9f9;
border-radius: 10px;
.property-name {
.property-name-text {
color: #606266;
}
}
.property-value {
.property-value-text {
color: #606266;
}
.property-value-value {
.item {
text-align: center;
line-height: 60rpx;
height: 60rpx;
padding: 0 10px;
border-radius: 10px;
background-color: var(--ui-BG-4);
}
.active {
color: #fff;
background-color: var(--ui-BG-Main);
}
}
}
}
}
</style>

View File

@ -7,12 +7,12 @@
</view>
<view class="popup-content">
<view
v-for="item in propertyList"
v-for="item in goodsPropertyList"
:key="item.id"
:class="['property-item', item.checked ? 'active' : '']"
@tap="chooseProperty(item)"
>
{{ item.label }}
{{ item.name }}
</view>
</view>
</uni-popup>
@ -21,6 +21,7 @@
<script setup>
import { ref, computed, defineEmits, defineProps, defineExpose } from 'vue'
import peach from '@/peach'
/**
* todo 底部高度配置
@ -32,28 +33,15 @@ const props = defineProps({
required: true,
type: Array,
},
goodsPropertyList: {
default: () => [],
required: true,
type: Array,
},
})
const emit = defineEmits(['update:modelValue'])
const propertyList = ref([
{
label: '红色',
id: 1,
checked: false,
},
{
label: '蓝色',
id: 2,
checked: false,
},
{
label: '白色',
id: 3,
checked: false,
},
])
const propertyListPopupRef = ref()
const onClosePopup = () => {
@ -61,28 +49,30 @@ const onClosePopup = () => {
}
function chooseProperty(item) {
const index = props.modelValue.findIndex((v) => v.id === item.id)
item.checked = !item.checked
if (index === -1) {
props.modelValue.push(item)
} else {
props.modelValue.splice(index, 1)
}
}
function onConfirmPopup() {
emit('update:modelValue', propertyList.value.filter((item) => item.checked).map((item) => item.id) ?? [])
let resut = props.goodsPropertyList
.filter((item) => {
if (item.checked) {
return item.propertyValues.filter((sitem) => sitem.checked)
}
})
.map((item) => {
let children = item.propertyValues.filter((sitem) => sitem.checked).map((titem) => titem.id)
return {
id: item.id,
children: children,
}
})
console.log(resut)
// emit('update:modelValue', props.goodsPropertyList.filter((item) => item.checked).map((item) => item.id) ?? [])
onClosePopup()
}
function onOpen() {
propertyList.value.map((item) => {
item.checked = false
return item
})
propertyList.value = propertyList.value.map((item) =>
props.modelValue.some((id) => id === item.id) ? { ...item, checked: true } : item
)
propertyListPopupRef.value.open('bottom')
}

View File

@ -1,9 +1,23 @@
import { ref } from 'vue'
import { ref, computed } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import peach from '@/peach'
import GoodsApi from '@/peach/api/trade/goods'
import { SPEC_TYPE } from './config'
const pickerRef = ref(null)
const propertyList = ref([1, 2])
const propertyList = ref([
{
id: 36,
children: [68],
},
{
id: 37,
children: [],
},
])
const goodsPropertyList = ref([])
const propertyListRef = ref(null)
@ -12,13 +26,64 @@ const formData = ref({
specText: SPEC_TYPE[0].label,
})
async function showPropertyList() {
await getGoodsProperty()
propertyListRef.value.onOpen()
}
function onRDPickerConfirm(e) {
peach.$store('trade').specType = SPEC_TYPE[e.value[0]].value
formData.value.specText = SPEC_TYPE[e.value[0]].label
formData.value.specType = SPEC_TYPE[e.value[0]].value
}
function pickerProperty() {
let index = specType ? 1 : 0
pickerRef.value.onOpen([index])
}
function onPropertyConfirm(e) {
console.log(e)
}
export { pickerRef, propertyListRef, formData, onRDPickerConfirm, onPropertyConfirm, propertyList }
async function getGoodsProperty() {
let { data } = await GoodsApi.getHistoryProperty()
// 根据已经选择数据,设置默认选中
data.forEach((item) => {
// 判断属性是否已经选中
let propertyParent = propertyList.value.find((sitem) => sitem?.id === item.id)
item.checked = propertyParent ? true : false
// 如果属性已经选中,查询子类中是否有选中
if (item.checked) {
item.propertyValues.forEach((child) => {
let childResult = propertyParent?.children.some((schild) => schild?.id === child.id)
child.checked = childResult ? true : false
})
}
})
goodsPropertyList.value = data
}
const specType = computed(() => peach.$store('trade').specType)
function initial() {
onLoad(() => {
formData.value.specType = specType ? true : false
formData.value.specText = SPEC_TYPE[specType ? 1 : 0].label
})
}
export {
initial,
pickerRef,
pickerProperty,
propertyListRef,
formData,
onRDPickerConfirm,
onPropertyConfirm,
propertyList,
showPropertyList,
goodsPropertyList,
}

View File

@ -53,10 +53,16 @@
</template>
</uni-easyinput>
</uni-forms-item>
<uni-forms-item label="商品品牌" name="brandId" label-position="left" required>
<uni-forms-item
label="商品品牌"
name="brandId"
label-position="left"
required
@tap="openPicker('brand', 'single')"
>
<uni-easyinput
type="text"
v-model="formData.brandId"
v-model="formData.brandText"
:styles="selfStyles"
placeholderStyle="color:#8a8a8a"
:clearable="false"
@ -69,19 +75,10 @@
</template>
</uni-easyinput>
</uni-forms-item>
<uni-forms-item label="商品规格" name="skus" label-position="left" required>
<uni-easyinput
type="text"
v-model="formData.skus"
:styles="selfStyles"
placeholderStyle="color:#8a8a8a"
:clearable="false"
:inputBorder="false"
placeholder="请添加商品规格"
disabled
>
<template v-slot:right> <uni-icons type="right" /> </template
></uni-easyinput>
<uni-forms-item label="商品规格" name="skus" required label-position="left">
<view class="btn-group">
<button class="ss-reset-button ss-set-property" @tap="clickSetProperty">规格设置</button>
</view>
</uni-forms-item>
<uni-forms-item label="商品关键词" name="keyword" required>
<uni-easyinput type="text" v-model="formData.keyword" placeholder="请输入商品关键词" />
@ -255,6 +252,7 @@ const formRef = ref(null)
const pickerMode = ref('single')
const popMark = ref('')
const categoryList = ref([])
const brandList = ref([])
const optionsCols = ref([])
function openPicker(mark, mode) {
@ -266,6 +264,9 @@ function openPicker(mark, mode) {
} else if (mark === 'category') {
optionsCols.value = categoryList.value
pickerRef.value.onOpen([0, 0])
} else if (mark === 'brand') {
optionsCols.value = brandList.value
pickerRef.value.onOpen([0])
}
}
@ -277,15 +278,53 @@ function onRDPickerConfirm(e) {
}
if (popMark.value === 'category') {
formData.value.categoryId = e.value
formData.value.categoryId = categoryList.value[e.value[0]].children[e.value[1]].id
formData.value.categoryText =
categoryList.value[e.value[0]].name + '/' + categoryList.value[e.value[0]].children[e.value[1]].name
}
if (popMark.value === 'brand') {
formData.value.brandId = brandList.value[e.value[0]].id
}
}
function getProduct() {
GoodsApi.getProduct(formData.value.id).then((res) => {
formData.value = res
function clickSetProperty() {
peach.$store('trade').$patch({
selectedProperty: formData.value.skus,
goodsInfo: formData.value,
})
peach.$router.go('/pages/product/sku')
}
function getProduct(id) {
GoodsApi.getProduct({ id }).then((res) => {
formData.value = res.data
// categoryList formData.value.categoryId
let tempCategory = categoryList.value.find((item) => {
return item.children.find((child) => {
return child.id === formData.value.categoryId
})
})
formData.value.categoryText =
tempCategory.name +
'/' +
tempCategory.children.find((item) => {
return item.id === formData.value.categoryId
}).name
// brandList, formData.value.brandId
let tempBrand = brandList.value.find((item) => {
return item.id === formData.value.brandId
})
formData.value.brandText = tempBrand.name
// DELIVERY_TYPES formData.value.deliveryTypes
formData.value.deliveryText = DELIVERY_TYPES.find((item) => {
return item.value === formData.value.deliveryTypes[0]
}).label
})
}
@ -309,9 +348,6 @@ function onSubmit() {
})
}
//
function getGoodsInfo() {}
//
async function getCategoryList() {
let { data } = await GoodsApi.getGoodsCategory()
@ -320,14 +356,15 @@ async function getCategoryList() {
//
async function getBrandList() {
let { data } = await GoodsApi.getBrandList()
console.log(data)
let { data } = await GoodsApi.getBrand()
brandList.value = data
}
onLoad((options) => {
getCategoryList()
onLoad(async (options) => {
await getCategoryList()
await getBrandList()
if (options.id) {
getProduct()
getProduct(options.id)
}
})
</script>
@ -369,6 +406,25 @@ onLoad((options) => {
text-align: right;
}
}
.btn-group {
height: 100%;
display: flex;
justify-content: flex-end;
align-items: center;
background-color: #f9f9f9;
border-radius: 0 10px 10px 0;
.ss-set-property {
width: 80px;
height: 60rpx;
line-height: normal;
background: var(--ui-BG-Main);
border-radius: 28rpx;
font-size: 26rpx;
font-weight: 500;
color: #fff;
}
}
}
}
</style>

View File

@ -10,13 +10,7 @@
>
<view class="property">
<uni-forms ref="formRef" v-model="formData" :rules="rules" label-position="top" label-width="160">
<uni-forms-item
label="商品规格"
@tap="pickerRef.onOpen([0])"
name="specType"
label-position="left"
required
>
<uni-forms-item label="商品规格" @tap="pickerProperty" name="specType" label-position="left" required>
<uni-easyinput
type="text"
:clearable="false"
@ -35,7 +29,12 @@
</uni-forms>
</view>
<template v-if="formData.specType">
<button class="ss-reset-button add-property" @tap="propertyListRef.onOpen()">+添加规格</button>
<button class="ss-reset-button add-property" @tap="showPropertyList">+添加规格</button>
<property-detail
v-if="propertyList.length > 0"
v-model="propertyList"
:goodsPropertyList="goodsPropertyList"
></property-detail>
</template>
<template v-else>
@ -44,21 +43,40 @@
<p-picker ref="pickerRef" mode="single" :options-cols="SPEC_TYPE" @confirm="onRDPickerConfirm"></p-picker>
<PropertyList ref="propertyListRef" v-model="propertyList" @confirm="onPropertyConfirm" />
<PropertyList
ref="propertyListRef"
v-model="propertyList"
:goodsPropertyList="goodsPropertyList"
@confirm="onPropertyConfirm"
/>
</pb-layout>
</template>
<script setup>
import SkuItem from './components/item'
import PropertyList from './components/propertyList'
import PropertyDetail from './components/propertyDetail'
import { SPEC_TYPE } from './js/config'
import { pickerRef, propertyListRef, onRDPickerConfirm, formData, propertyList, onPropertyConfirm } from './js/sku'
import {
initial,
pickerRef,
propertyListRef,
onRDPickerConfirm,
formData,
propertyList,
onPropertyConfirm,
showPropertyList,
goodsPropertyList,
pickerProperty,
} from './js/sku'
const bgStyle = {
backgroundImage: '',
backgroundColor: '#fff',
description: '',
}
initial()
</script>
<style lang="scss" scoped>

View File

@ -12,7 +12,7 @@ const GoodsApi = {
// 商品详情 spuIds
getProduct: (data) => {
return request({
url: '/product/spu/list',
url: '/product/spu/get-detail',
method: 'GET',
params: data,
})
@ -42,6 +42,27 @@ const GoodsApi = {
})
},
// 商品品牌
getBrand: (data) => {
return request({
url: '/product/brand/list',
method: 'GET',
params: data,
})
},
// 历史属性
getHistoryProperty: () => {
return request({
url: '/product/property/history-list',
method: 'GET',
})
},
getPropertyList: (data) => {
return request({
url: '/product/property/get',
method: 'GET',
params: data,
})
},
}
export default GoodsApi

View File

@ -48,9 +48,9 @@
</view>
</view>
<view class="ss-flex ss-row-around" :style="btnStyles">
<button class="ss-reset-button btn-group">详情</button>
<button class="ss-reset-button btn-group">编辑</button>
<button class="ss-reset-button btn-group btn-del">删除</button>
<button class="ss-reset-button btn-group" @click="clickGoods('detail')">详情</button>
<button class="ss-reset-button btn-group" @click="clickGoods('edit')">编辑</button>
<button class="ss-reset-button btn-group btn-del" @click="clickGoods('del')">删除</button>
</view>
</view>
</template>
@ -60,6 +60,7 @@ import peach from '@/peach'
import { ref, computed } from 'vue'
import { isArray } from 'lodash'
import { fen2yuan, formatSales, formatStock } from '@/peach/hooks/useGoods'
import { unix } from 'dayjs'
const props = defineProps({
goodsFields: {
@ -147,6 +148,24 @@ const salesAndStock = computed(() => {
}
return text.join(' | ')
})
function clickGoods(mark) {
if (mark === 'detail' || mark === 'edit') {
peach.$router.go('/pages/product/manageGoods', {
id: props.data.id,
mark: mark,
})
} else if (mark === 'del') {
uni.showModal({
title: '提示',
content: '是否删除该商品?',
success: (res) => {
if (res.confirm) {
}
},
})
}
}
</script>
<style lang="scss" scoped>

View File

@ -106,7 +106,7 @@ http.interceptors.request.use(
*/
http.interceptors.response.use(
(response) => {
console.log('response', response)
// console.log('response', response)
// 约定:如果是 /auth/ 下的 URL 地址,并且返回了 accessToken 说明是登录相关的接口,则自动设置登陆令牌
if (response.config.url.indexOf('/member/auth/') >= 0 && response.data?.data?.accessToken) {
$store('user').setToken(response.data.data.accessToken, response.data.data.refreshToken)

17
peach/store/trade.js Normal file
View File

@ -0,0 +1,17 @@
import { ref } from 'vue'
import { defineStore } from 'pinia'
const useTradeStore = defineStore('trade', () => {
// 已选择规格类型
const selectedProperty = ref(null)
// 商品信息
const goodsInfo = ref(null)
return {
selectedProperty,
goodsInfo,
}
})
export default useTradeStore