ry_app/pages/addBrand/addBrand.vue

977 lines
24 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<view class="container">
<!-- 顶部导航栏 -->
<view class="navbar">
<view class="back-btn" @click="goBack">
<!-- <text class="back-icon"></text> -->
</view>
<view class="title">品牌管理</view>
</view>
<!-- 搜索框 -->
<view class="search-box">
<input
v-model="searchKeyword"
type="text"
placeholder="搜索品牌名称"
class="search-input"
@input="onSearchInput"
/>
</view>
<!-- 品牌树状列表 -->
<view class="brand-tree">
<!-- 一级品牌项 -->
<view v-for="(brand, index) in brandList" :key="index" class="level1-item">
<view class="level1-header">
<text class="expand-icon" @click="toggleExpand(index)">{{ brand.expanded ? '▼' : '▶' }}</text>
<text class="level1-name" @click="toggleExpand(index)">{{ brand.name }}</text>
<view class="menu-container" style="position: relative;">
<view class="menu-btn" @click.stop="toggleMenuOptions(index)">
<text class="menu-icon">···</text>
</view>
<!-- 菜单选项列表 -->
<view v-if="showMenuOptions && selectedBrandIndex === index" class="menu-options">
<!-- 重命名选项 -->
<view class="menu-option" @click.stop="showRenameDialog(index)">
<text class="rename-icon">✏️</text>
<text>重命名</text>
</view>
<!-- 删除品牌选项 -->
<view class="menu-option delete-option" @click.stop="showDeleteBrandConfirm(index)">
<text class="trash-icon">🗑️</text>
<text>删除品牌</text>
</view>
</view>
</view>
</view>
<!-- 二级子品牌列表(展开时显示) -->
<view v-if="brand.expanded" class="level2-container">
<view
v-for="(subBrand, subIndex) in brand.children"
:key="subIndex"
class="level2-item"
draggable="true"
@dragstart="dragStart(index, subIndex)"
@dragover="dragOver"
@drop="drop(index, subIndex)"
>
<text class="level2-name">{{ subBrand.name }}</text>
<view class="edit-btn" @click.stop="openEditDialog(index, subIndex)">
<text class="edit-icon">✎</text>
<text class="edit-text">编辑</text>
</view>
</view>
<!-- 添加子品牌按钮 -->
<view class="add-sub-brand" @click="addSubBrand(index)">
<text class="add-icon">+</text>
<text class="add-text">添加子品牌</text>
</view>
</view>
</view>
</view>
<!-- 底部添加一级品牌按钮 -->
<view class="add-brand-btn" @click="addBrand">
<text class="add-btn-icon">+</text>
<text class="add-btn-text">添加品牌</text>
</view>
<!-- 修改品牌名称弹窗 -->
<view v-if="editDialogVisible" class="dialog-overlay">
<view class="dialog">
<view class="dialog-title">修改品牌名称</view>
<!-- 输入框 -->
<view class="input-wrapper">
<input
v-model="editBrandName"
type="text"
class="dialog-input"
placeholder="请输入品牌名称"
:focus="inputFocus"
@blur="inputFocus = false"
@focus="onInputFocus"
/>
<text v-if="editBrandName" class="clear-icon" @click="clearInput">×</text>
</view>
<!-- 带垃圾桶图标的删除按钮 -->
<view class="delete-btn" @click="showDeleteConfirm">
<text class="trash-icon">🗑️</text>
<text>删除品牌</text>
</view>
<view class="dialog-buttons">
<view class="cancel-btn" @click="closeEditDialog">取消</view>
<view class="confirm-btn" @click="confirmEdit">确定</view>
</view>
</view>
</view>
<!-- 添加品牌弹窗 -->
<view v-if="addBrandDialogVisible" class="dialog-overlay">
<view class="dialog">
<view class="dialog-title">{{ isAddingSubBrand ? '添加子品牌' : '添加品牌' }}</view>
<!-- 输入框 -->
<view class="input-wrapper">
<input
v-model="addBrandName"
type="text"
class="dialog-input"
placeholder="请输入品牌名称"
:focus="inputFocus"
@blur="inputFocus = false"
@focus="onInputFocus"
/>
<text v-if="addBrandName" class="clear-icon" @click="clearAddInput">×</text>
</view>
<view class="dialog-buttons">
<view class="cancel-btn" @click="closeAddBrandDialog">取消</view>
<view class="confirm-btn" @click="confirmAddBrand">确定</view>
</view>
</view>
</view>
<!-- 删除确认弹窗(在修改弹窗之上) -->
<view v-if="deleteConfirmVisible" class="delete-confirm-overlay">
<view class="delete-confirm-dialog">
<view class="delete-dialog-title">确认删除</view>
<view class="delete-dialog-content">确定要删除这个品牌吗?</view>
<view class="delete-dialog-buttons">
<view class="delete-cancel-btn" @click="hideDeleteConfirm">取消</view>
<view class="delete-confirm-btn" @click="deleteBrand"></view>
</view>
</view>
</view>
</view>
</template>
<script>
// 导入品牌相关API
import { getBrandList, addBrand, getBrandTree, deleteBrand, updateBrand } from '@/api/product'
import { getStoreId } from '@/utils/auth'
export default {
data() {
return {
brandList: [],
storeId: null,
editDialogVisible: false,
deleteConfirmVisible: false,
editParentIndex: null,
editSubIndex: null,
editBrandName: '',
inputFocus: false,
dragSource: {
parentIndex: null,
subIndex: null
},
// 添加品牌弹窗相关
addBrandDialogVisible: false,
addBrandName: '',
isAddingSubBrand: false,
currentParentIndex: null,
// 删除品牌相关
showMenuOptions: false,
selectedBrandIndex: null,
isDeletingLevel1Brand: false,
// 重命名弹窗相关
renameDialogVisible: false,
renameBrandName: '',
renameBrandIndex: null,
// 编辑二级品牌相关
editSubBrandId: null,
// 搜索关键字
searchKeyword: ''
}
},
onLoad() {
// 获取storeId
this.storeId = getStoreId()
// 加载品牌列表,初始时不传入搜索关键字
this.loadBrandList()
},
methods: {
goBack() {
uni.navigateBack()
},
// 加载品牌列表
async loadBrandList(brandName = '') {
try {
// 使用新的getBrandTree接口传入storeId作为URL参数和brandName作为查询参数
const res = await getBrandTree(this.storeId, brandName)
if (res.code === 200 && res.data) {
// 格式化并设置品牌列表
this.brandList = this.formatBrandData(res.data)
}
} catch (error) {
console.error('加载品牌列表失败:', error)
uni.showToast({ title: '加载品牌列表失败', icon: 'none' })
}
},
// 格式化品牌数据为树状结构
formatBrandData(brandData) {
// 假设新接口返回的数据结构已经是树状的包含id、brandName、children等字段
// 但需要根据实际返回格式进行调整
return brandData.map(brand => ({
name: brand.brandName,
id: brand.id,
expanded: true,
children: brand.children ? brand.children.map(subBrand => ({
name: subBrand.brandName,
id: subBrand.id
})) : []
}))
},
// 搜索输入处理
onSearchInput() {
const keyword = this.searchKeyword.trim()
// 调用API进行搜索传入搜索关键字
this.loadBrandList(keyword)
},
// 展开/折叠一级品牌
toggleExpand(index) {
this.brandList[index].expanded = !this.brandList[index].expanded
},
// 添加一级品牌
addBrand() {
// 打开添加品牌弹窗
this.isAddingSubBrand = false
this.currentParentIndex = null
this.addBrandName = ''
this.addBrandDialogVisible = true
// 聚焦输入框
this.$nextTick(() => {
setTimeout(() => {
this.inputFocus = true
}, 50)
})
},
// 添加子品牌
addSubBrand(parentIndex) {
// 打开添加子品牌弹窗
this.isAddingSubBrand = true
this.currentParentIndex = parentIndex
this.addBrandName = ''
this.addBrandDialogVisible = true
// 聚焦输入框
this.$nextTick(() => {
setTimeout(() => {
this.inputFocus = true
}, 50)
})
},
// 确认添加或修改品牌
async confirmAddBrand() {
if (!this.addBrandName.trim()) {
uni.showToast({ title: '请输入品牌名称', icon: 'none' })
return
}
try {
// 构造请求参数
const params = {
brandName: this.addBrandName.trim(),
storeId: this.storeId
}
// 如果是添加子品牌添加parentId参数
if (this.isAddingSubBrand) {
params.parentId = this.brandList[this.currentParentIndex].id || 0
} else {
// 添加一级品牌时parentId为0
params.parentId = 0
}
// 调用添加品牌接口
const res = await addBrand(params)
if (res.code === 200) {
// 关闭弹窗
this.closeAddBrandDialog()
// 重新加载品牌列表
await this.loadBrandList()
uni.showToast({
title: '添加成功',
icon: 'success'
})
} else {
uni.showToast({ title: res.msg || '操作失败', icon: 'none' })
}
} catch (error) {
console.error('添加品牌失败:', error)
uni.showToast({ title: '添加失败', icon: 'none' })
}
},
// 关闭添加品牌弹窗
closeAddBrandDialog() {
this.addBrandDialogVisible = false
this.addBrandName = ''
this.inputFocus = false
this.editSubBrandId = null
},
// 清除添加品牌输入框
clearAddInput() {
this.addBrandName = ''
// 聚焦输入框
this.$nextTick(() => {
this.inputFocus = true
})
},
// 打开编辑弹窗
openEditDialog(parentIndex, subIndex) {
this.editParentIndex = parentIndex
this.editSubIndex = subIndex
// 判断是一级品牌还是二级品牌
if (subIndex === undefined) {
// 一级品牌
this.isDeletingLevel1Brand = true
this.editBrandName = this.brandList[parentIndex].name
} else {
// 二级品牌
this.isDeletingLevel1Brand = false
this.editBrandName = this.brandList[parentIndex].children[subIndex].name
}
this.editDialogVisible = true
this.$nextTick(() => {
setTimeout(() => {
this.inputFocus = true
}, 50)
})
},
// 输入框获取焦点
onInputFocus() {
this.inputFocus = true
},
// 关闭编辑弹窗
closeEditDialog() {
this.editDialogVisible = false
this.editParentIndex = null
this.editSubIndex = null
this.editBrandName = ''
this.inputFocus = false
},
// 清除输入框内容
clearInput() {
this.editBrandName = ''
setTimeout(() => {
this.inputFocus = true
}, 50)
},
// 确认修改
async confirmEdit() {
if (this.editBrandName.trim() === '') {
uni.showToast({ title: '请输入品牌名称', icon: 'none' })
return
}
try {
// 构造请求参数
let brandId;
// 判断是修改一级品牌还是二级品牌
if (this.isDeletingLevel1Brand) {
// 修改一级品牌使用一级品牌的id
brandId = this.brandList[this.editParentIndex].id
} else {
// 修改二级品牌使用二级品牌的id
brandId = this.brandList[this.editParentIndex].children[this.editSubIndex].id
}
const params = {
brandName: this.editBrandName.trim(),
id: brandId,
storeId: this.storeId
}
// 调用修改品牌名称API
const res = await updateBrand(params)
if (res.code === 200) {
// 更新本地数据
if (this.isDeletingLevel1Brand) {
// 修改一级品牌名称
this.brandList[this.editParentIndex].name = this.editBrandName.trim()
} else {
// 修改二级品牌名称
this.brandList[this.editParentIndex].children[this.editSubIndex].name = this.editBrandName.trim()
}
// 关闭弹窗
this.closeEditDialog()
uni.showToast({ title: '修改成功', icon: 'success' })
} else {
uni.showToast({ title: res.msg || '修改失败', icon: 'none' })
}
} catch (error) {
console.error('修改品牌名称失败:', error)
uni.showToast({ title: '修改失败', icon: 'none' })
}
},
// 显示删除确认弹窗
showDeleteConfirm() {
this.deleteConfirmVisible = true
},
// 隐藏删除确认弹窗
hideDeleteConfirm() {
this.deleteConfirmVisible = false
},
// 删除品牌
async deleteBrand() {
try {
let brandId;
// 判断是删除一级品牌还是二级品牌
if (this.isDeletingLevel1Brand) {
// 删除一级品牌使用一级品牌的id
brandId = this.brandList[this.editParentIndex].id
} else {
// 删除二级品牌使用二级品牌的id
brandId = this.brandList[this.editParentIndex].children[this.editSubIndex].id
}
// 调用删除品牌API
const res = await deleteBrand(brandId)
if (res.code === 200) {
// 隐藏弹窗
this.hideDeleteConfirm()
this.closeEditDialog()
// 重新加载品牌列表
await this.loadBrandList()
uni.showToast({ title: '删除成功', icon: 'success' })
} else {
uni.showToast({ title: res.msg || '删除失败', icon: 'none' })
}
} catch (error) {
console.error('删除品牌失败:', error)
uni.showToast({ title: '删除失败', icon: 'none' })
}
},
// 切换菜单选项的显示/隐藏
toggleMenuOptions(index) {
if (this.showMenuOptions && this.selectedBrandIndex === index) {
// 如果点击的是当前显示选项的品牌,关闭选项
this.showMenuOptions = false
this.selectedBrandIndex = null
} else {
// 否则显示当前品牌的菜单选项
this.showMenuOptions = true
this.selectedBrandIndex = index
}
},
// 显示重命名弹窗(一级品牌)
showRenameDialog(index) {
// 关闭菜单选项
this.showMenuOptions = false
this.selectedBrandIndex = null
// 调用打开编辑弹窗方法只传parentIndex不传subIndex表示修改一级品牌
this.openEditDialog(index)
},
// 显示删除品牌确认弹窗
showDeleteBrandConfirm(index) {
// 关闭菜单选项
this.showMenuOptions = false
this.selectedBrandIndex = null
// 设置要删除的品牌索引
this.editParentIndex = index
this.editSubIndex = 0
this.isDeletingLevel1Brand = true
// 显示删除确认弹窗
this.showDeleteConfirm()
},
// 拖拽开始
dragStart(parentIndex, subIndex) {
this.dragSource = { parentIndex, subIndex }
},
// 拖拽经过
dragOver(e) {
e.preventDefault()
},
// 拖拽放下
drop(targetParentIndex, targetSubIndex) {
const { parentIndex, subIndex } = this.dragSource
if (parentIndex === targetParentIndex) {
const draggedItem = this.brandList[parentIndex].children.splice(subIndex, 1)[0]
this.brandList[targetParentIndex].children.splice(targetSubIndex, 0, draggedItem)
}
this.dragSource = { parentIndex: null, subIndex: null }
}
}
}
</script>
<style scoped>
.container {
background-color: #f5f5f5;
min-height: 100vh;
display: flex;
flex-direction: column;
}
/* 导航栏 */
.navbar {
background-color: #e62318;
color: #fff;
display: flex;
align-items: center;
justify-content: space-between;
padding: 20rpx 30rpx;
position: relative;
height: 88rpx;
box-sizing: border-box;
}
.title {
font-size: 34rpx;
font-weight: bold;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
.back-icon {
font-size: 32rpx;
}
.menu-icon {
font-size: 28rpx;
letter-spacing: 6rpx;
}
/* 菜单容器 */
.menu-container {
position: relative;
}
/* 菜单选项列表 */
.menu-options {
position: absolute;
top: 100%;
right: 0;
background-color: #fff;
border: 1rpx solid #f0f0f0;
border-radius: 8rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
z-index: 1000;
margin-top: 10rpx;
min-width: 200rpx;
}
/* 菜单项 */
.menu-option {
display: flex;
align-items: center;
gap: 10rpx;
font-size: 28rpx;
padding: 20rpx 30rpx;
cursor: pointer;
white-space: nowrap;
transition: background-color 0.2s;
}
/* 菜单项 hover 效果 */
.menu-option:active {
background-color: #f5f5f5;
}
/* 重命名选项 */
.menu-option:not(.delete-option) {
color: #333;
}
/* 重命名图标 */
.rename-icon {
font-size: 24rpx;
}
/* 删除品牌选项 */
.menu-option.delete-option {
color: #e62318;
border-top: 1rpx solid #f0f0f0;
}
/* 垃圾桶图标 */
.menu-option .trash-icon {
font-size: 24rpx;
}
/* 搜索框 */
.search-box {
background-color: #fff;
padding: 16rpx 30rpx;
}
.search-input {
background-color: #f5f5f5;
border-radius: 6rpx;
padding: 18rpx;
font-size: 28rpx;
width: 100%;
box-sizing: border-box;
color: #333;
height: 72rpx;
}
/* 品牌树 */
.brand-tree {
flex: 1;
padding: 20rpx 30rpx;
}
.level1-item {
background-color: #fff;
border-radius: 8rpx;
margin-bottom: 20rpx;
overflow: hidden;
}
.level1-header {
display: flex;
align-items: center;
padding: 20rpx 30rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.expand-icon {
font-size: 24rpx;
margin-right: 15rpx;
color: #666;
}
.level1-name {
font-size: 28rpx;
flex: 1;
}
.more-btn {
font-size: 24rpx;
color: #999;
letter-spacing: 4rpx;
}
/* 二级子品牌容器 */
.level2-container {
display: flex;
padding: 24rpx 30rpx;
gap: 24rpx;
flex-wrap: wrap;
background-color: #fff;
}
.level2-item {
background-color: #f5f5f5;
border-radius: 8rpx;
width: 180rpx;
height: 180rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
cursor: move;
}
.level2-name {
font-size: 36rpx;
margin-bottom: 8rpx;
}
.edit-btn {
display: flex;
align-items: center;
font-size: 24rpx;
color: #999;
}
.edit-icon {
margin-right: 4rpx;
}
.add-sub-brand {
border: 2rpx dashed #1677ff;
border-radius: 8rpx;
width: 180rpx;
height: 180rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: #1677ff;
}
.add-icon {
font-size: 36rpx;
margin-bottom: 8rpx;
}
.add-text {
font-size: 24rpx;
}
/* 底部添加按钮 */
.add-brand-btn {
background-color: #e62318;
color: #fff;
display: flex;
align-items: center;
justify-content: center;
padding: 24rpx;
font-size: 30rpx;
margin: 0 30rpx 30rpx 30rpx;
border-radius: 8rpx;
}
.add-btn-icon {
margin-right: 8rpx;
}
/* ================ 弹窗样式 ================ */
.dialog-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.dialog {
background-color: #fff;
border-radius: 12rpx;
width: 85%;
max-width: 650rpx;
padding: 40rpx 50rpx;
box-sizing: border-box;
position: relative;
z-index: 1001;
}
.dialog-title {
font-size: 34rpx;
font-weight: 600;
text-align: center;
margin-bottom: 40rpx;
color: #333;
}
/* 输入框样式 */
.input-wrapper {
position: relative;
margin-bottom: 30rpx;
width: 100%;
}
.dialog-input {
border: 1rpx solid #e5e5e5;
border-radius: 8rpx;
padding: 22rpx 60rpx 22rpx 22rpx;
font-size: 30rpx;
width: 100%;
box-sizing: border-box;
outline: none;
background-color: #fff;
pointer-events: auto;
user-select: text;
-webkit-user-select: text;
caret-color: #e62318;
height: 88rpx;
line-height: normal;
}
.dialog-input:focus {
border-color: #e62318;
box-shadow: 0 0 0 2rpx rgba(230, 35, 24, 0.1);
}
.clear-icon {
position: absolute;
right: 20rpx;
top: 50%;
transform: translateY(-50%);
font-size: 28rpx;
color: #999;
width: 30rpx;
height: 30rpx;
text-align: center;
line-height: 30rpx;
border-radius: 50%;
background-color: #f5f5f5;
z-index: 1002;
cursor: pointer;
}
.clear-icon:active {
background-color: #e0e0e0;
}
/* 删除按钮 */
.delete-btn {
color: #e62318;
font-size: 28rpx;
text-align: center;
margin-bottom: 40rpx;
display: flex;
align-items: center;
justify-content: center;
gap: 8rpx;
padding: 16rpx;
border-radius: 8rpx;
/* background-color: #fff5f5; */
cursor: pointer;
}
.delete-btn:active {
background-color: #ffeaea;
}
.trash-icon {
font-size: 24rpx;
}
/* 弹窗按钮 */
.dialog-buttons {
display: flex;
gap: 24rpx;
}
.cancel-btn {
flex: 1;
background-color: #f5f5f5;
color: #666;
text-align: center;
padding: 24rpx;
border-radius: 8rpx;
font-size: 30rpx;
cursor: pointer;
}
.cancel-btn:active {
background-color: #e8e8e8;
}
.confirm-btn {
flex: 1;
background-color: #e62318;
color: #fff;
text-align: center;
padding: 24rpx;
border-radius: 8rpx;
font-size: 30rpx;
cursor: pointer;
}
.confirm-btn:active {
background-color: #d11f15;
}
/* ================ 删除确认弹窗(在修改弹窗之上) ================ */
.delete-confirm-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.7);
display: flex;
align-items: center;
justify-content: center;
z-index: 2000; /* 比修改弹窗更高的层级 */
}
.delete-confirm-dialog {
background-color: #fff;
border-radius: 16rpx;
width: 70%;
max-width: 560rpx;
padding: 40rpx;
box-sizing: border-box;
position: relative;
animation: fadeIn 0.3s ease;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: scale(0.9);
}
to {
opacity: 1;
transform: scale(1);
}
}
.delete-dialog-title {
font-size: 34rpx;
font-weight: 600;
text-align: center;
margin-bottom: 24rpx;
color: #333;
}
.delete-dialog-content {
font-size: 30rpx;
color: #666;
text-align: center;
margin-bottom: 40rpx;
line-height: 1.4;
}
.delete-dialog-buttons {
display: flex;
gap: 24rpx;
}
.delete-cancel-btn {
flex: 1;
background-color: #f5f5f5;
color: #333;
text-align: center;
padding: 24rpx;
border-radius: 8rpx;
font-size: 30rpx;
cursor: pointer;
}
.delete-cancel-btn:active {
background-color: #e8e8e8;
}
.delete-confirm-btn {
flex: 1;
background-color: #e62318;
color: #fff;
text-align: center;
padding: 24rpx;
border-radius: 8rpx;
font-size: 30rpx;
cursor: pointer;
}
.delete-confirm-btn:active {
background-color: #d11f15;
}
</style>