feat: 更新 API 请求参数与组件样式优化

- 在 account.js 和 member.js 中添加 loading 和 skipMessage 参数,提升 API 调用的用户体验。
- 在 ShowGoods.vue 中优化商品详情展示样式,增强可读性和交互性。
- 在 Cart.vue 中重构购物车商品列表结构,提升布局一致性。
- 在多个页面中修复了提示信息的内容,确保用户操作的清晰反馈。
This commit is contained in:
田香琪
2026-07-06 16:33:21 +08:00
parent e13ca6281d
commit 297bdd95dd
28 changed files with 689 additions and 259 deletions

View File

@@ -55,7 +55,9 @@ export function editPwd (params) {
url: `/buyer/passport/member/modifyPass`, url: `/buyer/passport/member/modifyPass`,
method: Method.PUT, method: Method.PUT,
needToken: true, needToken: true,
data: params data: params,
loading: false,
skipMessage: true
}) })
} }

View File

@@ -427,7 +427,9 @@ export function distCash (params) {
url: `/buyer/distribution/cash`, url: `/buyer/distribution/cash`,
method: Method.POST, method: Method.POST,
needToken: true, needToken: true,
params params,
loading: false,
skipMessage: true
}); });
} }

View File

@@ -28,28 +28,36 @@
</div> </div>
<div class="goodsConfig mt_10"> <div class="goodsConfig mt_10">
<span @click="collect" <span
><el-icon :color="isCollected ? '#ed3f14' : '#666'"><StarFilled /></el-icon>{{ isCollected ? "已收藏" : "收藏" }}</span> class="collect-btn"
:class="{ 'is-collected': isCollected }"
@click="collect"
>
<el-icon class="collect-icon"><StarFilled /></el-icon>
<span class="collect-text">{{ isCollected ? '已收藏' : '收藏' }}</span>
</span>
</div> </div>
</div> </div>
<!-- 右侧商品信息活动信息操作展示 --> <!-- 右侧商品信息活动信息操作展示 -->
<div class="item-detail-right"> <div class="item-detail-right">
<div class="item-detail-title"> <div class="item-detail-title">
<p> <div class="item-detail-title-row">
{{ skuDetail.goodsName }} <span class="item-detail-name">{{ skuDetail.goodsName }}</span>
<el-tag <el-tag
v-if="skuDetail.goodsType !== 'VIRTUAL_GOODS'" v-if="skuDetail.goodsType !== 'VIRTUAL_GOODS'"
style="margin-top:10px;rgb(153, 149, 149);" class="goods-type-tag"
size="small"
> >
实物商品 实物商品
</el-tag> </el-tag>
<el-tag <el-tag
v-else-if="skuDetail.goodsType == 'VIRTUAL_GOODS'" v-else-if="skuDetail.goodsType == 'VIRTUAL_GOODS'"
style="margin-top:10px;rgb(153, 149, 149);" class="goods-type-tag"
size="small"
> >
虚拟商品 虚拟商品
</el-tag> </el-tag>
</p> </div>
</div> </div>
<div class="sell-point"> <div class="sell-point">
{{ skuDetail.sellingPoint }} {{ skuDetail.sellingPoint }}
@@ -224,13 +232,13 @@
<div class="item-select-row"> <div class="item-select-row">
<el-input-number <el-input-number
:min="1" :min="1"
:max="skuDetail.quantity" :max="quantityMax"
:disabled="skuDetail.quantity === 0" :disabled="isOutOfStock"
v-model="count" v-model="count"
:precision="0.1" :precision="0"
@blur="changeCount" @blur="changeCount"
></el-input-number> ></el-input-number>
<span class="inventory"> 库存{{ skuDetail.quantity }}</span> <span class="inventory">&nbsp&nbsp库存{{ skuDetail.quantity }}</span>
</div> </div>
</div> </div>
</div> </div>
@@ -325,9 +333,7 @@ export default {
handler(val) { handler(val) {
this.skuDetail = val.data; this.skuDetail = val.data;
this.wholesaleList = val.wholesaleList; this.wholesaleList = val.wholesaleList;
if (this.wholesaleList && this.wholesaleList.length > 0) { this.syncCountWithQuantity();
this.count = this.wholesaleList[0].num;
}
this.promotion(); this.promotion();
this.swiperGoodsImg(); this.swiperGoodsImg();
}, },
@@ -388,6 +394,13 @@ export default {
}) })
: []; : [];
}, },
quantityMax() {
const qty = Number(this.skuDetail.quantity) || 0;
return qty > 0 ? qty : 1;
},
isOutOfStock() {
return (Number(this.skuDetail.quantity) || 0) <= 0;
},
}, },
methods: { methods: {
// 初始化video // 初始化video
@@ -408,6 +421,22 @@ export default {
}, },
syncCountWithQuantity() {
const qty = Number(this.skuDetail.quantity) || 0;
if (qty <= 0) {
this.count = 1;
return;
}
if (this.wholesaleList && this.wholesaleList.length > 0) {
this.count = Math.min(Math.max(this.wholesaleList[0].num, 1), qty);
return;
}
if (this.count > qty) {
this.count = qty;
} else if (this.count < 1) {
this.count = 1;
}
},
changeCount(val) { changeCount(val) {
if (this.wholesaleList && this.wholesaleList.length > 0) { if (this.wholesaleList && this.wholesaleList.length > 0) {
if (this.count <= this.wholesaleList[0].num) { if (this.count <= this.wholesaleList[0].num) {
@@ -688,11 +717,23 @@ export default {
flex-direction: column; flex-direction: column;
} }
.item-detail-title p { .item-detail-title-row {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 8px;
padding: 8px 0;
}
.item-detail-name {
@include content_color($light_content_color); @include content_color($light_content_color);
font-weight: bold; font-weight: bold;
font-size: 20px; font-size: 20px;
padding: 8px 0; line-height: 1.4;
}
.goods-type-tag {
flex-shrink: 0;
} }
.item-detail-express { .item-detail-express {
@@ -941,13 +982,32 @@ export default {
.goodsConfig { .goodsConfig {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
> span {
.collect-btn {
display: inline-flex;
align-items: center;
gap: 4px;
padding-right: 10px; padding-right: 10px;
&:hover { color: #666;
cursor: pointer; cursor: pointer;
color: $theme_color; line-height: 1;
&.is-collected {
.collect-icon {
color: #ffc107;
}
} }
} }
.collect-icon {
display: inline-flex;
align-items: center;
font-size: 16px;
}
.collect-text {
line-height: 1;
}
} }
.sell-point { .sell-point {
font-size: 12px; font-size: 12px;

View File

@@ -338,7 +338,7 @@ export default {
font-weight: bolder; font-weight: bolder;
} }
.item-intro-detail{ .item-intro-detail{
margin: 0 30px; margin: 0;
width: 100%; width: 100%;
} }
.item-intro-nav{ .item-intro-nav{
@@ -365,6 +365,8 @@ export default {
} }
.item-intro-img { .item-intro-img {
width: 100%; width: 100%;
box-sizing: border-box;
padding: 0 30px;
min-height: 300px; min-height: 300px;
:deep img{ :deep img{
margin:0 auto; margin:0 auto;
@@ -377,6 +379,8 @@ export default {
.item-param-container { .item-param-container {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
box-sizing: border-box;
padding: 0 30px;
} }
.item-param-box { .item-param-box {
padding: 5px; padding: 5px;
@@ -400,6 +404,10 @@ export default {
color: #666666; color: #666666;
background-color: #F7F7F7; background-color: #F7F7F7;
} }
.remarks-container {
box-sizing: border-box;
padding: 0 30px;
}
.remarks-analyse-box { .remarks-analyse-box {
padding: 15px; padding: 15px;
display: flex; display: flex;
@@ -507,31 +515,36 @@ export default {
} }
/***************商品详情介绍和推荐侧边栏结束***************/ /***************商品详情介绍和推荐侧边栏结束***************/
/* 改变标签页样式 */ /* 改变标签页样式 */
:deep(.el-tabs__active-bar) { .item-tabs {
background-color: $theme_color !important; :deep(.el-tabs__header) {
} border: none;
:deep(.el-tabs__header) { margin: 0;
border: none; background-color: #f7f7f7;
} }
.item-tabs > .el-tabs > .el-tabs__header .el-tabs__item {
border-radius: 0px; :deep(.el-tabs__active-bar) {
color: #999; background-color: $theme_color !important;
height: 38px; }
}
.item-tabs > .el-tabs > .el-tabs__header .el-tabs__item.is-active { :deep(.el-tabs__item) {
color: #fff; border-radius: 0;
background-color: $theme_color; color: #999;
} height: 38px;
.item-tabs > .el-tabs > .el-tabs__header .el-tabs__item.is-active:before { padding: 0 20px;
content: '';
display: block; &:hover {
width: 100%; color: #999;
height: 1px; }
color: #fff; }
background: #F7F7F7;
position: absolute; :deep(.el-tabs__nav) {
top: 0; padding-left: 10px;
left: 0; }
:deep(.el-tabs__item.is-active) {
color: $theme_color !important;
background-color: transparent;
}
} }
:deep(.el-rate__icon) { :deep(.el-rate__icon) {
color: $theme_color; color: $theme_color;

View File

@@ -25,8 +25,9 @@
</div> </div>
<!-- 购物车商品列表 --> <!-- 购物车商品列表 -->
<div class="cart-goods"> <div class="cart-goods">
<div class="cart-goods-table">
<div class="cart-goods-title"> <div class="cart-goods-title">
<div class="width_60"> <div class="width_60 cart-col-check">
<el-checkbox <el-checkbox
v-model="allChecked" v-model="allChecked"
@change="changeChecked(allChecked, 'all')" @change="changeChecked(allChecked, 'all')"
@@ -43,56 +44,58 @@
<p>购物车空空如也</p> <p>购物车空空如也</p>
<router-link to="/">去选购&gt;</router-link> <router-link to="/">去选购&gt;</router-link>
</div> </div>
<div <div v-else class="cart-goods-list-box">
v-else <div
class="cart-goods-items" class="cart-shop-group"
v-for="(shop, index) in cartList" v-for="(shop, index) in cartList"
:key="index" :key="index"
> >
<div class="shop-name"> <div class="shop-name">
<div> <div class="width_60 cart-col-check">
<el-checkbox <el-checkbox
v-model="shop.checked" v-model="shop.checked"
@change="changeChecked(shop.checked, 'shop', shop.storeId)" @change="changeChecked(shop.checked, 'shop', shop.storeId)"
></el-checkbox> ></el-checkbox>
<span class="go-shop-page" @click="goShopPage(shop.storeId)">{{
shop.storeName
}}</span>
</div>
<span
class="shop-coupon"
v-if="shop.couponList.length"
:class="couponAvailable === index ? 'shop-coupon-show' : ''"
@click.stop="showCoupon(shop.id, index)"
>
<!-- 优惠券模态框 -->
<div v-if="couponAvailable === index">
<div
class="coupon-item"
v-for="(item, index) in shop.couponList"
:key="index"
>
<span v-if="item.couponType === 'PRICE'"
>{{ item.price }}</span>
<span v-if="item.couponType === 'DISCOUNT'"
>{{ item.couponDiscount }}</span>
<span>{{ item.consumeThreshold }}元可用</span>
<el-button
class="coupon-btn"
size="small"
type="primary"
@click="receiveShopCoupon(item)"
:disabled="item.disabled"
>{{ item.disabled ? "已领取" : "领取" }}</el-button
>
</div>
</div> </div>
</span> <div class="shop-info">
<div class="promotion-notice">{{ shop.promotionNotice }}</div> <span class="go-shop-page" @click="goShopPage(shop.storeId)">{{
</div> shop.storeName
<template v-for="(goods, goodsIndex) in shop.skuList" :key="goods.goodsSku?.id || goodsIndex"> }}</span>
<div class="goods-item"> <span
<div class="width_60"> class="shop-coupon"
v-if="shop.couponList.length"
:class="couponAvailable === index ? 'shop-coupon-show' : ''"
@click.stop="showCoupon(shop.id, index)"
>
<!-- 优惠券模态框 -->
<div v-if="couponAvailable === index">
<div
class="coupon-item"
v-for="(item, couponIndex) in shop.couponList"
:key="couponIndex"
>
<span v-if="item.couponType === 'PRICE'"
>{{ item.price }}</span>
<span v-if="item.couponType === 'DISCOUNT'"
>{{ item.couponDiscount }}</span>
<span>{{ item.consumeThreshold }}元可用</span>
<el-button
class="coupon-btn"
size="small"
type="primary"
@click="receiveShopCoupon(item)"
:disabled="item.disabled"
>{{ item.disabled ? "已领取" : "领取" }}</el-button
>
</div>
</div>
</span>
<div class="promotion-notice">{{ shop.promotionNotice }}</div>
</div>
</div>
<template v-for="(goods, goodsIndex) in shop.skuList" :key="goods.goodsSku?.id || goodsIndex">
<div class="goods-item">
<div class="width_60 cart-col-check">
<el-checkbox <el-checkbox
v-model="goods.checked" v-model="goods.checked"
@change=" @change="
@@ -155,21 +158,21 @@
</template> </template>
</div> </div>
</div> </div>
<div class="width_150"> <div class="width_150 cart-col-price">
{{ $filters.unitPrice(goods.purchasePrice, "¥") }} {{ $filters.unitPrice(goods.purchasePrice, "¥") }}
</div> </div>
<div class="width_100"> <div class="width_100 cart-col-qty">
<el-input-number <el-input-number
:min="1" :min="1"
size="small" size="small"
v-model="goods.num" v-model="goods.num"
@change="changeNum(goods.num, goods.goodsSku.id)" @change="changeNum(goods.num, goods.goodsSku.id)"
></el-input-number> ></el-input-number>
<div class="fontsize_12"> <div class="fontsize_12 cart-qty-stock">
{{ goods.goodsSku.quantity > 0 ? "有货" : "无货" }} {{ goods.goodsSku.quantity > 0 ? "有货" : "无货" }}
</div> </div>
</div> </div>
<div class="width_150"> <div class="width_150 cart-col-subtotal">
{{ $filters.unitPrice(goods.subTotal, "¥") }} {{ $filters.unitPrice(goods.subTotal, "¥") }}
</div> </div>
<div class="cart-ops-col"> <div class="cart-ops-col">
@@ -195,12 +198,14 @@
> >
</div> </div>
</div> </div>
</template> </template>
</div>
</div>
</div> </div>
<!-- 底部支付栏 --> <!-- 底部支付栏 -->
<div class="cart-goods-footer"> <div class="cart-goods-footer">
<div> <div>
<div class="width_60"> <div class="width_60 cart-col-check">
<el-checkbox <el-checkbox
v-model="allChecked" v-model="allChecked"
@change="changeChecked(allChecked, 'all')" @change="changeChecked(allChecked, 'all')"
@@ -286,7 +291,7 @@ export default {
collectGoods(id) { collectGoods(id) {
this.$Modal.confirm({ this.$Modal.confirm({
title: "收藏", title: "收藏",
content: "<p>商品收藏后可在个人中心我的收藏查看</p>", content: "商品收藏后可在个人中心我的收藏查看",
onOk: () => { onOk: () => {
APIMember.collectGoods("GOODS", id).then((res) => { APIMember.collectGoods("GOODS", id).then((res) => {
if (res.success) { if (res.success) {
@@ -315,7 +320,7 @@ export default {
} }
this.$Modal.confirm({ this.$Modal.confirm({
title: "删除", title: "删除",
content: "<p>确定要删除该商品吗?</p>", content: "确定要删除该商品吗?",
onOk: () => { onOk: () => {
APICart.delCartGoods({ skuIds: idArr.toString() }).then((res) => { APICart.delCartGoods({ skuIds: idArr.toString() }).then((res) => {
if (res.success) { if (res.success) {
@@ -332,7 +337,7 @@ export default {
clearCart() { clearCart() {
this.$Modal.confirm({ this.$Modal.confirm({
title: "提示", title: "提示",
content: "<p>确定要清空购物车吗?清空后不可恢复</p>", content: "确定要清空购物车吗?清空后不可恢复",
onOk: () => { onOk: () => {
APICart.clearCart().then((res) => { APICart.clearCart().then((res) => {
if (res.success) { if (res.success) {
@@ -512,6 +517,10 @@ export default {
} }
/** 商品列表 */ /** 商品列表 */
.cart-goods { .cart-goods {
.cart-goods-table {
border: 1px solid #e0e0e0;
@include background_color($light_white_background_color);
}
&-title { &-title {
height: 50px; height: 50px;
@include background_color($light_white_background_color); @include background_color($light_white_background_color);
@@ -519,7 +528,8 @@ export default {
display: flex; display: flex;
align-items: center; align-items: center;
padding: 0 20px; padding: 0 20px;
div { border-bottom: 1px solid #e0e0e0;
div:not(.cart-col-check) {
text-align: center; text-align: center;
} }
.goods-title { .goods-title {
@@ -532,7 +542,26 @@ export default {
height: 300px; height: 300px;
padding-top: 100px; padding-top: 100px;
} }
&-items { .cart-goods-list-box {
@include background_color($light_white_background_color);
}
.cart-col-check {
width: 60px;
min-width: 60px;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: flex-start;
text-align: left;
:deep(.el-checkbox) {
margin-right: 0;
}
}
.cart-shop-group {
&:not(:last-child) {
border-bottom: 1px solid #e0e0e0;
}
.shop-name { .shop-name {
height: 50px; height: 50px;
display: flex; display: flex;
@@ -540,8 +569,14 @@ export default {
padding: 0 20px; padding: 0 20px;
position: relative; position: relative;
@include title_color($light_title_color); @include title_color($light_title_color);
> * { .shop-info {
width: 50%; flex: 1;
min-width: 0;
display: flex;
align-items: center;
.promotion-notice {
margin-left: auto;
}
} }
.go-shop-page:hover { .go-shop-page:hover {
color: $theme_color; color: $theme_color;
@@ -559,6 +594,7 @@ export default {
.shop-coupon { .shop-coupon {
width: 80px; width: 80px;
height: 24px; height: 24px;
flex-shrink: 0;
position: relative; position: relative;
background: url(../assets/images/cart-coupon-icons02.png) 0 0 no-repeat; background: url(../assets/images/cart-coupon-icons02.png) 0 0 no-repeat;
> div { > div {
@@ -569,7 +605,6 @@ export default {
height: 300px; height: 300px;
background-color: #fff; background-color: #fff;
border: 1px solid $theme_color; border: 1px solid $theme_color;
// border-radius: 3px;
z-index: 1; z-index: 1;
padding: 10px 20px; padding: 10px 20px;
&::before { &::before {
@@ -612,6 +647,8 @@ export default {
.promotion-notice { .promotion-notice {
text-align: right; text-align: right;
font-size: 12px; font-size: 12px;
flex-shrink: 0;
margin-left: 10px;
} }
.shop-coupon-show { .shop-coupon-show {
background-position-y: -34px; background-position-y: -34px;
@@ -620,12 +657,16 @@ export default {
.goods-item { .goods-item {
position: relative; position: relative;
@extend .cart-goods-title; @extend .cart-goods-title;
@include background_color($light_white_background_color); background: transparent;
border-top: 1px solid #f0f0f0;
padding: 10px 20px; padding: 10px 20px;
height: auto; height: auto;
.cart-col-check {
padding-left: 20px;
box-sizing: border-box;
}
> div:nth-child(1) { > div:nth-child(1) {
padding-left: 15px; width: 60px;
width: 30px;
} }
> div:nth-child(2) { > div:nth-child(2) {
cursor: pointer; cursor: pointer;
@@ -647,9 +688,38 @@ export default {
} }
} }
} }
> div:nth-child(5) { .cart-col-price,
.cart-col-subtotal {
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
text-align: center;
}
.cart-col-subtotal {
font-weight: bold; font-weight: bold;
} }
.cart-col-qty {
position: relative;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
:deep(.el-input-number) {
width: 100%;
}
.cart-qty-stock {
position: absolute;
top: calc(100% + 4px);
left: 50%;
transform: translateX(-50%);
white-space: nowrap;
line-height: 1.2;
color: #999;
}
}
.num-input { .num-input {
width: 60px; width: 60px;
border: 1px solid #999; border: 1px solid #999;

View File

@@ -13,14 +13,15 @@
<div class="store-collect" v-if="!takeDownSale && goodsMsg.data"> <div class="store-collect" v-if="!takeDownSale && goodsMsg.data">
<span class="mr_10" v-if="goodsMsg.data"> <span class="mr_10" v-if="goodsMsg.data">
<router-link <router-link
class="store-name-link"
:to="{ path: '/merchant', query: { id: goodsMsg.data.storeId } }" :to="{ path: '/merchant', query: { id: goodsMsg.data.storeId } }"
>{{ goodsMsg.data.storeName }}</router-link> >{{ goodsMsg.data.storeName }}</router-link>
</span> </span>
<span @click="collect"> <span class="store-collect-btn" @click="collect">
<el-icon :color="storeCollected ? '#ed3f14' : '#666'"><StarFilled /></el-icon> <el-icon :color="storeCollected ? '#ffc107' : '#666'"><StarFilled /></el-icon>
{{ storeCollected? "已收藏店铺": "收藏店铺" }} {{ storeCollected? "已收藏店铺": "收藏店铺" }}
</span> </span>
<span class="ml_10" @click="IMService(goodsMsg.data.storeId,goodsMsg.data.goodsId,goodsMsg.data.id)">联系客服</span> <span class="ml_10 contact-service-btn" @click="IMService(goodsMsg.data.storeId,goodsMsg.data.goodsId,goodsMsg.data.id)">联系客服</span>
</div> </div>
</div> </div>
</div> </div>
@@ -233,12 +234,27 @@ export default {
transform: translateY(-50%); transform: translateY(-50%);
color: #999; color: #999;
span { span:not(.store-collect-btn):not(.contact-service-btn) {
&:hover { &:hover {
cursor: pointer; cursor: pointer;
color: $theme_color; color: $theme_color;
} }
} }
.store-collect-btn,
.contact-service-btn {
cursor: pointer;
}
.store-name-link {
color: $theme_color;
text-decoration: none;
&:hover {
color: $theme_color;
text-decoration: none;
}
}
} }
} }
.sale-btn{ .sale-btn{

View File

@@ -56,8 +56,7 @@
<div class="goods-show-detail"> <div class="goods-show-detail">
<el-tag <el-tag
v-if="item.salesModel === 'WHOLESALE'" v-if="item.salesModel === 'WHOLESALE'"
class="goods-show-tag" class="goods-show-tag goods-show-tag-wholesale"
color="purple"
> >
批发 批发
</el-tag> </el-tag>
@@ -302,6 +301,17 @@ export default {
} }
} }
.goods-show-tag-wholesale {
margin-right: 5px;
background-color: #f9f0ff;
border-color: #d3adf7;
color: #9254de;
:deep(.el-tag__content) {
color: #9254de;
}
}
.goods-show-tag-physical { .goods-show-tag-physical {
background-color: #e6f4ff; background-color: #e6f4ff;
border-color: #b3d8ff; border-color: #b3d8ff;

View File

@@ -109,7 +109,7 @@ export default {
cancel (id) { // 取消投诉 cancel (id) { // 取消投诉
this.$Modal.confirm({ this.$Modal.confirm({
title: '取消投诉', title: '取消投诉',
content: '<p>确定取消投诉吗?</p>', content: '确定取消投诉吗?',
onOk: () => { onOk: () => {
clearComplain(id).then((res) => { clearComplain(id).then((res) => {
if (res.success) { if (res.success) {

View File

@@ -362,18 +362,36 @@ export default {
} }
}); });
}, },
normalizeWithdrawError (message) {
if (!message) return '提现失败,请稍后重试';
const text = String(message);
if (text.includes('最少提现金额为1元')) {
return '提现金额单次最少提现金额为1元';
}
const colonIndex = text.indexOf(':');
if (colonIndex > -1 && colonIndex < text.length - 1) {
return text.slice(colonIndex + 1).trim();
}
return text;
},
withdraw() { withdraw() {
// 申请提现 const price = Number(this.withdrawPrice);
if (!price || price < 1) {
this.$Message.error('提现金额单次最少提现金额为1元');
return;
}
distCash({ price: this.withdrawPrice }).then((res) => { distCash({ price: this.withdrawPrice }).then((res) => {
this.withdrawApplyModal = false;
this.price = 0;
if (res.success) { if (res.success) {
this.$Message.success("申请已提交,请等待审核"); this.withdrawApplyModal = false;
this.withdrawPrice = 0;
this.$Message.success('申请已提交,请等待审核');
this.distribution(); this.distribution();
this.getLog(); this.getLog();
} else { } else {
this.$Message.error(res.message); this.$Message.error(this.normalizeWithdrawError(res.message));
} }
}).catch((err) => {
this.$Message.error(this.normalizeWithdrawError(err?.message || err?.data?.message));
}); });
}, },
qrcodeData(data64) { qrcodeData(data64) {

View File

@@ -146,8 +146,8 @@ export default {
cancel(id) { // 取消收藏 cancel(id) { // 取消收藏
let typeName = this.params.type === 'GOODS' ? '商品' : '店铺' let typeName = this.params.type === 'GOODS' ? '商品' : '店铺'
this.$Modal.confirm({ this.$Modal.confirm({
title: 'Title', title: '取消收藏',
content: `<p>确定取消收藏该${typeName}吗?</p>`, content: `确定取消收藏该${typeName}吗?`,
onOk: () => { onOk: () => {
cancelCollect(this.params.type, id).then(res => { cancelCollect(this.params.type, id).then(res => {
if (res.success) { if (res.success) {
@@ -160,8 +160,8 @@ export default {
cancelStore(id) { // 取消收藏 cancelStore(id) { // 取消收藏
let typeName = this.params.type === 'GOODS' ? '商品' : '店铺' let typeName = this.params.type === 'GOODS' ? '商品' : '店铺'
this.$Modal.confirm({ this.$Modal.confirm({
title: 'Title', title: '取消收藏',
content: `<p>确定取消收藏该${typeName}吗?</p>`, content: `确定取消收藏该${typeName}吗?`,
onOk: () => { onOk: () => {
cancelStoreCollect(this.params.type, id).then(res => { cancelStoreCollect(this.params.type, id).then(res => {
if (res.success) { if (res.success) {

View File

@@ -139,6 +139,13 @@ export default {
this.status = this.$route.query.status this.status = this.$route.query.status
}, },
methods: { methods: {
normalizePwdError (message) {
if (!message) return '修改失败,请稍后重试';
if (String(message).includes('旧密码') || String(message).includes('密码不正确')) {
return '旧密码不正确';
}
return message;
},
// 修改 // 修改
handleRegist () { handleRegist () {
this.$refs['formRegister'].validate((valid) => { this.$refs['formRegister'].validate((valid) => {
@@ -146,7 +153,7 @@ export default {
const {newPassword, againPassword, password} = this.formRegister const {newPassword, againPassword, password} = this.formRegister
if (newPassword !== againPassword) { if (newPassword !== againPassword) {
this.$Message.error({ this.$Message.error({
content: '新密码不一致' content: '新密码和确认密码不一致'
}); });
return return
} }
@@ -154,10 +161,14 @@ export default {
params.newPassword = md5(newPassword) params.newPassword = md5(newPassword)
params.password = md5(password) params.password = md5(password)
editPwd(params).then(res => { editPwd(params).then(res => {
if (res.message === 'success' && res.result) { if (res.success && res.result) {
this.$Message.success('修改密码成功'); this.$Message.success('修改密码成功');
this.$router.push('/home') this.$router.push('/home')
} else {
this.$Message.error(this.normalizePwdError(res.message));
} }
}).catch((err) => {
this.$Message.error(this.normalizePwdError(err?.message || err?.data?.message));
}); });
} }
}) })
@@ -183,7 +194,7 @@ export default {
} }
if (newPassword !== againPassword) { if (newPassword !== againPassword) {
this.$Message.error({ this.$Message.error({
content: '新密码不一致' content: '新密码和确认密码不一致'
}); });
return return
} }

View File

@@ -37,26 +37,30 @@
<div> <div>
<el-input type="textarea" maxlength="500" show-word-limit :rows="4" v-model="orderGoods.content" /> <el-input type="textarea" maxlength="500" show-word-limit :rows="4" v-model="orderGoods.content" />
</div> </div>
<div style="display:flex;align-items:center;"> <div class="upload-section">
<div class="demo-upload-list" v-for="(img, index) in orderGoods.uploadList" :key="index"> <div class="upload-row">
<img :src="img"> <div class="demo-upload-list" v-for="(img, index) in orderGoods.uploadList" :key="index">
<div class="demo-upload-list-cover"> <img :src="img">
<div class="demo-upload-list-cover">
<el-icon @click="handleView(img)"><View /></el-icon> <el-icon @click="handleView(img)"><View /></el-icon>
<el-icon @click="handleRemove(index)"><Delete /></el-icon> <el-icon @click="handleRemove(index)"><Delete /></el-icon>
</div>
</div> </div>
</div> <el-upload
<el-upload class="upload-trigger"
:show-upload-list="false" :show-file-list="false"
:on-success="handleSuccess" :on-success="handleSuccess"
:before-upload="handleBeforeUpload" :before-upload="handleBeforeUpload"
:format="['jpg','jpeg','png']" :format="['jpg','jpeg','png']"
:action="action" :action="action"
:headers="accessToken" :headers="accessToken"
style="display: inline-block;width:58px;"> >
<div class="hover-pointer icon-upload" style=""> <div class="hover-pointer icon-upload">
<el-icon :size="20"><Camera /></el-icon> <el-icon :size="20"><Camera /></el-icon>
</div> </div>
</el-upload> </el-upload>
</div>
<div class="describe">上传评价图片最多9张</div>
</div> </div>
</div> </div>
</li> </li>
@@ -165,7 +169,7 @@ export default {
this.$forceUpdate() this.$forceUpdate()
}, },
handleBeforeUpload () { // 上传之前钩子 handleBeforeUpload () { // 上传之前钩子
const check = this.orderGoods.uploadList.length < 10; const check = this.orderGoods.uploadList.length < 9;
if (!check) { if (!check) {
this.$Notice.warning({ this.$Notice.warning({
title: '最多可以上传九张图片' title: '最多可以上传九张图片'
@@ -237,7 +241,7 @@ export default {
} }
.demo-upload-list{ .demo-upload-list{
display: inline-block; display: block;
width: 60px; width: 60px;
height: 60px; height: 60px;
text-align: center; text-align: center;
@@ -248,8 +252,7 @@ export default {
background: #fff; background: #fff;
position: relative; position: relative;
box-shadow: 0 1px 1px rgba(0,0,0,.2); box-shadow: 0 1px 1px rgba(0,0,0,.2);
margin-right: 4px; flex-shrink: 0;
margin-top: 10px;
} }
.demo-upload-list img{ .demo-upload-list img{
width: 100%; width: 100%;
@@ -274,17 +277,47 @@ export default {
margin: 0 2px; margin: 0 2px;
} }
.icon-upload { .icon-upload {
width: 58px; width: 60px;
height:58px; height: 60px;
line-height: 58px; line-height: 60px;
text-align:center; text-align: center;
display: inline-block; display: flex;
border:1px dashed #999; align-items: center;
justify-content: center;
border: 1px dashed #999;
border-radius: 4px; border-radius: 4px;
margin-top: 10px; box-sizing: border-box;
&:hover{ &:hover{
cursor: pointer; cursor: pointer;
border-color: $theme_color; border-color: $theme_color;
} }
} }
.upload-section {
margin-top: 10px;
}
.upload-row {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 4px;
}
.upload-trigger {
display: inline-flex;
align-items: center;
flex-shrink: 0;
:deep(.el-upload) {
display: inline-flex;
align-items: center;
}
}
.describe {
font-size: 12px;
color: #999;
margin-top: 8px;
}
</style> </style>

View File

@@ -26,9 +26,9 @@
</el-form-item> </el-form-item>
</el-form> </el-form>
</div> </div>
<div class="mt_20"> <div class="action-bar mt_20">
<el-button type="primary" class="mr_10" :loading="loading" @click="save">保存收货地址</el-button>
<el-button @click="$router.back()">返回</el-button> <el-button @click="$router.back()">返回</el-button>
<el-button type="primary" class="ml_10" :loading="loading" @click="save">保存收货地址</el-button>
</div> </div>
</UserCenterLayout> </UserCenterLayout>
@@ -153,4 +153,9 @@ export default {
.add-box { .add-box {
margin: 40px 0; margin: 40px 0;
} }
.action-bar {
display: flex;
justify-content: flex-end;
}
</style> </style>

View File

@@ -39,7 +39,7 @@
</div> </div>
</div> </div>
<div> <div>
<span @click="shopPage(order.shopId)">{{ order.storeName }}</span> <span @click="shopPage(order.storeId)">{{ order.storeName }}</span>
</div> </div>
<div class="order-actions"> <div class="order-actions">
<!-- 订单基础操作 --> <!-- 订单基础操作 -->
@@ -89,7 +89,7 @@
</div> </div>
<div class="mt_10"> <div class="mt_10">
<span class="global_color" <span class="global_color"
>{{ $filters.unitPrice(singleOrder.flowPrice, "¥") }} </span>x {{ singleOrder.num }} >{{ $filters.unitPrice(singleOrder.flowPrice, "¥") }} </span> x {{ singleOrder.num }}
</div> </div>
</div> </div>
</div> </div>
@@ -173,7 +173,7 @@ export default {
cancel(sn) { // 取消售后申请 cancel(sn) { // 取消售后申请
this.$Modal.confirm({ this.$Modal.confirm({
title: '取消', title: '取消',
content: '<p>确定取消此次售后申请吗?</p>', content: '确定取消此次售后申请吗?',
onOk: () => { onOk: () => {
cancelAfterSale(sn).then(res => { cancelAfterSale(sn).then(res => {
if (res.success) { if (res.success) {

View File

@@ -4,8 +4,12 @@
<!-- 操作按钮 --> <!-- 操作按钮 -->
<el-card class="mb_10" shadow="never" v-if="(afterSale.serviceStatus == 'PASS' && <el-card class="mb_10" shadow="never" v-if="(afterSale.serviceStatus == 'PASS' &&
afterSale.serviceType != 'RETURN_MONEY') || (afterSale.afterSaleAllowOperationVO && afterSale.afterSaleAllowOperationVO.cancel)"> afterSale.serviceType != 'RETURN_MONEY') || (afterSale.afterSaleAllowOperationVO && afterSale.afterSaleAllowOperationVO.cancel)">
<el-button type="success" @click="openModal" v-if="afterSale.serviceStatus == 'PASS' && <el-button
afterSale.serviceType != 'RETURN_MONEY'" size="small">提交物流</el-button>&nbsp; @click="openModal"
v-if="afterSale.serviceStatus == 'PASS' && afterSale.serviceType != 'RETURN_MONEY'"
size="small"
class="submit-logistics-btn"
>提交物流</el-button>&nbsp;
<el-button type="danger" @click="cancel(afterSale.sn)" v-if="afterSale.afterSaleAllowOperationVO && afterSale.afterSaleAllowOperationVO.cancel" size="small">取消售后</el-button> <el-button type="danger" @click="cancel(afterSale.sn)" v-if="afterSale.afterSaleAllowOperationVO && afterSale.afterSaleAllowOperationVO.cancel" size="small">取消售后</el-button>
</el-card> </el-card>
<div class="order-card"> <div class="order-card">
@@ -302,4 +306,17 @@ table{
padding: 5px; padding: 5px;
} }
} }
:deep(.submit-logistics-btn) {
background-color: #ff9900 !important;
border-color: #ff9900 !important;
color: #fff !important;
&:hover,
&:focus {
background-color: #e68a00 !important;
border-color: #e68a00 !important;
color: #fff !important;
}
}
</style> </style>

View File

@@ -17,10 +17,12 @@
<div class="eval-con"> <div class="eval-con">
<div class="eval-form"> <div class="eval-form">
<span class="color999">投诉主题</span> <div class="topic-row">
<el-select v-model="form.complainTopic" style="width:260px;margin-bottom:10px"> <span class="color999 topic-label">投诉主题</span>
<el-option v-for="item in reasonList" :value="item.reason" :key="item.id">{{ item.reason }}</el-option> <el-select v-model="form.complainTopic" class="topic-select">
</el-select> <el-option v-for="item in reasonList" :value="item.reason" :key="item.id">{{ item.reason }}</el-option>
</el-select>
</div>
<el-input type="textarea" maxlength="500" show-word-limit :rows="4" placeholder="请输入投诉内容" v-model="form.content" /> <el-input type="textarea" maxlength="500" show-word-limit :rows="4" placeholder="请输入投诉内容" v-model="form.content" />
</div> </div>
<div class="upload-section"> <div class="upload-section">
@@ -186,6 +188,22 @@ export default {
} }
} }
.topic-row {
display: flex;
align-items: center;
margin-bottom: 10px;
}
.topic-label {
flex-shrink: 0;
margin-right: 8px;
}
.topic-select {
width: 200px;
flex-shrink: 0;
}
.demo-upload-list{ .demo-upload-list{
display: block; display: block;
width: 60px; width: 60px;

View File

@@ -174,17 +174,6 @@
<td>{{ $filters.unitPrice(goods.refundPrice, "¥") }}</td> <td>{{ $filters.unitPrice(goods.refundPrice, "¥") }}</td>
<td>{{ $filters.unitPrice((goods.goodsPrice * goods.num), "¥") }}</td> <td>{{ $filters.unitPrice((goods.goodsPrice * goods.num), "¥") }}</td>
<td class="order-item-actions"> <td class="order-item-actions">
<el-button
v-if="
goods.afterSaleStatus.includes('NOT_APPLIED') ||
goods.afterSaleStatus.includes('PART_AFTER_SALE')
"
@click="applyAfterSale(goods.sn)"
type="default"
size="small"
class="order-item-action-btn mb_5"
>申请售后</el-button
>
<el-button <el-button
v-if="goods.commentStatus == 'UNFINISHED'" v-if="goods.commentStatus == 'UNFINISHED'"
@click="comment(order.order.sn, goodsIndex)" @click="comment(order.order.sn, goodsIndex)"
@@ -201,6 +190,17 @@
size="small" size="small"
>投诉</el-button >投诉</el-button
> >
<el-button
v-if="
goods.afterSaleStatus.includes('NOT_APPLIED') ||
goods.afterSaleStatus.includes('PART_AFTER_SALE')
"
@click="applyAfterSale(goods.sn)"
type="default"
size="small"
class="order-item-action-btn mb_5"
>申请售后</el-button
>
</td> </td>
</tr> </tr>
</tbody> </tbody>
@@ -256,7 +256,7 @@
</el-dialog> </el-dialog>
<!--查询物流--> <!--查询物流-->
<el-dialog v-model="logisticsModal" width="40"> <el-dialog v-model="logisticsModal" width="40%">
<template #header><p><span>查询物流</span></p></template> <template #header><p><span>查询物流</span></p></template>
<div class="layui-layer-wrap"> <div class="layui-layer-wrap">
<dl> <dl>

View File

@@ -22,11 +22,15 @@
</ul> </ul>
<!-- 分页 --> <!-- 分页 -->
<div class="page-size"> <div class="page-size">
<el-pagination :total="total" <el-pagination
:total="total"
:current-page="params.pageNumber"
:page-size="params.pageSize"
:page-sizes="[10, 20, 30, 40, 50, 100]"
layout="sizes, prev, pager, next, jumper"
@current-change="changePageNum" @current-change="changePageNum"
@size-change="changePageSize" @size-change="changePageSize"
:page-size="params.pageSize" />
layout="sizes, prev, pager, next, jumper"></el-pagination>
</div> </div>
</UserCenterLayout> </UserCenterLayout>
</div> </div>
@@ -44,7 +48,7 @@ export default {
spinShow: false, // 控制loading是否加载 spinShow: false, // 控制loading是否加载
params: { params: {
pageNumber: 1, pageNumber: 1,
pageSize: 30, pageSize: 10,
order: "desc", order: "desc",
sort: "updateTime", sort: "updateTime",
}, },

View File

@@ -29,6 +29,16 @@ export const sellerUrl =
let isRefreshToken = 0; let isRefreshToken = 0;
const refreshToken = getTokenDebounce(); const refreshToken = getTokenDebounce();
/** 会员中心(/home/*)页面使用局部 loading不启用全屏遮罩 */
function isMemberCenterRoute() {
try {
const path = router.currentRoute?.value?.path || "";
return path === "/home" || path.startsWith("/home/");
} catch {
return false;
}
}
const service = axios.create({ const service = axios.create({
timeout: 10000, timeout: 10000,
baseURL: buyerUrl, baseURL: buyerUrl,
@@ -56,7 +66,8 @@ service.interceptors.request.use(
const method = (config.method || "get").toLowerCase(); const method = (config.method || "get").toLowerCase();
const isReadRequest = method === "get" || method === "head"; const isReadRequest = method === "get" || method === "head";
const shouldShowLoading = const shouldShowLoading =
loading === true || (!isReadRequest && loading !== false); loading === true ||
(!isReadRequest && loading !== false && !isMemberCenterRoute());
if (typeof window !== "undefined" && shouldShowLoading) { if (typeof window !== "undefined" && shouldShowLoading) {
config.loading = ElLoading.service({ fullscreen: true }); config.loading = ElLoading.service({ fullscreen: true });
@@ -166,7 +177,7 @@ service.interceptors.response.use(
if (errorResponse.status === 404) { if (errorResponse.status === 404) {
return Promise.reject(buildRejectError(error, { message: "请求的资源不存在" })); return Promise.reject(buildRejectError(error, { message: "请求的资源不存在" }));
} }
if (error.message) { if (error.message && !error.config?.skipMessage) {
const _message = const _message =
error.code === "ECONNABORTED" error.code === "ECONNABORTED"
? "连接超时,请稍候再试!" ? "连接超时,请稍候再试!"

View File

@@ -450,9 +450,14 @@ export default {
}, },
// 下架 // 下架
lower() { lower() {
const reason = this.underForm.reason ? this.underForm.reason.trim() : "";
if (!reason) {
this.$Message.error("下架原因不能为空");
return;
}
let params = { let params = {
goodsId: this.id, goodsId: this.id,
reason:this.underForm.reason reason
}; };
lowGoods(params).then((res) => { lowGoods(params).then((res) => {
this.$Modal.remove(); this.$Modal.remove();

View File

@@ -93,3 +93,8 @@
color: #c0c4cc; color: #c0c4cc;
cursor: not-allowed; cursor: not-allowed;
} }
/* MessageBox 需高于嵌套 Dialog如上传图片/资源库 z-index: 3500~3700 */
.el-overlay.is-message-box {
z-index: 4000 !important;
}

View File

@@ -106,8 +106,24 @@
.el-form-item__content { .el-form-item__content {
margin-left: 0 !important; margin-left: 0 !important;
align-items: flex-start;
} }
} }
.sku-val-item {
display: flex;
flex-direction: column;
align-items: flex-start;
}
.sku-val-input-row {
display: flex;
align-items: center;
}
.sku-val-image {
margin-top: 10px;
}
} }
div.base-info-item { div.base-info-item {

View File

@@ -128,7 +128,7 @@
> >
<template #item="{ element }"> <template #item="{ element }">
<div class="demo-upload-list"> <div class="demo-upload-list">
<img :src="element" /> <img :src="element" @error="handleGoodsPictureError($event, element)" />
<div class="demo-upload-list-cover"> <div class="demo-upload-list-cover">
<div> <div>
<el-button link type="primary" @click="handleViewGoodsPicture(element)">预览</el-button> <el-button link type="primary" @click="handleViewGoodsPicture(element)">预览</el-button>
@@ -220,29 +220,27 @@
<el-form :model="item" class="sku-val-form flex"> <el-form :model="item" class="sku-val-form flex">
<!--规格值文本列表--> <!--规格值文本列表-->
<el-form-item v-for="(val, index) in item.spec_values" :key="index" <el-form-item v-for="(val, index) in item.spec_values" :key="index"
class="sku-item-content-val flex" label="" style="line-height: 32px;"> class="sku-item-content-val" label="">
<div style="display: flex; justify-content: flex-start; align-items: center;"> <div class="sku-val-item">
<div class="sku-val-input-row">
<el-autocomplete
<el-autocomplete v-model="val.value"
v-model="val.value" :disabled="containsSameSkuItem"
:disabled="containsSameSkuItem" :fetch-suggestions="(q, cb) => cb([])"
:fetch-suggestions="(q, cb) => cb([])" maxlength="30"
maxlength="30" placeholder="请输入规格值"
placeholder="请输入规格值" style="width: 180px"
style="width: 180px" @focus="changeSkuVals(val, item.name)"
@focus="changeSkuVals(val, item.name)" @blur="checkSkuVal(val, $index, item)"
@blur="checkSkuVal(val, $index, item)" @change="skuValueChange(val, index, item)"
@change="skuValueChange(val, index, item)" />
/> <el-button link type="danger" style="margin-left: 6px" @click="handleCloseSkuValue(val, index, item)">
<el-button link type="danger" style="margin-left: 6px" @click="handleCloseSkuValue(val, index, item)"> <el-icon><Delete /></el-icon>
<el-icon><Delete /></el-icon> </el-button>
</el-button> </div>
</div>
<!-- 内联错误提示 --> <!-- 内联错误提示 -->
<div v-if="val._error" class="sku-inline-error">{{ val._error }}</div> <div v-if="val._error" class="sku-inline-error">{{ val._error }}</div>
<div v-if="$index === 0 && openImage" style="margin-top: 10px"> <div v-if="$index === 0 && openImage" class="sku-val-image">
<vuedraggable <vuedraggable
v-model="val.images" v-model="val.images"
:animation="200" :animation="200"
@@ -279,12 +277,13 @@
> >
<el-icon :size="40"><Camera /></el-icon> <el-icon :size="40"><Camera /></el-icon>
</el-upload> </el-upload>
</div>
</div> </div>
</el-form-item> </el-form-item>
<el-form-item <el-form-item
v-if="item.spec_values.length < 10 && item.spec_values.length >= 1 && item.spec_values[0].value !== ''" v-if="item.spec_values.length < 10 && item.spec_values.length >= 1 && item.spec_values[0].value !== ''"
class="sku-item-content-val flex" label="" style="line-height: 32px;"> class="sku-item-content-val" label="">
<el-autocomplete <el-autocomplete
v-model="newSkuValues[$index]" v-model="newSkuValues[$index]"
:disabled="containsSameSkuItem" :disabled="containsSameSkuItem"
@@ -498,6 +497,7 @@
ref="ossManage" ref="ossManage"
:isComponent="true" :isComponent="true"
:initialize="picModelFlag" :initialize="picModelFlag"
:max-select="selectedFormBtnName === 'goodsGalleryFiles' ? 1 : 0"
@callback="callbackSelected" @callback="callbackSelected"
@selected="(list) => { selectedImage = list }" @selected="(list) => { selectedImage = list }"
/> />
@@ -664,6 +664,9 @@ export default {
/** 商品参数用于验证 */ /** 商品参数用于验证 */
goodsParams: [], goodsParams: [],
}, },
invalidGoodsGalleryFiles: [],
goodsPictureFallback:
"data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%22148%22%20height%3D%22148%22%20viewBox%3D%220%200%20148%20148%22%3E%3Crect%20width%3D%22148%22%20height%3D%22148%22%20fill%3D%22%23f5f7fa%22/%3E%3Ctext%20x%3D%2274%22%20y%3D%2274%22%20text-anchor%3D%22middle%22%20dominant-baseline%3D%22middle%22%20font-size%3D%2212%22%20fill%3D%22%23999%22%3EImage%20unavailable%3C/text%3E%3C/svg%3E",
/** 表格头 */ /** 表格头 */
skuTableColumn: [], skuTableColumn: [],
/** 表格数据 */ /** 表格数据 */
@@ -916,13 +919,28 @@ export default {
}, },
// 查看商品大图 // 查看商品大图
handleViewGoodsPicture(url) { handleViewGoodsPicture(url) {
if (this.invalidGoodsGalleryFiles.includes(url)) {
this.$Message.warning("图片链接已失效,请重新上传主图");
return;
}
this.previewGoodsPicture = url; this.previewGoodsPicture = url;
this.goodsPictureVisible = true; this.goodsPictureVisible = true;
}, },
handleGoodsPictureError(event, url) {
if (!this.invalidGoodsGalleryFiles.includes(url)) {
this.invalidGoodsGalleryFiles.push(url);
}
const img = event && event.target;
if (!img || img.dataset.fallbackApplied) return;
img.dataset.fallbackApplied = "true";
img.src = this.goodsPictureFallback;
},
// 移除商品图片 // 移除商品图片
handleRemoveGoodsPicture(file) { handleRemoveGoodsPicture(file) {
this.baseInfoForm.goodsGalleryFiles = this.baseInfoForm.goodsGalleryFiles =
this.baseInfoForm.goodsGalleryFiles.filter((i) => i !== file); this.baseInfoForm.goodsGalleryFiles.filter((i) => i !== file);
this.invalidGoodsGalleryFiles =
this.invalidGoodsGalleryFiles.filter((i) => i !== file);
}, },
assignSkuTableIndex() { assignSkuTableIndex() {
this.skuTableData.forEach((row, index) => { this.skuTableData.forEach((row, index) => {
@@ -1151,6 +1169,7 @@ export default {
response.result.recommend response.result.recommend
? (response.result.recommend = 1) ? (response.result.recommend = 1)
: (response.result.recommend = 0); : (response.result.recommend = 0);
this.invalidGoodsGalleryFiles = [];
this.baseInfoForm = { ...this.baseInfoForm, ...response.result }; this.baseInfoForm = { ...this.baseInfoForm, ...response.result };
this.baseInfoForm.release = 1; //即使是被放入仓库,修改的时候也会显示会立即发布 this.baseInfoForm.release = 1; //即使是被放入仓库,修改的时候也会显示会立即发布
this.categoryId = response.result.categoryPath.split(",")[2]; this.categoryId = response.result.categoryPath.split(",")[2];
@@ -2117,6 +2136,14 @@ export default {
this.$Message.error("请上传商品图片"); this.$Message.error("请上传商品图片");
return; return;
} }
const invalidGalleryFiles = submit.goodsGalleryFiles.filter((url) =>
this.invalidGoodsGalleryFiles.includes(url)
);
if (invalidGalleryFiles.length > 0) {
this.submitLoading = false;
this.$Message.error("存在已失效的商品主图,请删除后重新上传");
return;
}
if (submit.templateId === "") submit.templateId = 0; if (submit.templateId === "") submit.templateId = 0;
let flag = false; let flag = false;
let paramValue = ""; let paramValue = "";

View File

@@ -3,12 +3,12 @@
<el-button @click="handleClickUploadImage">上传图片</el-button> <el-button @click="handleClickUploadImage">上传图片</el-button>
<el-dialog v-model="show" width="850px" title="上传图片" append-to-body :z-index="3500"> <el-dialog v-model="show" width="850px" title="上传图片" append-to-body :z-index="3500">
<div class="import-oss" @click="importOSS">从资源库中导入</div> <div class="import-oss" @click="importOSS">从资源库中导入</div>
<div style="display: flex; flex-wrap: wrap"> <div class="upload-images-wrap">
<vuedraggable <vuedraggable
v-model="images" v-model="images"
:animation="200" :animation="200"
:item-key="draggableItemKey" :item-key="draggableItemKey"
style="display: flex; flex-wrap: wrap" class="upload-images-draggable"
> >
<template #item="{ element, index }"> <template #item="{ element, index }">
<div class="upload-list"> <div class="upload-list">
@@ -212,27 +212,57 @@ export default {
.wrapper { .wrapper {
margin: 10px 0; margin: 10px 0;
} }
$upload-item-size: 150px;
.upload-images-wrap {
display: flex;
flex-wrap: wrap;
align-items: flex-start;
}
.upload-images-draggable {
display: flex;
flex-wrap: wrap;
}
.upload-list { .upload-list {
width: 150px; width: $upload-item-size;
height: 150px; height: $upload-item-size;
text-align: center; text-align: center;
border: 1px solid transparent; border: 1px solid #dcdfe6;
border-radius: 4px; border-radius: 4px;
display: inline-block; display: inline-block;
background: #fff; background: #fff;
position: relative; position: relative;
margin-right: 4px; margin: 0 4px 4px 0;
vertical-align: bottom; overflow: hidden;
flex-shrink: 0;
} }
.upload-box { .upload-box {
margin: 10px 0; width: $upload-item-size;
height: $upload-item-size;
margin: 0 4px 4px 0;
display: inline-block; display: inline-block;
vertical-align: bottom; flex-shrink: 0;
:deep(.el-upload) {
width: $upload-item-size;
height: $upload-item-size;
display: block;
}
:deep(.el-upload-dragger) {
width: $upload-item-size;
height: $upload-item-size;
min-height: $upload-item-size;
padding: 0;
display: flex;
align-items: center;
justify-content: center;
border-radius: 4px;
box-sizing: border-box;
}
} }
.upload-trigger { .upload-trigger {
width: 148px; width: 100%;
height: 148px; height: 100%;
line-height: 148px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;

View File

@@ -277,7 +277,7 @@
<dd> <dd>
<el-button <el-button
v-if="canConfirmReceipt" v-if="canConfirmReceipt"
type="danger" type="primary"
:loading="confirmLoading" :loading="confirmLoading"
@click="sellerConfirmSubmit('PASS')" @click="sellerConfirmSubmit('PASS')"
style="margin-left: 5px" style="margin-left: 5px"
@@ -286,7 +286,7 @@
</el-button> </el-button>
<el-button <el-button
v-if="canConfirmReceipt" v-if="canConfirmReceipt"
type="primary" type="danger"
:loading="confirmLoading" :loading="confirmLoading"
@click="sellerConfirmSubmit('REFUSE')" @click="sellerConfirmSubmit('REFUSE')"
style="margin-left: 5px" style="margin-left: 5px"

View File

@@ -185,25 +185,31 @@
height: auto; height: auto;
margin: 0; margin: 0;
:deep(.el-checkbox__label) { .el-checkbox__label {
display: none; display: none;
} }
} }
&.oss-card-selectable {
cursor: pointer;
}
.preview { .preview {
width: 100%; width: 100%;
height: 26px; height: 26px;
background-color: #ffffff; background-color: #ffffff;
text-align: center;
line-height: 30px;
color: #666666; color: #666666;
position: absolute; position: absolute;
left: 0; left: 0;
bottom: 0; bottom: 0;
display: flex; display: flex;
flex-wrap: nowrap; flex-wrap: nowrap;
align-items: center;
justify-content: center;
> div { > div {
width: 100%;
flex: 1; flex: 1;
display: flex;
align-items: center;
justify-content: center;
height: 100%;
} }
} }
} }

View File

@@ -59,6 +59,10 @@
padding: 0 4px; padding: 0 4px;
} }
.oss-manage-date-popper {
z-index: 5000 !important;
}
</style> </style>
<template> <template>
<div class="search"> <div class="search">
@@ -88,6 +92,7 @@
end-placeholder="结束日期" end-placeholder="结束日期"
style="width: 240px" style="width: 240px"
type="daterange" type="daterange"
popper-class="oss-manage-date-popper"
@change="selectDateRange" @change="selectDateRange"
/> />
</el-form-item> </el-form-item>
@@ -162,37 +167,43 @@
<el-checkbox-group v-model="selectedOss" @change="selectOssChange"> <el-checkbox-group v-model="selectedOss" @change="selectOssChange">
<div class="img-box"> <div class="img-box">
<div v-for="(item, index) in data" :key="index" class="img-item"> <div v-for="(item, index) in data" :key="index" class="img-item">
<el-checkbox :value="item.id + ',' + item.url" class="check-box"> <div
<div class="card"
class="card" :class="{
@mouseenter="onMouseOver(item, index)" 'custom-checkbox-card-checked': selectedOss.includes(item.id + ',' + item.url),
@mouseleave="onMouseOut(item, index)" 'oss-card-selectable': isComponent && selectImage,
> }"
<img :src="item.url" alt="" /> @mouseenter="onMouseOver(item, index)"
<div v-if="item.isShowPreview" class="preview"> @mouseleave="onMouseOut(item, index)"
<div @click.prevent="download(item)"> @click="handleCardClick(item, $event)"
<el-tooltip content="下载" placement="top"> >
<el-icon :size="18"><Download /></el-icon> <el-checkbox
</el-tooltip> :value="item.id + ',' + item.url"
</div> class="card-checkbox"
<div @click.prevent="remove(item)"> @click.stop
<el-tooltip content="删除" placement="top"> />
<el-icon :size="18"><Delete /></el-icon> <img :src="item.url" alt="" />
</el-tooltip> <div v-if="item.isShowPreview" class="preview">
</div> <div @click.prevent="download(item)">
<div @click.prevent="showPic(item)"> <el-tooltip content="下载" placement="top">
<el-tooltip content="预览" placement="top"> <el-icon :size="18"><Download /></el-icon>
<el-icon :size="22"><View /></el-icon> </el-tooltip>
</el-tooltip> </div>
</div> <div @click.prevent="remove(item)">
<el-tooltip content="删除" placement="top">
<el-icon :size="18"><Delete /></el-icon>
</el-tooltip>
</div>
<div @click.prevent="showPic(item)">
<el-tooltip content="预览" placement="top">
<el-icon :size="22"><View /></el-icon>
</el-tooltip>
</div> </div>
</div> </div>
</el-checkbox>
<div>
<el-tooltip :content="item.name" placement="bottom">
<div class="text">{{ item.name }}</div>
</el-tooltip>
</div> </div>
<el-tooltip :content="item.name" placement="bottom">
<div class="text">{{ item.name }}</div>
</el-tooltip>
</div> </div>
</div> </div>
</el-checkbox-group> </el-checkbox-group>
@@ -1070,6 +1081,18 @@ export default {
this.$emit("selected", e); this.$emit("selected", e);
} }
}, },
handleCardClick(item, event) {
if (!(this.isComponent && this.selectImage)) return;
if (event?.target?.closest?.(".preview") || event?.target?.closest?.(".card-checkbox")) {
return;
}
const key = `${item.id},${item.url}`;
const next = this.selectedOss.includes(key)
? this.selectedOss.filter((k) => k !== key)
: [...this.selectedOss, key];
this.selectedOss = next;
this.selectOssChange(next);
},
// 页码改变时回调 // 页码改变时回调
pageChange(value) { pageChange(value) {
this.selectedOss = []; this.selectedOss = [];

View File

@@ -165,9 +165,13 @@
<div v-for="(item, index) in data" :key="index" class="img-item"> <div v-for="(item, index) in data" :key="index" class="img-item">
<div <div
class="card" class="card"
:class="{ 'custom-checkbox-card-checked': selectedOss.includes(item.id + ',' + item.url) }" :class="{
'custom-checkbox-card-checked': selectedOss.includes(item.id + ',' + item.url),
'oss-card-selectable': isComponent && selectImage
}"
@mouseenter="onMouseOver(item, index)" @mouseenter="onMouseOver(item, index)"
@mouseleave="onMouseOut(item, index)" @mouseleave="onMouseOut(item, index)"
@click="toggleSelectedOss(item)"
> >
<el-checkbox <el-checkbox
:value="item.id + ',' + item.url" :value="item.id + ',' + item.url"
@@ -176,17 +180,17 @@
/> />
<img :src="item.url" alt="" /> <img :src="item.url" alt="" />
<div v-if="item.isShowPreview" class="preview"> <div v-if="item.isShowPreview" class="preview">
<div @click.prevent="download(item)"> <div @click.stop.prevent="download(item)">
<el-tooltip content="下载" placement="top"> <el-tooltip content="下载" placement="top">
<el-icon :size="18"><Download /></el-icon> <el-icon :size="18"><Download /></el-icon>
</el-tooltip> </el-tooltip>
</div> </div>
<div @click.prevent="remove(item)"> <div @click.stop.prevent="remove(item)">
<el-tooltip content="删除" placement="top"> <el-tooltip content="删除" placement="top">
<el-icon :size="18"><Delete /></el-icon> <el-icon :size="18"><Delete /></el-icon>
</el-tooltip> </el-tooltip>
</div> </div>
<div @click.prevent="showPic(item)"> <div @click.stop.prevent="showPic(item)">
<el-tooltip content="预览" placement="top"> <el-tooltip content="预览" placement="top">
<el-icon :size="22"><View /></el-icon> <el-icon :size="22"><View /></el-icon>
</el-tooltip> </el-tooltip>
@@ -488,6 +492,10 @@ export default {
type: Boolean, type: Boolean,
default: false, default: false,
}, },
maxSelect: {
type: Number,
default: 0,
},
}, },
data() { data() {
return { return {
@@ -1059,6 +1067,10 @@ export default {
// 复选框值改变时触发 // 复选框值改变时触发
selectOssChange(e) { selectOssChange(e) {
if (e) { if (e) {
if (this.maxSelect > 0 && e.length > this.maxSelect) {
e = e.slice(-this.maxSelect);
this.selectedOss = e;
}
this.selectList = e.map(item => { this.selectList = e.map(item => {
return {id: item.split(',')[0]} return {id: item.split(',')[0]}
}); });
@@ -1069,6 +1081,22 @@ export default {
this.$emit("selected", e); this.$emit("selected", e);
} }
}, },
toggleSelectedOss(item) {
if (!item) return;
const value = `${item.id},${item.url}`;
const index = this.selectedOss.indexOf(value);
if (index > -1) {
this.selectedOss.splice(index, 1);
} else if (this.maxSelect === 1) {
this.selectedOss = [value];
} else {
if (this.maxSelect > 0 && this.selectedOss.length >= this.maxSelect) {
this.selectedOss = this.selectedOss.slice(1);
}
this.selectedOss.push(value);
}
this.selectOssChange(this.selectedOss);
},
// 页码改变时回调 // 页码改变时回调
pageChange(value) { pageChange(value) {
this.selectedOss = []; this.selectedOss = [];