由于源码过多,然后可能影响阅读主要内容,我就先争对这个功能进行编写,后面会放源码来解释,先看运行效果:

针对这个功能我写一个简洁版大家查看的时候也好理解:

简洁版

1、创建一个包含对象的数组,每个对象都有名字和图片属性:

data() {
  return {
    itemList: [
      { name: '对象1', image: '图片1' },
      { name: '对象2', image: '图片2' },
      { name: '对象3', image: '图片3' }
    ],
    newItemName: '',
    newItemImage: ''
  }
}

2、使用v-for指令在页面中渲染列表,并显示每个对象的名字和图片:因为我定义的是前三个对象是固定的无法删除所以有一个判断index<3,如果想都可以删除的话把这个判断去掉就行了

<template>
  <ul>
    <li v-for="(item, index) in itemList" :key="index">
      <img :src="item.image" alt="">
      <span>{{item.name}}</span>
      <button @click="deleteItem(index)" :disabled="index<3">{{index<3?'':''}}删除</button>
    </li>
  </ul>
  <div>
    <input v-model="newItemName" placeholder="请输入新对象的名字">
    <input v-model="newItemImage" placeholder="请输入新对象的图片">
    <button @click="addItem">添加</button>
  </div>
</template>

3、实现添加对象的方法。当点击添加按钮时,在列表末尾添加一个对象,根据输入的名字和图片和属性进行赋值:

methods: {
  addItem() {
    if (this.newItemName && this.newItemImage) {
      this.itemList.push({ name: this.newItemName, image: this.newItemImage });
      this.newItemName = '';
      this.newItemImage = '';
    }
  }
}

4、实现删除对象方法。点击某个对象对的删除按钮时,移除该对象从列表中:

methods: {
  deleteItem(index) {
    this.itemList.splice(index, 1);
  }
}

5、修改对象名字的方法。当点击某个地下时,将起名字绑定到输入框上,进行修改:

methods: {
  editItem(item) {
    this.newItemName = item.name;
  }
}

源码

源码如下所示:

<template>
	<view class="content">
		<view @click="consoles" class="samsung">
			<image src="/static/Tall-Book-Images/exitright.png" mode="aspectFit" style="width: 50rpx; height: 50rpx; ">
			</image>
		</view>
		<view class="tilte">
			<view class="title-top">分类管理</view>
			<view class="title-bottom">长按拖动分类可排序,轻触你添加的分类可编辑。</view>
		</view>
		<view class="Classify">
			<view :class="[showCode ? 'Expenditurelv':'Expenditure']" @click="expendituer()">支出</view>
			<view :class="[showCodeR ? 'ExpenditureH':'Expenditure']" @click="ExpenditureH()">入账</view>
			<view :class="[showCodeL ? 'ExpenditureL': 'Expenditure']" @click="ExpenditureL()">不计入收支</view>
		</view>
		<view class="box">
			<view v-for="(item,index) in classify" :key="index">
				<view class="box-mini" v-if="index<= 19">
					<view class="img-box">
						<image :src="item.imgs" mode="aspectFit" style="width: 50rpx; height: 50rpx;">
						</image>
					</view>
					<view style="font-size: 28rpx;">{{item.name}}</view>
				</view>

				<view v-else class="box-mini" @click="deletes(index)">
					<view class="img-box">
						<image :src="item.imgs" mode="aspectFit" style="width: 50rpx; height: 50rpx;">
						</image>
					</view>
					<view style="font-size: 28rpx;">{{item.name}}</view>
					<view style="color: #cccccc;">轻触编辑</view>
				</view>

			</view>

			<view class="box-mini" :style="styleactive" @click="last"> <!-- 添加-->
				<view class="img-boxone">
					<image src="/static/Tall-Book-Images/tianjia.png" mode="aspectFit"
						style="width: 80rpx; height: 80rpx;">
					</image>
				</view>
				<view style="font-size: 28rpx;">添加</view>
			</view>
		</view>

		<u-popup :show="showaddition" mode="bottom" @close="close" @open="openss" round="10">
			<view style="width: 100%; height: 650rpx; ">
				<view class="top">
					<view class="window" @click="close()">
						<image src="/static/Tall-Book-Images/exitright.png" mode="aspectFit"
							style="width: 40rpx; height: 40rpx;"></image>
					</view>
					<view
						style=" font-size: 20px; text-align: center; line-height: 80rpx; width: 220rpx; height: 80rpx; position: absolute;top: 0; left: 0; right: 0; bottom: 0; margin: auto;">
						添加分类
					</view>
				</view>
				<view class="center">
					<uni-easyinputs v-model="value" maxlength="4" @input="input"
						placeholder="不能与已有类型名重复"></uni-easyinputs>
					<view class="hint" :style="{color: isimit ? '#c90003' : '#c9c9c9'}">
						{{showCharacterCount}}
					</view>
				</view>
				<view class="bottom">
					<button class="comfors" :style="active" @click="comfors">确定</button>
				</view>
			</view>
		</u-popup>

		<u-popup :show="showdelete" mode="bottom" @close="close" round="10"> <!--轻触编辑窗口-->
			<view class="delete-box">
				<view class="delete" @click="objectdelete">删除</view>
				<view class="modification" @click="modification">修改</view>
				<view class="cancel" @click="cancel">取消</view>
			</view>
		</u-popup>

		<u-popup :show="ShowdeleteEmphasize" mode="bottom" @close="close" round="10"><!--强调是否确认窗口-->
			<view class="delete-box">
				<view class="delete-text">删除后。“私消”分类下的内容将归类为“其他”分类</view>
				<view class="delete-Emphasize" @click="Emphasize">删除</view>
				<view class="cancel" @click="cancel">取消</view>
			</view>
		</u-popup>
	</view>
</template>

<script>
	export default {
		data() {
			return {
				styleactive: { //添加
					background: '',
				},
				active: {
					background: ' #f2f2f2',
					color: '#d4d4d4'
				},
				value: '', //输入框的值
				isimit: false, //是否超过了最大限制标志位
				activeindex: -1,
				showaddition: false, //点开添加
				showdelete: false, //删除弹窗
				showCode: true, //支出分类
				showCodeR: false, //入账分类
				showCodeL: false, //不计入收支
				deleteobj: -1, //删除索引用来确定删除操作的下标将index赋值给它
				ShowdeleteEmphasize: false, //强调是否删除窗口
				transfer:false,//判断是不是从删除弹窗中修改页面传送到添加弹窗修改名字的
				classify: [{
						name: '餐饮',
						imgs: '/static/Tall-Book-Images/cy1.png'
					},
					{
						name: '交通',
						imgs: '/static/Tall-Book-Images/jt1.png'
					},
					{
						name: '服饰',
						imgs: '/static/Tall-Book-Images/yf1.png'
					},
					{
						name: '购物',
						imgs: '/static/Tall-Book-Images/gw1.png'
					},
					{
						name: '服务',
						imgs: '/static/Tall-Book-Images/sd1.png'
					},
					{
						name: '教育',
						imgs: '/static/Tall-Book-Images/jy1.png'
					},
					{
						name: '娱乐',
						imgs: '/static/Tall-Book-Images/ht1.png'
					},
					{
						name: '运动',
						imgs: '/static/Tall-Book-Images/lq1.png'
					},
					{
						name: '生活缴费',
						imgs: '/static/Tall-Book-Images/fz1.png'
					},
					{
						name: '旅行',
						imgs: '/static/Tall-Book-Images/lx1.png'
					},
					{
						name: '宠物',
						imgs: '/static/Tall-Book-Images/cw1.png'
					},
					{
						name: '医疗',
						imgs: '/static/Tall-Book-Images/yl1.png'
					},
					{
						name: '保险',
						imgs: '/static/Tall-Book-Images/bx1s.png'
					},
					{
						name: '公益',
						imgs: '/static/Tall-Book-Images/xin1.png'
					},
					{
						name: '发红包',
						imgs: '/static/Tall-Book-Images/hb1.png'
					},
					{
						name: '转账',
						imgs: '/static/Tall-Book-Images/zz1.png'
					},
					{
						name: '亲属卡',
						imgs: '/static/Tall-Book-Images/yhk1.png'
					},
					{
						name: '其他人情',
						imgs: '/static/Tall-Book-Images/qtrq1.png'
					},
					{
						name: '其他',
						imgs: '/static/Tall-Book-Images/qt1.png'
					},
					{
						name: '退还',
						imgs: '/static/Tall-Book-Images/thsh1.png'
					}
				]

			}
		},
		computed: {
			showCharacterCount() { //添加中备注字数限制
				const count = this.value.length;
				this.isimit = count > 3
				return `${count}/${4}`
			}
		},
		methods: {
			input(e) {
				console.log('输入内容:', e);
				// console.log('this.index.lenth', this.value.length)
				this.value = e
				if (this.value) {
					this.active.background = '#3eb575'
					this.active.color = '#ffffff'
				} else {
					this.active.background = '#f2f2f2'
					this.active.color = '#d4d4d4'
				}
			},
			consoles() {
				console.log('consoles')
				this.$emit('close')
			},
			expendituer() { //点击支出
				this.showCode = true;
				this.showCodeL = false;
				this.showCodeR = false;
			},
			ExpenditureH() { //点击入账
				this.showCode = false;
				this.showCodeR = true;
				this.showCodeL = false;
			},
			ExpenditureL() { //点击不计入收支
				this.showCode = false;
				this.showCodeR = false;
				this.showCodeL = true;
			},
			close() {
				this.showaddition = false //关闭添加分类弹窗
				this.showdelete = false //关闭删除弹窗
				this.ShowdeleteEmphasize = false//关闭强调删除窗口
			},
			openss() {
				//弹出层退出
			},
			last() { //添加功能
				this.styleactive.background = "#dfdfdf"
				this.showaddition = true //打开弹窗
				setTimeout(() => {
					this.styleactive.background = "#ffffff" //样式改变后立马还原
				}, 150)
			},
			comfors() {
				if(this.transfer){
					if (this.value) {
					this.active.background = "#33955f"
					setTimeout(() => {
						this.active.background = "#3eb575"
					}, 150)
					this.classify[this.deleteobj].name = this.value//将新改的名字传入新增的对象
					this.showaddition = false//关闭添加弹窗
					this.transfer = false//关闭判断
					
				}
				
				}else {
					if (this.value) {
					this.active.background = "#33955f"
					setTimeout(() => {
						this.active.background = "#3eb575"
					}, 150)
					const newItem = {
						name: this.value,
						class: 'Clicre', //背景绿色样式
						img: '/static/Tall-Book-Images/hb.png',
						imgs: '/static/Tall-Book-Images/hb1.png',
					}
					this.showaddition = false
					this.classify.push(newItem)
					console.log('classify', this.classify)
					this.$emit('updateList', newItem)
				}
			}			
			},
			deletes(index) { //删除功能
				this.deleteobj = index
				this.showdelete = true
				// console.log('deleteobj', this.deleteobj)
			},
			objectdelete() {//确认删除?
				this.showdelete = false //关闭删除弹窗
				this.ShowdeleteEmphasize = true //开启强调弹窗
			},
			Emphasize(){//在次强调
				if (this.deleteobj >= 20) {
					this.classify.splice(this.deleteobj, 1) //删除操作
				}
				this.ShowdeleteEmphasize = false//关闭强调弹窗
			},
			cancel(){//关闭删除弹窗
				this.showdelete = false
				this.ShowdeleteEmphasize = false
			},
			modification(){//修改内容
				this.showdelete = false//关闭删除弹窗
				this.showaddition = true//开启添加弹窗
				this.transfer = true
			}
		}
	}
</script>

<style lang="scss" scoped>
	.Classify {
		width: 100%;
		display: flex;
		height: 120rpx;
		margin-top: 10rpx;
		padding-left: 30rpx;
		padding-right: 30rpx;
		align-items: center;
		margin-bottom: -12rpx;
		justify-content: flex-start;

	}

	.content {
		position: relative;
		width: 100%;
		height: 100%;
	}

	.samsung {
		position: absolute;
		width: auto;
		height: auto;
		top: 40rpx;
		left: 30rpx;
	}

	.tilte {
		display: flex;
		flex-flow: column wrap;
		justify-content: center;
		align-items: center;
		white-space: pre-wrap; //换行保留空格
		width: 90%;
		height: 350rpx;
		margin: 100rpx auto 0;
		// background-color: #c9c9c9;
	}

	.title-top {
		font-size: 42rpx;
		font-weight: bold;
		margin-bottom: 40rpx;
	}

	.title-bottom {
		font-size: 34rpx;
		text-align: center;
		letter-spacing: 0.1em;
		line-height: 1.5em;
	}

	.Expenditurelv {
		width: auto;
		height: 60rpx;
		color: #3eb575;
		font-size: 32rpx;
		text-align: center;
		line-height: 60rpx;
		margin-left: 20rpx;
		border-radius: 10rpx;
		background-color: #ebf7f1;
		padding: 0rpx 20rpx 0rpx 20rpx;

	}

	.ExpenditureH {
		width: auto;
		height: 60rpx;
		color: #f5c53a;
		font-size: 32rpx;
		text-align: center;
		line-height: 60rpx;
		margin-left: 20rpx;
		border-radius: 10rpx;
		background-color: #fdf8eb;
		padding: 0rpx 20rpx 0rpx 20rpx;


	}

	.ExpenditureL {
		width: auto;
		height: 60rpx;
		color: #8c8bc3;
		font-size: 32rpx;
		text-align: center;
		line-height: 60rpx;
		margin-left: 20rpx;
		border-radius: 10rpx;
		background-color: #f1f3f6;
		padding: 0rpx 20rpx 0rpx 20rpx;

	}

	.Expenditure {
		width: auto;
		height: 60rpx;
		color: #7d7d7d;
		font-size: 32rpx;
		text-align: center;
		line-height: 60rpx;
		margin-left: 20rpx;
		border-radius: 10rpx;
		background-color: #fafafa;
		padding: 0rpx 20rpx 0rpx 20rpx;

	}

	.box {
		width: 100%;
		height: 500px;
		margin-top: 50rpx;
		display: flex;
		flex-wrap: wrap;
		overflow-y: auto;
	}

	.box-mini {
		display: flex;
		width: 120rpx;
		height: 172rpx;
		align-items: center;
		flex-flow: column wrap;
		justify-content: space-evenly;
		margin: 0 0rpx 20rpx 52rpx;
	}

	.activebox {
		background-color: #d4d4d4;
	}

	.img-box {
		width: 80rpx;
		height: 80rpx;
		display: flex;
		background: #3eb575;
		border-radius: 50%;
		justify-content: center;
		align-items: center;
	}

	.img-boxone {
		width: 80rpx;
		height: 80rpx;
		display: flex;
		border-radius: 50%;
		justify-content: center;
		align-items: center;
	}

	.top {
		position: relative;
		width: 100%;
		height: 30%;
	}

	.center {
		width: 100%;
		height: 30%;
		display: flex;
		padding: 0 30rpx 0 30rpx;
		justify-content: center;
		align-items: center;
	}

	.hint {
		position: absolute;
		display: flex;
		justify-content: center;
		align-items: center;
		right: 35px;
		width: 80rpx;
		height: 80rpx;
	}

	.bottom {
		width: 100%;
		height: 40%;
		position: relative;
	}

	.window {
		width: 100rpx;
		height: 100%;
		display: flex;
		justify-content: center;
		align-items: center;
		position: absolute;
	}

	.comfors {
		left: 0;
		top: 0;
		right: 0;
		bottom: 0;
		margin: auto;
		position: absolute;
		width: 380rpx;
		height: 100rpx;
		border-radius: 10rpx;
		background-color: #f2f2f2;
		display: flex;
		justify-content: center;
		align-items: center;
		color: #d4d4d4;
	}

	.delete-box {
		width: 100%;
		display: flex;
		height: 400rpx;
		border-radius: 20px;
		flex-flow: column wrap;
		background-color: #fafafa;
	}
	
	.delete-text{
		width: 100%;
		height: 35%;
		display: flex;
		color: #c7c7c2;
		font-size: 28rpx;
		align-items: center;
		justify-content: center;
		border-bottom: 1rpx solid #eaeaea;
	}

	.delete {
		width: 100%;
		height: 38%;
		display: flex;
		color: #fa519a;
		font-size: 36rpx;
		align-items: center;
		justify-content: center;
		border-bottom: 1rpx solid #dddddd;
	}
	
	.delete-Emphasize{
		width: 100%;
		height: 35%;
		display: flex;
		color: #fa519a;
		font-size: 36rpx;
		align-items: center;
		justify-content: center;
		border-bottom: 20rpx solid #eaeaea;
	}

	.modification {
		width: 100%;
		height: 35%;
		display: flex;
		font-size: 36rpx;
		align-items: center;
		justify-content: center;
		border-bottom: 20rpx solid #eaeaea;
	}

	.cancel {
		width: 100%;
		height: 25%;
		display: flex;
		font-size: 36rpx;
		align-items: center;
		justify-content: center;
	}
</style>

插入操作:

1、因为我的设置是前20个不能更改后面添加的才能进行增加删除修改属性,所以列表渲染这里用来if-else结构else后的就是新添加的对象

2、点击可添加数组中新对象

3、打开添加对象弹窗:这里使用的是uview组件uView 2.0 - 全面兼容 nvue 的 uni-app 生态框架 - uni-app UI 框架

写好需要输入的内容点击确定触发点击事件comfors:

transfer是用来判断该对象是不是更改名字的,当点击修改按钮的时候transfer=true这个时候confors只修改对象的名字不新增对象并且将transfer的值变为false,如果为false则新增数组的对象,transfer默认为false:

把新对象的名字加入后放入新定义的对象newItem中

关闭新增弹窗将新对象newItem通过push放入数组classify中,这样就完成了数组的插入。

删除操作:

1、定义一个值deleteobj用于获取数组中每个对象的下标index:

data:

methods:

通过splice找到对应需要删除的对象的位置this.deleteobj从而删除该对象。

弹窗:

修改操作:

通过修改点击事件modification进入添加弹窗界面

此时关闭删除弹窗打开添加弹窗并打开transfer=ture让添加弹窗知道这次这是修改对象的属性而不是添加对象:

从而完成修改操作。最后在发一下运行效果

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐