基于Node+Vue.js高质量商城系统的开发

5/26/2023

程序员根本不会有35岁危机!!!

周末,我没有出去,又接了个单子,一个网友让我用Node+vue做一个商城系统。

其实web开发,好多语言的后台逻辑处理都是千篇一律,学好一门语言可以横扫其他语言。所以学好啥语言都有前途。

这一个单子就是我原来一个月的工资!

程序行业是一个备受争议的行业:

高薪、996、秃顶、格子衫……

这都是外界给程序员的帽子。

我是一名多年开发的程序员,从本科到硕士一直重试计算机相关的工作,基本也是见证了这个行业的起起落落。

其实外界的声音不重要的,关键是做自己吧!今年和同行聊一下:

35岁有没有职业危机!

从我的职场经验来看,35岁没有危机,反而也是越来越吃香,薪资越来越高!

那什么样的程序员容易淘汰???

停滞不前,一直不学习,35岁的时候还是一顿CRUD(增删改查)!你说不辞退你辞退谁?

当然到了35岁,在学习能力和加班方面,我们比不上应届生。

但是你可以在技术、管理方面建立优势,当然也可以开发兼职,兼职可以做哪些??

咨询、接私活、自媒体等。

这些随便做起来一个都可以超过你上班的工资。我曾经做去企业做过一次咨询,给了五位数,Tob是真的香!上次和我对象的一个朋友吃饭,他是一个本科生,一直做NLP,上周去大学代课,一上午给了4500。

所以35岁还年轻,于程序员来说,有太多的转型的方向,我觉得程序员35岁失业就是P话!!!

下面看一下开发的这个系统。

# 一,系统界面

1

2

3

4

5

6

7

8

9

10

11

12

13

# 二,系统核心代码展示

// 小孟v:jishulearn
<template>
	<view class="cart">
		<!--<view class="header" :style="{ position: headerPosition, top: headerTop }">-->
			<!--<view class="title">购物车</view>-->
		<!--</view>-->
		<!--&lt;!&ndash; 占位 &ndash;&gt;-->
		<!--<view class="place"></view>-->
		<!-- 购物车为空 -->
		<view v-if="cartList.length === 0" class="empty">
			<text class="iconfont icongouwuche" :class="'text-'+themeColor.name"></text>
			<view v-if="hasLogin" class="empty-tips">
				空空如也
				<navigator
					class="navigator"
					:class="'text-'+themeColor.name"
					v-if="hasLogin"
					url="../category/category"
					open-type="switchTab"
					>随便逛逛></navigator
				>
			</view>
			<view v-else class="empty-tips">
				空空如也
				<view class="navigator" :class="'text-'+themeColor.name" @tap="navTo('/pages/public/logintype')"
					>登录/注册 ></view
				>
			</view>
		</view>
		<!-- 购物车列表 -->
		<view class="goods-list" v-else>
			<view class="rf-cart-row" v-for="(row, index) in cartList" :key="index">
				<!-- 删除按钮 -->
				<view class="menu" @tap.stop="deleteCartItem(row.id, 'one')"  :class="'bg-'+themeColor.name">
					<text class="iconfont icon iconiconfontshanchu1"></text>
				</view>
				<!-- 商品 -->
				<view
					class="carrier"
					:class="[
						theIndex === index ? 'open' : oldIndex === index ? 'close' : ''
					]"
					@touchstart="touchStart(index, $event)"
					@touchmove="touchMove(index, $event)"
					@touchend="touchEnd(index, $event)"
				>
					<!-- checkbox -->
					<view class="checkbox-box" @tap="selected(index, row)">
						<view
							class="checkbox"
							:class="[
								parseInt(row.status, 10) === 0 ? `checkbox-disabled ${'text-'+themeColor.name}` : 'text-'+themeColor.name
							]"
						>
							<view :class="[row.selected ? `on ${'bg-'+themeColor.name}` : '']"></view>
						</view>
					</view>
					<!-- 商品信息 -->
					<view class="goods-info">
						<view class="img">
							<image :src="row.productPic"></image>
						</view>
						<view class="info">
							<view
								class="title in2line"
								@tap="navTo(`/pages/product/product?id=${row.product.id}`)"
							>
								{{ row.productName }}
							</view>
							<view class="state-wrapper">
								<view class="spec" @tap.stop="toggleSpec(row)">{{ row.productAttr|attrFilter}}</view>
								<view class="state" v-if="parseInt(row.status, 10) === 0">
									已失效
								</view>
							</view>
							<view class="price-number">
								<view class="price">{{ moneySymbol }}{{
									row.price
								}}</view>
								<view class="remark" >{{ row.remark }}</view>
								<view class="number" >
									<view class="sub" @tap.stop="sub(row, index)">
										<text class="iconfont icon icon-jianhao"></text>
									</view>
									<view class="input" @tap.stop="discard">
										<input
											type="number"
											:class="'text-'+themeColor.name"
											v-model="row.quantity"
											@input.stop="numberChange(row, $event, index)"
										/>
									</view>
									<view class="add" @tap.stop="add(row, index)">
										<text class="iconfont icon iconjia1"></text>
									</view>
								</view>
							</view>
						</view>
					</view>
				</view>
			</view>
		</view>
		<!-- 脚部菜单 -->
		<view
			class="footer"
			:style="{ bottom: footerbottom }"
			v-if="cartList.length !== 0"
		>
			<view class="checkbox-box" @tap="allSelect">
				<view class="checkbox" :class="'text-'+themeColor.name">
					<view :class="[isAllselected ? `on ${'bg-'+themeColor.name}` : '']"></view>
				</view>
				<view class="text">全选</view>
			</view>
			<view
				class="delBtn"
				:class="'text-'+themeColor.name"
				@tap="deleteCartItem"
				v-if="selectedList.length > 0"
				>删除</view
			>
			<view class="settlement">
				<view class="sum"
					>合计:
					<view class="money">{{ moneySymbol }}{{ sumPrice }}</view>
				</view>
				<view class="btn" :class="'bg-'+themeColor.name" @tap="createOrder"
					>结算({{ selectedList.length }})</view
				>
			</view>
		</view>
		<!-- 规格-模态层弹窗 -->
		<view
			class="popup spec show"
			v-if="specClass === 'show'"
			@touchmove.stop.prevent="stopPrevent"
			@tap="hideSpec"
		>
			<!-- 遮罩层 -->
			<view class="mask" @tap="hideSpec"></view>
			<view class="layer" @tap.stop="stopPrevent">
				<rf-attr-content
					:isSelectedNum="false"
					:product="productDetail"
					@toggle="toggleSpec"
					></rf-attr-content>
			</view>
		</view>
		<!--页面加载动画-->
		<rfLoading isFullScreen :active="loading"></rfLoading>
	</view>
</template>
<script>
import {
	cartItemClear,
	cartItemDel,
	cartItemList,
	cartItemUpdateNum,
	cartItemUpdateSku,
	productDetail
} from '@/api/product';
import rfAttrContent from '@/components/rf-attr-content';
import { mapMutations } from 'vuex';
export default {
  components: { rfAttrContent },
	filters:{
		attrFilter(val){
			if(!val) return ;
			val = JSON.parse(val);
			let res = [];
			val.map(v=>{
				res.push(v.value);
			})
			return res.join(',');
		}
	},
	data() {
		return {
			sumPrice: '0.00',
			headerPosition: 'fixed',
			headerTop: null,
			statusTop: null,
			showHeader: true,
			selectedList: [],
			isAllselected: false,
			// 控制滑动效果
			theIndex: null,
			oldIndex: null,
			isStop: false,
			cartList: [],
			hasLogin: null,
			footerbottom: 0,
			specClass: 'none',
			productDetail: {
        base_attribute_format: [],
        sku: []
			},
			specSelected: [],
			specChildList: [],
			specList: [],
			currentSkuId: '',
			moneySymbol: this.moneySymbol,
			loading: true,
			singleSkuText: this.singleSkuText
		};
	},
	onPageScroll(e) {
		// 兼容iOS端下拉时顶部漂移
		this.headerPosition = e.scrollTop >= 0 ? 'fixed' : 'absolute';
		this.headerTop = e.scrollTop >= 0 ? null : 0;
		this.statusTop = e.scrollTop >= 0 ? null : -this.statusHeight + 'px';
	},
	// 下拉刷新,需要自己在page.json文件中配置开启页面下拉刷新 "enablePullDownRefresh": true
	onPullDownRefresh() {
		this.selectedList.length = 0;
		this.isAllselected = false;
		this.sumPrice = 0;
		this.getCartItemList('refresh');
	},
	onShow() {
		// 兼容H5下结算条位置
		// #ifdef H5
		this.footerbottom =
			document.getElementsByTagName('uni-tabbar')[0].offsetHeight + 'px';
		// #endif
		// #ifdef APP-PLUS
		this.showHeader = false;
		// eslint-disable-next-line
		this.statusHeight = plus.navigator.getStatusbarHeight();
		// #endif
		this.initData();
	},
	methods: {
		...mapMutations(['setCartNum']),

		// 规格弹窗开关
		toggleSpec(row) {
			if (parseInt(row.status, 10) === 0) return;
			if (this.specClass === 'show') {
				if (!this.hasLogin) {
					this.specClass = 'none';
					this.$mHelper.toast('请先登录!');
					return;
				}
				this.handleCartItemUpdateSku(this.currentSkuId, row.skuId);
				this.specClass = 'hide';
				setTimeout(() => {
					this.specClass = 'none';
				}, 250);
			} else if (this.specClass === 'none') {
				if (row) {
					this.getProductDetail(row);
				}
			}
		},
		// 获取产品详情
		async getProductDetail(row) {
			this.currentSkuId = row.sku_id;
			await this.$http
				.get(`${productDetail}`, {
					id: row.product_id
				})
				.then(async r => {
					this.productDetail = r.data;
					this.productDetail.sku_name = row.sku_name;
					this.specClass = 'show';
				});
		},
		hideSpec() {
			this.specClass = 'hide';
			setTimeout(() => {
				this.specClass = 'none';
			}, 250);
		},
		stopPrevent() {},
		// 删除选中购物车商品
		async deleteCartItem(id, type) {
			let ids = [];
			for (let i = 0; i < this.cartList.length; i++) {
				if (this.cartList[i].selected) {
					ids.push(parseInt(this.cartList[i].id, 10));
				}
			}
			await this.$http2.post("cart/delete",{ids},{
				header:{
					'content-type':'application/x-www-form-urlencoded'
				}
			});
			this.selectedList.length = 0;
			this.isAllselected = false;
			this.sumPrice = 0;
			this.getCartItemList();
			this.oldIndex = null;
			this.theIndex = null;
			// await this.$http
			// 	.post(`${cartItemDel}`, {
			// 		sku_ids: JSON.stringify(skuIds)
			// 	})
			// 	.then(() => {
			// 		this.selectedList.length = 0;
			// 		this.isAllselected = false;
			// 		this.sumPrice = 0;
			// 		this.getCartItemList();
			// 		this.oldIndex = null;
			// 		this.theIndex = null;
			// 	});
		},
		// 修改购物车商品sku
		async handleCartItemUpdateSku(skuId, newSkuId) {
			await this.$http
				.post(`${cartItemUpdateSku}`, {
					sku_id: skuId,
					new_sku_id: newSkuId
				})
				.then(() => {
					this.selectedList.length = 0;
					this.isAllselected = false;
					this.sumPrice = 0;
					this.getCartItemList();
				});
		},
		// 数据初始化
		initData() {
			this.hasLogin = this.$mStore.getters.hasLogin;
			if (this.hasLogin) {
				this.theIndex = null;
				this.oldIndex = null;
				this.selectedList.length = 0;
				this.isAllselected = false;
				this.sumPrice = 0;
				this.getCartItemList();
			} else {
				this.cartList = [];
				this.selectedList.length = 0;
				this.loading = false;
			}
		},
		// 通用跳转
		navTo(route) {
			if (!this.$mStore.getters.hasLogin) {
				uni.setStorageSync('backToPage', JSON.stringify({ route: '/pages/cart/cart' }));
			}
			this.$mRouter.push({ route });
		},
		// 获取购物车列表
		async getCartItemList(type) {
			let r = await this.$http2.get('cart/list').catch(() => {
				this.cartList = [];
				this.loading = false;
				if (type === 'refresh') {
					uni.stopPullDownRefresh();
				}
			});
			this.loading = false;
			this.cartList = r.data;
			this.$set(this,'cartList',[...r.data]);
		// 清空购物车
		clearCart(params) {
			const content = params ? '清空失效商品?' : '清空购物车?';
			uni.showModal({
				content,
				success: async e => {
					if (e.confirm) {
						await this.$http.post(`${cartItemClear}`, params).then(() => {
							this.selectedList.length = 0;
							this.isAllselected = false;
							this.sumPrice = 0;
							this.getCartItemList();
						});
					}
				}
			});
		},
		// 控制左滑删除效果
		touchStart(index, event) {
			// 多点触控不触发
			if (event.touches.length > 1) {
				this.isStop = true;
				return;
			}
			this.oldIndex = this.theIndex;
			this.theIndex = null;
			// 初始坐标
			this.initXY = [event.touches[0].pageX, event.touches[0].pageY];
		},

# 三,欢迎关注我

如果你想学习更多的技术,关注下方公众号,回复:项目大全,干货不断!

spring公众号