feat(商品)

This commit is contained in:
Ankkaya 2024-06-12 18:33:39 +08:00
parent 1c71006dc5
commit d5e3d34e0f
13 changed files with 569 additions and 450 deletions

View File

@ -12,14 +12,35 @@
"path": "pages/index/redirect"
},
{
"path": "pages/index/order",
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "订单"
"navigationBarTitleText": "首页"
},
"meta": {
"auth": true
}
},
{
"path": "pages/index/order",
"style": {
"navigationBarTitleText": "订单",
"enablePullDownRefresh": true
},
"meta": {
"auth": true
}
},
{
"path": "pages/index/product",
"style": {
"navigationBarTitleText": "产品",
"enablePullDownRefresh": true
},
"meta": {
"auth": false
}
},
{
"path": "pages/index/my",
"style": {
@ -38,15 +59,6 @@
"auth": false
}
},
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "首页"
},
"meta": {
"auth": true
}
},
{
"path": "pages/index/login",
"style": {
@ -55,15 +67,6 @@
"meta": {
"auth": false
}
},
{
"path": "pages/index/product",
"style": {
"navigationBarTitleText": "产品"
},
"meta": {
"auth": false
}
}
],
"subPackages": [

View File

@ -2,7 +2,7 @@
<pb-layout navbar="inner" tabbar="/pages/index/index" :bgStyle="bgStyle">
<view class="dashboard-module ss-p-x-30">
<view class="merchant-info flex align-center">
<image class="logo" :src="merchantInfo.logo" mode="aspectFill"></image>
<image class="logo" :src="merchantInfo?.logo" mode="aspectFill"></image>
<view class="detail flex flex-column justify-center gap-10">
<view class="name ss-font-26">{{ merchantInfo.name }}</view>
@ -22,8 +22,8 @@
<button class="right-btn ss-reset-button">查看详情</button>
</view>
<view class="des ss-m-t-20">
总销售额{{ state.statistic.totalSalesAmount }} | 成功退款{{ state.statistic.refundCount }} |
退款金额{{ state.statistic.refundAmount }}
总销售额 {{ state.statistic.totalSalesAmount }} | 成功退款 {{ state.statistic.refundCount }} |
退款金额 {{ state.statistic.refundAmount }}
</view>
</view>

View File

@ -1,215 +1,215 @@
<template>
<pb-layout navbar="inner" tabbar="/pages/index/my" :bgStyle="bgStyle">
<view class="my-module ss-p-x-30">
<view class="user-info flex align-center">
<image class="avatar" :src="userInfo.avatar || '/static/default_avatar.png'" mode="aspectFill"></image>
<pb-layout navbar="inner" tabbar="/pages/index/my" :bgStyle="bgStyle">
<view class="my-module ss-p-x-30">
<view class="user-info flex align-center">
<image class="avatar" :src="userInfo.avatar || '/static/default_avatar.png'" mode="aspectFill"></image>
<view class="detail flex flex-column justify-center gap-10">
<view class="name ss-font-26">{{ userInfo.nickname }}</view>
<view class="description ss-font-26">{{ userInfo.mobile }}</view>
</view>
</view>
<view class="detail flex flex-column justify-center gap-10">
<view class="name ss-font-26">{{ userInfo.nickname }}</view>
<view class="description ss-font-26">{{ userInfo.mobile }}</view>
</view>
</view>
<view class="statistic ss-m-t-24">
<view class="title ss-font-24 ss-m-b-20"> 账户余额 </view>
<view class="remain flex justify-between align-center">
<view class="left flex align-center">
<view class="unit self-start"></view>
<view class="sremain ss-font-60">{{ remain }}</view>
</view>
<button @click="peach.$router.go('/pages/user/wallet/money')" class="right-btn ss-reset-button">
查看详情
</button>
</view>
<view class="cent ss-m-t-20"> 总获取积分 {{ userInfo.point }} </view>
</view>
<view class="statistic ss-m-t-24">
<view class="title ss-font-24 ss-m-b-20"> 账户余额 </view>
<view class="remain flex justify-between align-center">
<view class="left flex align-center">
<view class="unit self-start"></view>
<view class="sremain ss-font-60">{{ remain }}</view>
</view>
<button @click="peach.$router.go('/pages/user/wallet/money')" class="right-btn ss-reset-button">
查看详情
</button>
</view>
<view class="cent ss-m-t-20"> 总获取积分 {{ userInfo.point }} </view>
</view>
<view class="menu ss-m-t-70">
<view class="title ss-m-b-30">我的服务</view>
<view class="items">
<view class="item" v-for="item in state.menus" :key="item.name" @click="navService(item)">
<image class="icon" :src="item.icon" mode="aspectFill"></image>
<view class="label">{{ item.name }}</view>
</view>
<view class="menu ss-m-t-70">
<view class="title ss-m-b-30">我的服务</view>
<view class="items">
<view class="item" v-for="item in state.menus" :key="item.name" @click="navService(item)">
<image class="icon" :src="item.icon" mode="aspectFill"></image>
<view class="label">{{ item.name }}</view>
</view>
</view>
</view>
</view>
</view>
</view>
<view class="ss-p-x-30 bottom">
<button class="ss-reset-button login-btn-start" @tap="logOut">退出登录</button>
</view>
</pb-layout>
<view class="ss-p-x-30 bottom">
<button class="ss-reset-button login-btn-start" @tap="logOut">退出登录</button>
</view>
</pb-layout>
</template>
<script setup>
import { ref, computed } from 'vue'
import $store from '@/peach/store'
import AuthUtil from '@/peach/api/member/auth'
import peach from '@/peach'
const bgStyle = {
backgroundImage: '/static/bg-page.png',
imageType: 'local',
backgroundColor: '#fff',
backgroundImage: '/static/bg-page.png',
imageType: 'local',
backgroundColor: '#fff',
}
const state = ref({
menus: [
{
name: '余额提现',
icon: '/static/withdraw.png',
path: '/pages/user/wallet/withdraw',
},
{
name: '购买积分',
icon: '/static/buycent.png',
path: '/pages/user/point/buy',
},
{
name: '分发积分',
icon: '/static/dispensecent.png',
path: '/pages/user/point/share',
},
{
name: '提现规则',
icon: '/static/rule.png',
path: '',
},
],
menus: [
{
name: '余额提现',
icon: '/static/withdraw.png',
path: '/pages/user/wallet/withdraw',
},
{
name: '购买积分',
icon: '/static/buycent.png',
path: '/pages/user/point/buy',
},
{
name: '分发积分',
icon: '/static/dispensecent.png',
path: '/pages/user/point/share',
},
{
name: '提现规则',
icon: '/static/rule.png',
path: '',
},
],
})
const userStore = $store('user')
const userStore = peach.$store('user')
const remain = computed(() => {
return userStore.userWallet?.balance
return userStore.userWallet?.balance
})
const userInfo = computed(() => {
return userStore.userInfo
return userStore.userInfo
})
function navService(item) {
peach.$router.go(item.path)
peach.$router.go(item.path)
}
function logOut() {
uni.showModal({
title: '提示',
content: '确认退出账号?',
success: async function (res) {
if (!res.confirm) {
return
}
await AuthUtil.logout()
peach.$store('user').logout()
peach.$router.go('/pages/index/login')
},
})
uni.showModal({
title: '提示',
content: '确认退出账号?',
success: async function (res) {
if (!res.confirm) {
return
}
await AuthUtil.logout()
userStore.logOut()
peach.$router.go('/pages/index/login')
},
})
}
</script>
<style lang="scss" scoped>
.my-module {
.user-info {
.avatar {
width: 80rpx;
height: 80rpx;
margin-right: 20rpx;
}
.detail {
.name {
color: #fff;
}
.description {
color: #fff;
opacity: 0.4;
}
}
}
.statistic {
height: 200rpx;
background-color: #fffefe;
opacity: 0.9;
border-radius: 26rpx;
padding: 31rpx 40rpx;
.title {
font-weight: 500;
color: var(--ui-TC);
font-size: 24rpx;
}
.remain {
.left {
font-weight: 600;
flex: 1;
color: var(--ui-BG-Main);
.unit {
position: relative;
top: 4px;
}
}
.right-btn {
width: 161rpx;
height: 63rpx;
background-color: var(--ui-BG-Main);
color: #fff;
}
}
.cent {
color: var(--ui-TC-2);
font-size: 24rpx;
}
}
.menu {
.title {
font-weight: 600;
}
.items {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 30rpx 30rpx;
.item {
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
gap: 23rpx;
.icon {
width: 94rpx;
height: 94rpx;
.user-info {
.avatar {
width: 80rpx;
height: 80rpx;
margin-right: 20rpx;
}
.label {
font-size: 22rpx;
color: #0c0c0cff;
.detail {
.name {
color: #fff;
}
.description {
color: #fff;
opacity: 0.4;
}
}
}
.statistic {
height: 200rpx;
background-color: #fffefe;
opacity: 0.9;
border-radius: 26rpx;
padding: 31rpx 40rpx;
.title {
font-weight: 500;
color: var(--ui-TC);
font-size: 24rpx;
}
.remain {
.left {
font-weight: 600;
flex: 1;
color: var(--ui-BG-Main);
.unit {
position: relative;
top: 4px;
}
}
.right-btn {
width: 161rpx;
height: 63rpx;
background-color: var(--ui-BG-Main);
color: #fff;
}
}
.cent {
color: var(--ui-TC-2);
font-size: 24rpx;
}
}
.menu {
.title {
font-weight: 600;
}
.items {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 30rpx 30rpx;
.item {
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
gap: 23rpx;
.icon {
width: 94rpx;
height: 94rpx;
}
.label {
font-size: 22rpx;
color: #0c0c0cff;
}
}
}
}
}
}
}
.bottom {
position: absolute;
bottom: 70px;
width: calc(100% - 60rpx);
position: absolute;
bottom: 70px;
width: calc(100% - 60rpx);
.login-btn-start {
height: 82rpx;
line-height: normal;
background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
border-radius: 28rpx;
font-size: 26rpx;
font-weight: 500;
color: #fff;
}
.login-btn-start {
height: 82rpx;
line-height: normal;
background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
border-radius: 28rpx;
font-size: 26rpx;
font-weight: 500;
color: #fff;
}
}
</style>

View File

@ -124,7 +124,7 @@
<script setup>
import { ref } from 'vue'
import OrderApi from '@/peach/api/trade/order'
import { onLoad, onReachBottom, onPullDownRefresh } from '@dcloudio/uni-app'
import { onLoad, onShow, onReachBottom, onPullDownRefresh } from '@dcloudio/uni-app'
import { fen2yuan, formatOrderColor, formatOrderStatus, handleOrderButtons } from '@/peach/hooks/useGoods'
import peach from '@/peach'
import _, { isEmpty } from 'lodash'
@ -223,6 +223,10 @@ onLoad(async (options) => {
if (options.type) {
state.value.currentTab = options.type
}
})
onShow(async () => {
resetPagination(state.value.pagination)
await getOrderList()
})

View File

@ -15,6 +15,7 @@
:data="item"
:topRadius="10"
:bottomRadius="10"
@refresh="refresh"
@click="peach.$router.go('/pages/product/manageGoods', { id: item.id, mark: 'detail' })"
/>
</view>
@ -42,7 +43,7 @@
<script setup>
import { ref } from 'vue'
import { onLoad, onReachBottom } from '@dcloudio/uni-app'
import { onLoad, onShow, onReachBottom, onPullDownRefresh } from '@dcloudio/uni-app'
import GoodApi from '@/peach/api/trade/goods'
import peach from '@/peach'
import _ from 'lodash'
@ -93,6 +94,7 @@ function addGoods() {
selectedProperty: null,
goodsInfo: null,
skus: null,
specType: false,
})
peach.$router.go('/pages/product/manageGoods', {
title: '添加商品',
@ -107,13 +109,28 @@ function loadMore() {
getList()
}
onLoad(async (options) => {
function refresh() {
resetPagination(state.value.pagination)
getList()
}
onShow(async () => {
resetPagination(state.value.pagination)
await getList()
})
onReachBottom(() => {
loadMore()
})
//
onPullDownRefresh(() => {
resetPagination(state.value.pagination)
getList()
setTimeout(function () {
uni.stopPullDownRefresh()
}, 800)
})
</script>
<style lang="scss" scoped>
@ -124,6 +141,7 @@ onReachBottom(() => {
bottom: 70px;
right: 20px;
font-size: 80rpx;
z-index: 999;
}
.goods-list-box {

View File

@ -97,7 +97,7 @@ const props = defineProps({
},
})
const formData = ref({})
const specType = computed(() => peach.$store('trade').goodsInfo?.specType || false)
const specType = computed(() => peach.$store('trade').specType)
watch(
() => props.skus,

View File

@ -1,136 +1,122 @@
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, SKU_RULE_CONFIG } from "./config";
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, SKU_RULE_CONFIG } from './config'
const pickerRef = ref(null);
const pickerRef = ref(null)
// 多属性商品 sku 列表
const skus = ref([]);
const skus = ref([])
const propertyList = ref([]);
// 已选中商品规格的规格属性和值
const propertyList = ref([])
const goodsPropertyList = ref([]);
const goodsPropertyList = ref([])
const propertyListRef = ref(null);
const propertyListRef = ref(null)
const canEdit = computed(() => peach.$store("trade").canEdit);
const canEdit = computed(() => peach.$store('trade').canEdit)
const formData = ref({
specType: true,
specText: SPEC_TYPE[0].label,
});
specType: true,
specText: SPEC_TYPE[0].label,
})
async function showPropertyList() {
await getGoodsProperty();
propertyListRef.value.onOpen();
await getGoodsProperty()
propertyListRef.value.onOpen()
}
function onRDPickerConfirm(e) {
formData.value.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
formData.value.specText = SPEC_TYPE[e.value[0]].label
// 如果商品规格不一致,则需要重新初始化 sku 列表
initSku();
peach.$store("trade").specType = SPEC_TYPE[e.value[0]].value;
// 如果商品规格不一致,则需要重新初始化 sku 列表
initSku()
}
function pickerProperty() {
if (canEdit.value) {
let index = formData.value.specType ? 1 : 0;
if (canEdit.value) {
let index = formData.value.specType ? 1 : 0
console.log(index);
pickerRef.value.onOpen([index]);
}
pickerRef.value.onOpen([index])
}
}
async function onPropertyConfirm(e) {
await getGoodsProperty();
console.log(e);
await getGoodsProperty()
console.log(e)
}
function onConfirm() {
console.log(skus.value);
console.log(skus.value)
}
async function getGoodsProperty() {
let { data } = await GoodsApi.getHistoryProperty();
let { data } = await GoodsApi.getHistoryProperty()
// 把 propertyList 中 id 相同的属性合并,并去重
propertyList.value = peach.$store("trade").selectedProperty;
// 此处处理商品详情情况下,多规格属性
// 把 propertyList 中 id 相同的属性合并,并去重
propertyList.value = peach.$store('trade').selectedProperty || []
console.log(propertyList.value);
if (propertyList.value) {
// 根据已经选择数据,设置默认选中
data.forEach((item) => {
// 判断属性是否已经选中
let propertyParent = propertyList.value.find(
(sitem) => sitem?.id === item.id
);
// 判断属性是否已经选中
let propertyParent = propertyList.value.find((sitem) => sitem?.id === item.id)
item.checked = propertyParent ? true : false;
item.checked = propertyParent ? true : false
// 如果属性已经选中,查询子类中是否有选中
if (item.checked) {
item.propertyValues.forEach((child) => {
let childResult = propertyParent?.children.some(
(schild) => schild === child.id
);
child.checked = childResult ? true : false;
});
}
});
// 如果属性已经选中,查询子类中是否有选中
if (item.checked) {
item.propertyValues.forEach((child) => {
let childResult = propertyParent?.children.some((schild) => schild === child.id)
child.checked = childResult ? true : false
})
}
})
goodsPropertyList.value = data;
} else {
goodsPropertyList.value = [];
}
goodsPropertyList.value = data
console.log(goodsPropertyList.value);
console.log(goodsPropertyList.value)
}
function changeSubProperty() {
// 修改子属性状态,需要同步更新 skus 的显示
console.log(goodsPropertyList.value);
// 过滤父属性 checked 选项,深拷贝避免后面循环改变元数据内容
let temp = JSON.parse(
JSON.stringify(goodsPropertyList.value.filter((item) => item.checked))
);
temp.forEach((item) => {
item.propertyValues = item.propertyValues.filter((child) => child.checked);
});
// 修改子属性状态,需要同步更新 skus 的显示
console.log(goodsPropertyList.value)
// 过滤父属性 checked 选项,深拷贝避免后面循环改变元数据内容
let temp = JSON.parse(JSON.stringify(goodsPropertyList.value.filter((item) => item.checked)))
temp.forEach((item) => {
item.propertyValues = item.propertyValues.filter((child) => child.checked)
})
let result = temp.map((item) => {
return item.propertyValues.map((child) => ({
propertyId: item.id,
propertyName: item.name,
valueId: child.id,
valueName: child.name,
}));
});
let result = temp.map((item) => {
return item.propertyValues.map((child) => ({
propertyId: item.id,
propertyName: item.name,
valueId: child.id,
valueName: child.name,
}))
})
let tempSkus = [];
let tempSkus = []
for (let item of reduceArr(result)) {
let obj = {
picUrl: "",
barCode: "",
price: 0,
marketPrice: 0,
costPrice: 0,
stock: 0,
weight: 0,
volume: 0,
properties: item,
};
tempSkus.push(obj);
}
for (let item of reduceArr(result)) {
let obj = {
picUrl: '',
barCode: '',
price: 0,
marketPrice: 0,
costPrice: 0,
stock: 0,
weight: 0,
volume: 0,
properties: item,
}
tempSkus.push(obj)
}
skus.value = tempSkus;
skus.value = tempSkus
}
/**
@ -140,32 +126,35 @@ function changeSubProperty() {
* @returns {Type}
*/
function initSku() {
// 单规格
if (!formData.value.specType) {
let obj = {
picUrl: "",
barCode: "",
price: 0,
marketPrice: 0,
costPrice: 0,
stock: 0,
weight: null,
volume: null,
properties: [
{
propertyId: 0,
propertyName: "默认",
valueId: 0,
valueName: "默认",
},
],
};
// 单规格
if (!formData.value.specType) {
let obj = {
picUrl: '',
barCode: '',
price: 0,
marketPrice: 0,
costPrice: 0,
stock: 0,
weight: null,
volume: null,
properties: [
{
propertyId: 0,
propertyName: '默认',
valueId: 0,
valueName: '默认',
},
],
}
skus.value = [obj];
} else {
// 多规格
skus.value = [];
}
skus.value = [obj]
} else {
// 初始化已选中规格,属性和格式化后的结果
propertyList.value = []
goodsPropertyList.value = []
// 多规格
skus.value = []
}
}
/**
@ -175,126 +164,127 @@ function initSku() {
* @returns {Type}
*/
function submitProperty() {
try {
validateSku(skus.value);
peach.$store("trade").skus = skus.value;
peach.$router.back();
} catch (e) {
console.log(skus.value);
console.log(e, "校验失败");
}
try {
validateSku(skus.value)
peach.$store('trade').$patch({
skus: skus.value,
specType: formData.value.specType,
})
peach.$router.back()
} catch (e) {
console.log(skus.value)
console.log(e, '校验失败')
}
}
function validateSku(skus) {
let warningInfo = "请检查商品各行相关属性配置,";
let validateStatue = true;
let skusValue = skus ?? peach.$store("trade").skus;
let warningInfo = '请检查商品各行相关属性配置,'
let validateStatue = true
let skusValue = skus ?? peach.$store('trade').skus
// 判断如果是多规格,并且 skusValue 为空 [],则提示
if (formData.value.specType && skusValue.length < 1) {
uni.showToast({
title: "请选择商品规格",
icon: "none",
duration: 4000,
});
throw new Error("请选择商品规格");
}
for (const sku of skusValue) {
for (const rule of SKU_RULE_CONFIG) {
const arg = getValue(sku, rule.name);
if (!rule.rule(arg)) {
validateStatue = false;
warningInfo += rule.message;
break;
}
// 判断如果是多规格,并且 skusValue 为空 [],则提示
if (formData.value.specType && skusValue.length < 1) {
uni.showToast({
title: '请选择商品规格',
icon: 'none',
duration: 4000,
})
throw new Error('请选择商品规格')
}
if (!validateStatue) {
uni.showToast({
title: warningInfo,
icon: "none",
duration: 4000,
});
throw new Error(warningInfo);
for (const sku of skusValue) {
for (const rule of SKU_RULE_CONFIG) {
const arg = getValue(sku, rule.name)
if (!rule.rule(arg)) {
validateStatue = false
warningInfo += rule.message
break
}
}
if (!validateStatue) {
uni.showToast({
title: warningInfo,
icon: 'none',
duration: 4000,
})
throw new Error(warningInfo)
}
}
}
}
function getValue(obj, arg) {
const keys = arg.split(".");
let value = obj;
for (const key of keys) {
if (value && typeof value === "object" && key in value) {
value = value[key];
} else {
value = undefined;
break;
const keys = arg.split('.')
let value = obj
for (const key of keys) {
if (value && typeof value === 'object' && key in value) {
value = value[key]
} else {
value = undefined
break
}
}
}
return value;
return value
}
function reduceArr(arr) {
return arr.reduce((acc, cur) => {
let tempAcc = [];
return arr.reduce((acc, cur) => {
let tempAcc = []
if (acc.length < 1) {
cur.forEach((item, index) => {
if (tempAcc[index]) {
tempAcc[index].push(item);
if (acc.length < 1) {
cur.forEach((item, index) => {
if (tempAcc[index]) {
tempAcc[index].push(item)
} else {
tempAcc[index] = [item]
}
})
} else {
tempAcc[index] = [item];
}
});
} else {
acc.forEach((item, index) => {
cur.forEach((sitem, sindex) => {
tempAcc.push([...item, sitem]);
});
});
acc.forEach((item, index) => {
cur.forEach((sitem, sindex) => {
tempAcc.push([...item, sitem])
})
})
if (cur.length < 1) {
tempAcc = acc;
}
}
return tempAcc;
}, []);
if (cur.length < 1) {
tempAcc = acc
}
}
return tempAcc
}, [])
}
const specType = computed(
() => peach.$store("trade").goodsInfo?.specType || false
);
const specType = computed(() => peach.$store('trade').specType)
function initial() {
onLoad(() => {
formData.value.specType = specType.value ? true : false;
formData.value.specText = SPEC_TYPE[specType.value ? 1 : 0].label;
skus.value = JSON.parse(JSON.stringify(peach.$store("trade").skus));
// 如果新增商品 sku并且是单规格初始化 sku
onLoad(() => {
formData.value.specType = specType.value
formData.value.specText = SPEC_TYPE[specType.value ? 1 : 0].label
skus.value = JSON.parse(JSON.stringify(peach.$store('trade').skus))
// 如果新增商品 sku并且是单规格初始化 sku
if (!skus.value) {
initSku();
}
if (specType.value) {
getGoodsProperty();
}
});
if (!skus.value) {
initSku()
}
if (specType.value) {
getGoodsProperty()
}
})
}
export {
initial,
canEdit,
skus,
pickerRef,
pickerProperty,
validateSku,
onConfirm,
submitProperty,
propertyListRef,
formData,
onRDPickerConfirm,
onPropertyConfirm,
propertyList,
showPropertyList,
goodsPropertyList,
changeSubProperty,
};
initial,
canEdit,
skus,
pickerRef,
pickerProperty,
validateSku,
onConfirm,
submitProperty,
propertyListRef,
formData,
onRDPickerConfirm,
onPropertyConfirm,
propertyList,
showPropertyList,
goodsPropertyList,
changeSubProperty,
}

View File

@ -120,7 +120,6 @@
placeholderStyle="color:#8a8a8a"
:inputBorder="false"
v-model="formData.deliveryText"
:disabled="!canEdit"
placeholder="请选择配送方式"
disabled
>
@ -129,9 +128,18 @@
</template>
</uni-easyinput>
</uni-forms-item>
<uni-forms-item label="商品详情">
<uni-forms-item label="虚拟销量" name="virtualSalesCount" required>
<uni-easyinput
v-model="formData.virtualSalesCount"
type="number"
:disabled="!canEdit"
placeholder="请输入虚拟销量"
/>
</uni-forms-item>
<uni-forms-item label="商品详情" required>
<piaoyiEditor
:values="richValues"
@changes="saveContens"
:maxlength="3000"
:readOnly="richReadOnly"
:photoUrl="photoUrl"
@ -161,7 +169,7 @@ import GoodsApi from '@/peach/api/trade/goods'
import piaoyiEditor from '@/uni_modules/piaoyi-editor/components/piaoyi-editor/piaoyi-editor.vue'
import peach from '@/peach'
import { baseUrl, apiPath } from '@/peach/config'
import { handleTree } from '@/peach/utils'
import { handleTree, floatToFixed2, convertToInteger } from '@/peach/utils'
import { validateSku } from './js/sku'
const bgStyle = {
@ -198,12 +206,16 @@ const formData = ref({
'http://101.43.181.163:9001/mall-backend/8f11e372520501531d06bfce15ea97bbecead41c5e4a36d15d7e40af85729ff3.png',
],
name: '测试商品',
categoryId: [],
categoryId: 91,
categoryText: '',
brandId: '',
brandId: 4,
keyword: '香酥鸭,但家',
deliveryTypes: [],
deliveryTypes: [3],
deliveryText: '',
sort: 0,
giveIntegral: 0,
virtualSalesCount: 0,
subCommissionType: false,
introduction: '但家贵阳香酥鸭现榨香酥鸭无任何添加剂香酥鸭但家贵阳香酥鸭现榨香酥鸭无任何添加剂香酥鸭',
})
@ -362,7 +374,8 @@ function getProduct(id) {
GoodsApi.getProduct({ id }).then((res) => {
formData.value = res.data
richValues.value = res.data.description
//
richValues.value = res.data.description ? res.data.description + '<p><br/></p>' : ''
// categoryList formData.value.categoryId
let tempCategory = categoryList.value.find((item) => {
@ -390,18 +403,28 @@ function getProduct(id) {
return item.value === formData.value.deliveryTypes[0]
}).label
formData.value.skus.forEach((item) => {
item.price = floatToFixed2(item.price)
item.marketPrice = floatToFixed2(item.marketPrice)
item.costPrice = floatToFixed2(item.costPrice)
})
peach.$store('trade').$patch({
goodsInfo: formData.value,
skus: formData.value.skus,
specType: formData.value.specType,
})
})
}
function saveContens(e) {
formData.value.description = e.html
}
function onSubmit() {
console.log('res', formData.value)
console.log('richtext', richValues.value)
formData.value.description = richValues.value
formData.value.skus = peach.$store('trade').skus
formRef.value
@ -414,22 +437,42 @@ function onSubmit() {
tempObj.skus.forEach((item) => {
item.name = formData.value.name
item.price = convertToInteger(item.price)
item.marketPrice = convertToInteger(item.marketPrice)
item.costPrice = convertToInteger(item.costPrice)
})
tempObj.specType = peach.$store('trade').specType
let msg = ''
if (formData.value.id) {
tempObj.id = formData.value.id
await GoodsApi.editProduct(tempObj)
msg = '修改成功'
} else {
await GoodsApi.addProduct(tempObj)
msg = '添加成功'
}
})
.catch((err) => {
uni.showToast({
title: err[0].errorMessage,
title: msg,
icon: 'none',
duration: 4000,
})
setTimeout(() => {
peach.$router.redirect('/pages/index/product')
}, 1000)
})
.catch((err) => {
console.log('err', err)
if (err) {
uni.showToast({
title: err[0]?.errorMessage,
icon: 'none',
duration: 4000,
})
}
})
}
@ -452,7 +495,7 @@ onLoad(async (options) => {
goodsTitle.value = options.title
//
richReadOnly.value = true
// richReadOnly.value = true
/**
* todo 滚动一定距离后修改富文本状态和 canEdit 一致

View File

@ -33,6 +33,14 @@ const GoodsApi = {
data,
})
},
// 删除商品
delProduct: (data) => {
return request({
url: '/product/spu/delete',
method: 'DELETE',
params: data,
})
},
// 商品分类
getGoodsCategory: (data) => {
return request({

View File

@ -59,6 +59,7 @@
import { ref, computed } from 'vue'
import { isArray } from 'lodash'
import peach from '@/peach'
import GoodsApi from '@/peach/api/trade/goods'
import { fen2yuan, formatSales, formatStock } from '@/peach/hooks/useGoods'
import { unix } from 'dayjs'
@ -114,7 +115,7 @@ const props = defineProps({
},
})
const emits = defineEmits(['click'])
const emits = defineEmits(['click', 'refresh'])
function onClick() {
emits('click')
@ -155,6 +156,7 @@ function clickGoods(mark) {
selectedProperty: null,
goodsInfo: null,
skus: null,
specType: false,
})
peach.$router.go('/pages/product/manageGoods', {
id: props.data.id,
@ -165,8 +167,16 @@ function clickGoods(mark) {
uni.showModal({
title: '提示',
content: '是否删除该商品?',
success: (res) => {
success: async (res) => {
if (res.confirm) {
await GoodsApi.delProduct({ id: props.data.id })
uni.showToast({
title: '删除成功',
icon: 'none',
})
emits('refresh')
}
},
})

View File

@ -1,29 +1,33 @@
import { ref, computed } from "vue";
import { defineStore } from "pinia";
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'
const useTradeStore = defineStore("trade", () => {
// 已选择规格类型
const selectedProperty = ref(null);
const useTradeStore = defineStore('trade', () => {
// 已选择规格类型
const selectedProperty = ref(null)
// 商品信息
const goodsInfo = ref(null);
// 商品信息
const goodsInfo = ref(null)
// 详情标记
const detailTag = ref("edit");
// 详情标记
const detailTag = ref('edit')
// 商品属性
const skus = ref(null);
// 规格类型,默认单规格
const specType = ref(false)
// 商品是否可编辑
const canEdit = computed(() => (detailTag.value === "detail" ? false : true));
// 商品属性
const skus = ref(null)
return {
selectedProperty,
goodsInfo,
skus,
canEdit,
detailTag,
};
});
// 商品是否可编辑
const canEdit = computed(() => (detailTag.value === 'detail' ? false : true))
export default useTradeStore;
return {
selectedProperty,
goodsInfo,
skus,
canEdit,
detailTag,
specType,
}
})
export default useTradeStore

View File

@ -56,6 +56,45 @@ export const handleTree = (data, id, parentId, children) => {
return tree
}
export const convertToInteger = (num) => {
if (typeof num === 'undefined') return 0
const parsedNumber = typeof num === 'string' ? parseFloat(num) : num
// TODO 分转元后还有小数则四舍五入
return Math.round(parsedNumber * 100)
}
/**
* 将一个整数转换为分数保留两位小数
* @param num
*/
export const formatToFraction = (num) => {
if (typeof num === 'undefined') return '0.00'
const parsedNumber = typeof num === 'string' ? parseFloat(num) : num
return (parsedNumber / 100.0).toFixed(2)
}
export const floatToFixed2 = (num) => {
let str = '0.00'
if (typeof num === 'undefined') {
return str
}
const f = formatToFraction(num)
const decimalPart = f.toString().split('.')[1]
const len = decimalPart ? decimalPart.length : 0
switch (len) {
case 0:
str = f.toString() + '.00'
break
case 1:
str = f.toString() + '0'
break
case 2:
str = f.toString()
break
}
return str
}
export function resetPagination(pagination) {
pagination.list = []
pagination.total = 0

View File

@ -263,7 +263,7 @@ export default {
},
clear() {
this.editorCtx.clear()
this.$emit()
this.$emit('update:modelValue', '')
},
insertDate() {
const date = new Date()