本文代码较多,PC 端访问知识库原文(https://sourl.cn/hBirCb)获得最好阅读体验~
成果展示
实践材料
- Trae IDE(分析文件、代码辅助、文件管理)。点击阅读原文,直达下载链接~
- 微信开发者工具(模拟调试、真机预览)
- Cmder(项目提交、远程管理)
- Sourcetree(本地可视化管理代码,Bug 追查)
本实践需要的语法基础
“ 说明:此项目初衷是能够快速成功部署上线,且本人电气专业,非计算机出身,经切身实践验证,纯小白在项目初期面对微信小程序环境,只需最基本的前端语法基础!相信AI辅助效能!相信自己能力!
前端三大件*(对应微信小程序可以简单理解为换了个名字)*
1..wxml
(定义网页的结构和内容:基础外观)
- 类似 HTML,但使用的是微信小程序自定义的标签和语法。
- 使用了类似于 Vue.js 的模板语法,支持数据绑定、条件渲染、列表渲染等功能。
- 标签是微信小程序特有的,例如
、
、 等,而不是标准的 HTML 标签。
- 项目示例
<view class="t\_card" bindtap="home">
<image src="/images/home.png"></image>
<text>首页</text>
</view>
2..wxss
(控制网页的布局、视觉样式:包装美化)
- 类似于 CSS,但有一些扩展功能。
- 支持大部分 CSS 属性,但也有一些限制(比如某些高级 CSS 特性可能不支持)。
- 增加了尺寸单位 rpx,可以根据屏幕宽度进行自适应布局(750rpx = 屏幕宽度)。
- 支持全局样式和局部样式。
- 项目示例
.avatar {
width: 100rpx;
height: 100rpx;
border-radius: 50%;
margin-right: 16rpx; /* 给头像右边一些空间 */
border: 2px solid #fbbd08;
object-fit: cover; /* 确保图像覆盖整个圆形区域 */
}
.details {
display: flex;
flex-direction: column;
justify-content: space-between;
height: 100%;
margin-right: auto; /* 让联系信息靠右 */
}
3..js
(添加交互性和动态功能:逻辑功能)
- 类似 JavaScript
- 负责处理页面的数据、事件和生命周期。
- 提供了微信小程序特有的 API,比如 wx.request(网络请求)、wx.showToast(弹窗提示)等。
- 数据绑定采用单向数据流(类似于 React 或 Vue 的响应式机制)。
- 页面逻辑通过 Page() 方法定义,包含 data(数据)、onLoad(生命周期函数)、事件处理等。
- 项目示例
/**
* 页面的初始数据
*/
data: {
first\_title: true,
//place: '',
},
onLoad(e) {
this.getuserdetail();
this.data.id = e.scene;
this.getPublish(e.scene);
},
changeTitle(e) {
let that = this;
that.setData({
first\_title: e.currentTarget.dataset.id
})
},
开始实践
项目实践目的
- 项目说明
- 因为追求效率,所以使用开源项目 GitHub开源二手书籍交易平台 进行大 换 血 ,在此非常感谢作者进行分享。总共提交了 34 次,耗时 13378 分钟,从设计到UI再到所有页面逻辑代码皆由本人一人修改测验完成。
- 作为专门为校内服务的二手仓小程序,初衷就是快速进行绿色交换,一个纯粹二手交易发布信息的发布平台,并没有做留言,私信,点赞和线上支付等功能,一是因为学校内部就已经拥有了一个非常棒的小程序,能方便我们学生进行交流娱乐等,而我只是细分了一个二手的功能;二是因为我作为个人,非企业,还没有相关权限,资金也不够支撑;三是因为我个人精力有限,运营安全方面无法顾及。
- 前期准备
- 微信小程序作为一款公众应用不仅仅是代码项目的实现,个人还需要掌握 微信公众平台 注册小程序并进行后期资料的补充;以及通过 微信官方文档 确保代码规范性,防止使用停维接口,确保代码稳健性;当遇到问题时,寻求 微信开放社区 上的经验作为辅助,为了版本迭代维护,建议附加学习使用 git 代码管理相关工具。
- 云开发
- 作为小白,我们只需要完成前端的内容,后端的内容选择微信云开发, 注意一定是在创建项目前就选择“微信云开发”,而非选择“不使用云服务”再后期在项目里面点击“云开发”进行开通 ,否则会出现云开发开通失败的情况!
代码实现过程
跨维认知,踏出壁垒
本人虽为小白,但是面对任何事情都抱有学习的态度,不分专业,不分技术,只要条件足够,自己想做,有骨子干劲,不会就学呗~ 谁不是从 0 开始的?大多数阻碍我们的就是我们自己,收藏夹吃灰的宝藏一直就在那里静静地躺着,一遇到一点难处就放弃,半途而废,不实践永无提升与成长,逼自己一把,保持热忱的心态,终究也能成为大佬!技术落地能力+超导体级执行力——即刻启动!
学会模板,快速搭建
对于新手开发者来说,使用项目模板进行开发是一个不错的选择。这不仅能够帮助快速上手,还能让我们在实践中学习最佳实践和规范,通过使用这些模板,可以避免一些常见的陷阱,并且学习到正确的开发方式。而且,理解一个完整的项目架构对于一个新手可能会感到压力山大,模板提供了一个简化的起点,使得我们可以专注于学习核心概念和技术,利用 Trae 快速搭建,而不是被复杂的配置所困扰。在这个项目之前,我也利用模板利用 AI 制作了个人网站—— SpongeBob 的个人网站,且已经完全部署、成功上线!欢迎访问!
源码理解,Trae 一下
阅读源码、理解源码是项目修改第一步,这部分没有做好,后期工作难以开展。利用 Trae 的 Chat 帮助我进行大致文件的分析,梳理好逻辑,确定好自己的设计需求,综合考量项目制作强度,并添加相应的编译模式。
迭代综述,Trae 赋能
- 设计思路
- 首页: 参考淘宝、闲鱼等交易平台的 UI 设计,并保留原作者的宫格/列表展示功能和切换导航标签功能,拟定列表形式下每个商品 or 需求模块展示图片、详情(截取 20 字)、类别标签、价格、用户昵称、头像;宫格形式下展示图片、类别标签、价格。整体错落有致,信息关键醒目。
<!-- 分类导航 -->
<view class="{{scrollTop>310?'nofixed':''}}"></view>
<view class="kind\_contain {{scrollTop>310?'fixed':''}}">
<view class="nav-item {{-2==collegeCur?'tab-on':''}}" bindtap="selectAll">
<view class="nav-text">全部</view>
</view>
<scroll-view scroll-x class="nav" scroll-with-animation scroll-left="{{scrollLeft}}rpx">
<view class="nav-item" wx:for="{{college}}" wx:key="id" bindtap="collegeSelect" data-id="{{index}}">
<view class="nav-text {{index==collegeCur+1?'tab-on':''}}">{{item.name}}</view>
</view>
</scroll-view>
<view class="kind\_img" bindtap="showlist">
<image lazy-load src="{{showList?'/images/l\_down.png':'/images/l\_right.png'}}" />
</view>
<view class="kindlist\_box" wx:if="{{showList}}">
<view class="kindlist\_card">
<view class="list\_grid">
<block wx:for="{{college}}" wx:key="id">
<view class="list\_one" bindtap="collegeSelect" data-id="{{index}}" data-class="{{item.id}}">
<view class="{{index==collegeCur+1?'list-on':''}}">
{{item.name}}
</view>
</view>
</block>
</view>
</view>
</view>
</view>
<!-- 宫格显示 -->
<view hidden="{{!iscard}}">
<view class="card\_grid" wx:if="{{list.length>0}}">
<block wx:for="{{list}}" wx:key="\_id">
<view class="card\_one" bindtap="detail" data-id="{{item.\_id}}">
<image lazy-load class="card\_poster" mode="aspectFill" src="{{item.images && item.images.length > 0 ? item.images[0] : '/images/moren.png'}}" />
<view class="card\_between">
<view class="card\_title text-cut">{{item.type}}</view>
<view class="card\_author text-cut">{{item.category}}</view>
</view>
<view class="card\_between">
<view class="list\_price">
<text class="price-symbol">¥</text>
{{item.price}}
</view>
<image lazy-load class="card\_buy" src="/images/buy.png"></image>
</view>
</view>
</block>
</view>
</view>
<!-- 列表显示 -->
<view hidden="{{iscard}}">
<block wx:if="{{list.length>0}}">
<block wx:for="{{list}}" wx:key="\_id">
<view class="list\_box" bindtap="detail" data-id="{{item.\_id}}">
<image lazy-load class="list\_poster" mode="aspectFill" src="{{item.images && item.images.length > 0 ? item.images[0] : '/images/moren.png'}}" />
<view class="list\_content">
<view class="list\_title">{{item.details}}</view>
<view class="list\_word">
<view class="list\_info">
<text class="type-text">{{item.type}}</text>
<text class="category-text">{{item.category}}</text>
</view>
</view>
<view class="list\_between">
<view class="list\_price">
<text class="price-symbol">¥</text>
{{item.price}}
</view>
</view>
<view class="user\_info\_container">
<view class="user\_info">
<!-- <image class="avatar" lazy-load src="{{item.gender==0?'/images/girl.png':'/images/boy.png'}}"></image> -->
<image class="avatar" mode="aspectFill" lazy-load src="{{item.touxiang || '/images/avator.png'}}" />
<view class="{{item.\_openid == 'ofJNi7PO93SYI1cTNif3ltnTjJ-I' ? 'admin-nickname' : 'normal-nickname'}}">
{{item.nickname}}
</view>
</view>
<view class="tab">详情</view>
</view>
</view>
</view>
</block>
</block>
</view>
- 发布页: 分步骤一二三根据校园交易信息有效性,决定增加步骤一发布需求 or 发布商品,并提供种类供选择,步骤二相应的信息模版为详情+联系方式+图片+价格形式,填写关键信息,最终信息上传至 publish 集合。
const db = wx.cloud.database();
const app = getApp();
const config = require("../../config.js");
Page({
data: {
systeminfo: app.systeminfo,
entime: {
enter: 600,
leave: 300
}, //进入褪出动画时长
college: JSON.parse(config.data).college.splice(1),
steps: [{
text: '步骤一',
desc: '选择标签'
},
{
text: '步骤二',
desc: '补充具体信息'
},
{
text: '步骤三',
desc: '发布成功'
},
],
categories: [
{ name: '书籍资料', days: 60 , id: -1 },
{ name: '学习用具', days: 60 , id: 0 },
{ name: '数码产品', days: 45 , id: 1 },
{ name: '衣饰化妆', days: 45 , id: 2 },
{ name: '运动器材', days: 45 , id: 3 },
{ name: '寝具用品', days: 45 , id: 4 },
{ name: '委托合作', days: 3 , id: 5 },
{ name: '其他', days: 30 , id: 6 }
],
selectedCategory: '', // 存储用户选择的类别
isProductOrDemand: '', // 存储用户选择的商品或需求
cids: '-1', //学院选择的默认值
dura: 0, // 根据选择自动设定
details: '', // 新增详情信息输入
contactInfo: '', // 新增联系方式输入
images: [], // 存储选择的图片路径
maxImages: 4, // 最大图片数量
price: '', // 默认为空字符串
showPopup: false, // 控制弹出层显示/隐藏
inputFocused: false // 控制输入框是否自动聚焦
},
//恢复初始态
initial() {
let that = this;
that.setData({
price: '',
selectedCategory: '',
isProductOrDemand: '',
cids: '-1', //学院选择的默认值
details: '',
contactInfo: '',
images: [],
show\_a: true, // 初始显示步骤一
show\_b: false, // 初始不显示步骤二
show\_c: false,// 初始不显示步骤三
active: 0 // 初始激活步骤一
})
},
onLoad() {
this.initial();
//this.setData({ price: '0' });
},
// 步骤一新增选择发布类型和类别的方法
chooseType(e) {
const type = e.detail.value;
this.setData({ isProductOrDemand: type });
},
chooseCategory(e) {
const categoryIndex = e.detail.value;
const selectedCategory = this.data.categories[categoryIndex];
this.setData({
cids:selectedCategory.id,
selectedCategory: selectedCategory.name,
dura: selectedCategory.days
});
},
confirm() {
console.log("开始执行 confirm 方法");
//console.log("当前的 openid: ", app.openid);
let that = this;
if (!that.data.isProductOrDemand || !that.data.selectedCategory) {
wx.showToast({
title: '请选择发布类型和类别',
icon: 'none'
});
returnfalse;
}
if (!app.openid) {
wx.showModal({
title: '温馨提示',
content: '该功能需要注册方可使用,是否马上去注册',
success(res) {
if (res.confirm) {
wx.navigateTo({
url: '/pages/login/login',
})
}
}
})
returnfalse
}
//console.log("所有条件已满足,准备继续执行");
// 直接进入下一步骤,模拟已登录状态
that.setData({
show\_a: false,
show\_b: true,
active: 1,
});
},
//步骤二上传逻辑
// 新增的事件处理函数
bindDetailsInput(e) {
this.setData({
details: e.detail.value
});
},
bindContactInput(e) {
this.setData({
contactInfo: e.detail.value
});
},
//图片
chooseImage: function() {
const that = this;
const count = this.data.maxImages - this.data.images.length; // 计算还可以选择多少张图片
wx.chooseImage({
count: count,
sizeType: ['original', 'compressed'],
sourceType: ['album', 'camera'],
success(res) {
const tempFilePaths = res.tempFilePaths;
// 上传每一张选中的图片
Promise.all(tempFilePaths.map(tempFilePath =>
wx.cloud.uploadFile({
cloudPath: `your-folder-name/${Date.now()}-${Math.floor(Math.random(0, 1)*1000)}.png`, // 根据需要设置文件路径和名称
filePath: tempFilePath,
})
)).then(results => {
const fileIds = results.map(result => result.fileID);
that.setData({
images: that.data.images.concat(fileIds), // 将fileID存入images数组
});
}).catch(error => {
console.error('图片上传失败:', error);
});
},
fail() {
// 如果用户取消选择图片,这里可以不做处理
}
});
},
deleteImage: function(e) {
const index = e.currentTarget.dataset.index;
let images = this.data.images;
images.splice(index, 1); // 从数组中移除对应的图片
this.setData({
images: images,
});
},
previewImage: function(e) {
const index = e.currentTarget.dataset.index;
const current = this.data.images[index];
wx.previewImage({
current: current,
urls: this.data.images
});
},
// 显示数字输入框
// 显示数字输入框
showPriceInput() {
this.setData({
showPopup: true,
inputFocused: true // 设置为true以自动聚焦
}, () => {
// 使用定时器延迟执行,确保弹出层已经渲染完成
setTimeout(() => {
const inputComponent = this.selectComponent('#priceInput');
if (inputComponent) {
inputComponent.focus(); // 手动调用focus方法
}
}, 300); // 根据实际情况调整延迟时间
});
},
// 隐藏数字输入框
hidePriceInput() {
this.setData({
showPopup: false,
inputFocused: false // 关闭自动聚焦
});
},
// 当输入价格时触发
onPriceInput(event) {
let value = event.detail.value;
// 如果输入为空,直接设置为空字符串
if (value === '') {
this.setData({ price: '' });
return;
}
// 检查并处理输入值
if (/^\.\d$/.test(value)) { // 如果以单个小数点开头并跟随一个数字,在前面补0
value = '0' + value;
} elseif (value.includes('.')) { // 包含小数点的情况
const [integerPart, decimalPart] = value.split('.');
// 整数部分不能超过4位
let formattedIntegerPart = integerPart;
if (integerPart.length > 6) {
formattedIntegerPart = integerPart.substring(0, 6); // 只保留前4位
}
// 小数部分不能超过1位
let formattedDecimalPart = decimalPart.length > 1 ? decimalPart.substring(0, 1) : decimalPart;
// 合并整数和小数部分
value = formattedIntegerPart + '.' + formattedDecimalPart;
} else { // 不包含小数点的部分
// 整数部分长度限制为4位
if (value.length > 6) {
value = value.substring(0, 6); // 只保留前4位
}
value = parseInt(value, 10).toString(); // 转换为整数后转回字符串
}
// 更新价格数据
this.setData({ price: value }, () => {
// 确保即使输入不合规,界面也反映正确的值
const currentPrice = this.data.price;
if (currentPrice && currentPrice.includes('.')) {
const [integerPart, decimalPart] = currentPrice.split('.');
if (decimalPart.length > 1) {
// 如果小数部分超过了1位,则调整它
this.setData({ price: `${integerPart}.${decimalPart.substring(0, 1)}` });
}
}
});
},
goToPreviousStep: function() {
this.setData({
show\_a: true, // 显示步骤一
show\_b: false, // 隐藏步骤二
active: 0 // 更新步骤条的激活状态为步骤一
});
},
// 发布逻辑
publish() {
let that = this;
// 检查是否已经获取了用户的openid
if (!app.openid) {
wx.showToast({
title: '请先登录',
icon: 'none'
});
return;
}
// 使用openid从user集合中获取用户的昵称
db.collection('user').where({
\_openid: app.openid
}).get({
success: function(res) {
if (res.data.length > 0) { // 确保找到了用户数据
const nickname = res.data[0].Nickname; // 假设字段名为Nickname,请根据实际情况修改
const xinbie = res.data[0].gender.id;
const avatar = res.data[0].info.avatarUrl;
// 继续进行发布的逻辑
if (!that.data.details || that.data.details.length > 210 || !that.data.price || !that.data.contactInfo || that.data.contactInfo.length > 20) {
wx.showToast({
title: '请检查详情、价格联系方式',
icon: 'none'
});
returnfalse;
}
wx.showModal({
title: '温馨提示',
content: '请确保您填写的信息无误,是否马上发布?',
success(res) {
if (res.confirm) {
db.collection('publish').add({
data: {
type: that.data.isProductOrDemand == 'demand' ? '需求':'商品',
category: that.data.selectedCategory,
collegeid: that.data.cids,
details: that.data.details,
contactInfo: that.data.contactInfo,
images: that.data.images,
price: that.data.price,
createTime: db.serverDate(),
nickname: nickname, // 添加用户昵称
openid: app.openid, // 确保openid也被上传
gender: xinbie,
touxiang: avatar,
//campus: that.data.campus,
dura: new Date().getTime() + that.data.dura * (24 * 60 * 60 * 1000),
},
success(e) {
that.setData({
show\_a: false,
show\_b: false,
show\_c: true,
active: 2,
detail\_id: e.\_id
});
wx.pageScrollTo({ scrollTop: 0 });
},
fail(err) {
console.error('发布失败:', err);
wx.showToast({
title: '发布失败,请重试',
icon: 'none'
})
}
})
}
}
});
} else {
wx.showToast({
title: '未找到用户信息,请检查登录状态',
icon: 'none'
});
}
},
fail: function(err) {
console.error('获取用户信息失败:', err);
wx.showToast({
title: '获取用户信息失败,请稍后再试',
icon: 'none'
});
}
});
},
detail() {
let that = this;
wx.navigateTo({
url: '/pages/detail/detail?scene=' + that.data.detail\_id,
})
}
})
- 详情页: 相关信息从云开发中的 publish 集合中获取,并根据当前用户的 openid 从 user 集合中获取用户的头像、昵称;点击用户头像跳转至个人发布页面,查看该用户的发布记录,如果是本用户所发,则相应增加删除键。
<wxs src="../../common.wxs" module="morejs" />
<view class="top\_contain" style="height: 530rpx; display: flex; box-sizing: border-box">
<swiper class="top\_img" indicator-dots="true" autoplay="true" interval="3000" duration="500" style="height: 420rpx; display: flex; box-sizing: border-box; width: 700rpx">
<block wx:for="{{images}}" wx:key="index">
<swiper-item>
<image lazy-load src="{{item}}" mode="aspectFill" bindtap="previewImage" data-index="{{index}}"></image>
</swiper-item>
</block>
</swiper>
<view class="price\_box" style="position: relative; left: -1rpx; top: -6rpx">
<view class="now\_price">¥{{price}}元</view>
<view class="publish\_type">{{publishType}}</view>
<view class="category">{{category}}</view>
</view>
</view>
<view class="blank" style="height: 27rpx; display: block; box-sizing: border-box"></view>
<view class="center\_contain">
<view class="c\_title title\_on">发布详情</view>
</view>
<!-- 发布信息 -->
<view class="user\_box">
<image lazy-load mode="aspectFill" src="{{avatarUrl}}" class="avatar" bindtap="goToAuthorPage"></image>
<view class="details">
<view class="sex">
<image lazy-load src="{{genderId==0?'/images/girl.png':'/images/boy.png'}}"></image>
</view>
<view class="des\_box">
<view class="{{isadmin == 'ol6n36\_xtRPxfRrclkXGnNe-T4OY' ? 'admin-name' : 'normal-name'}}">
{{username}}
</view>
</view>
</view>
<view class="contact">{{contactInfo}}</view>
</view>
<view class="detail\_box">
<view class="detail\_content">{{details}}</view>
</view>
<view class="time\_box">
<view class="time">发布于{{createtime}}</view>
</view>
<view style="height: 96rpx;"></view>
<!-- 底部导航 -->
<view class="tabbar">
<view class="t\_card" bindtap="home">
<image src="/images/home.png"></image>
<text>首页</text>
</view>
<view class="t\_card">
<image src="/images/share.png"></image>
<text>分享</text>
<button class="t\_button" open-type="share"></button>
</view>
<view class="t\_card collect\_box" bindtap="collect">
<view class="collect shadow" style="width: 100%; display: flex; justify-content: center; align-items: center;">
收藏
</view>
</view>
</view>
<!-- 悬浮客服功能 -->
<view class="contact\_box" bindtap="go" data-go="/pages/kefu/kefu" animation="{{animationKefuData}}">
<image src="/images/ww.jpg"></image>
<view>反馈</view>
</view>
- 注册登录页: 因为该小程序的目标用户为校内学生,且最终的交易为用户之间直接加联系方式进行,平台不承担诸如线上支付、跑腿等功能,考虑快捷性和隐私性,注册时不直接获取微信用户的头像、昵称、微信号等,让用户自己填写昵称,性别,并供学院选择,后期也可以上传头像,修改信息等;再次登录无需密码等验证方式,直接采用 openid 进行 user 集合检索匹对。
const db = wx.cloud.database();
const app = getApp();
const config = require("../../config.js");
Page({
/**
* 页面的初始数据
*/
data: {
ids: -1,
xinbie: -1,
Nickname: '',
campus: JSON.parse(config.data).campus,
gender: JSON.parse(config.data).gender,
},
choose: function(e) {
let that = this;
const type = e.currentTarget.dataset.type; // 获取选择器类型
const index = e.detail.value; // 获取选择的索引值
if (type === 'campus') {
// 更新学院选择状态
that.setData({
ids: index
});
} elseif (type === 'gender') {
// 更新性别选择状态
that.setData({
xinbie: index
},() => {
console.log("更新后的xinbie:", that.data.xinbie); // 打印更新后的xinbie
});
}
},
// 用户手动输入昵称的处理函数
handlenameInput(e) {
this.setData({
Nickname: e.detail.value.trim()
});
},
getUserInfo() {
let that = this;
wx.getUserProfile({
desc: '获取用户信息',
success: (res) => {
that.setData({
userInfo: res.userInfo
})
that.check();
},
fail: (err) => {
wx.showToast({
title: '获取用户信息失败',
icon: 'none',
duration: 2000
});
}
})
},
//校检
check() {
let that = this;
let username = that.data.Nickname; // 使用用户手动输入的昵称
if (username == '') {
wx.showToast({
title: '请先填写你的昵称',
icon: 'none',
duration: 2000
});
returnfalse
}
//先检查昵称是否已存在
db.collection('user').where({
Nickname: username
}).get({
success: function(res) {
if (res.data.length > 0) {
wx.showToast({
title: '该昵称已被使用,请更换',
icon: 'none',
duration: 2000
});
returnfalse;
}
//继续执行其他校验
//校检校区
let ids = that.data.ids;
if (ids == -1) {
wx.showToast({
title: '请选择您的学院',
icon: 'none',
duration: 2000
});
returnfalse
}
//校验性别
let xinbie = that.data.xinbie;
if (xinbie == -1) {
wx.showToast({
title: '请选择您的性别',
icon: 'none',
duration: 2000
});
returnfalse
}
//所有校验通过,继续执行提交
wx.showLoading({
title: '正在提交',
});
console.log("xinbie 的值:", that.data.xinbie);
db.collection('user').add({
data: {
campus: that.data.campus[that.data.ids],
gender: that.data.gender[that.data.xinbie],
Nickname: that.data.Nickname,
stamp: new Date().toLocaleString(),
info: Object.assign({}, that.data.userInfo, { nickName: username }), // 修改部分
useful: true,
parse: 0,
},
success: function(res) {
//console.log(res)
db.collection('user').doc(res.\_id).get({
success: function(res) {
app.userinfo = res.data;
app.openid = res.data.\_openid;
// 添加这行代码来保存 openid 到本地存储
wx.setStorageSync('openid', res.data.\_openid);
wx.navigateBack({})
},
})
},
fail() {
wx.hideLoading();
wx.showToast({
title: '注册失败,请重新提交',
icon: 'none',
})
}
})
}
})
},
})
2.制作步骤
“ 在开发过程中,Trae 主要用于快速分析代码逻辑缺陷和性能瓶颈。典型的协作流程为:上传代码片段→接收优化建议→本地调试验证,这一模式显著减少了重复性调试时间。
【以发布页为例】
- 内容及样式: 添加 publish.wxml 文件至对话中,在 Trae 的 Builder 模式下提出自己的迭代需求,一旦修改后,不用着急点击全部接受,而是去微信开发者工具同步打开同样路径的项目进行实时调试预览,直至首页的结构内容元素达到设计需求。而修改 publish.wxss 页面布局样式也是类似操作,但是相对前者需要更多的耐心进行试验。
- 页面交互: 这一步即添加 publish.js 至对话中,依次解决步骤一、二、三的交互逻辑,每完成一个步骤都会感叹Trae的能效!相对于前者交互逻辑修改较为简单,毕竟框架基础搭好了,无非选择在哪个元素添加触发函数。
3.问题解决
“ 作为小白,利用 Trae 辅助编写项目,毕竟自己的编程功底薄弱,唯一的问题也即全部的问题——调试不出来自己想要的效果。
- 最小化复现问题
不要一次性调试整个页面,而是拆解成最小代码块(比如先让一个按钮生效),用 console.log 或调试工具逐行验证数据流。
- 对比学习:Trae + 官方文档
Trae 解释代码 → 再去查 MDN/微信小程序文档进行深入理解。例如:Trae 提示“this.setData 是异步的”,我就去研究官方对数据更新的说明。
- 改一点,测一点
不要一次性改大量代码!每改一个小部分就立刻预览测试,如果报错,先用 Trae 分析,再尝试自己根据错误信息推测原因。
- 心态!心态!心态!调试是一场修行!
一个 bug 卡半天很正常,别急着放弃;每天解决一个小问题,一个月后就能独立写功能;Trae 给的答案,要自己动手改、自己运行验证。
- 记录日志
遇到一个坑,就记录下来(比如:“wx.request 的 success 回调里 this 会变,要用 箭头函数”),下次再遇到类似问题,就能快速解决。
- 总结
不断的提问,预览和调试,遇到错误也还是交给 AI ,让它帮自己进行一个分析,但并非说 AI 是万能的,一个没有任何代码基础的小白,可以说都不知道怎么提问,代码就算给出来了,总得复制过来进行一个调试是吧,基础的一个软件操作界面也是必要的,还有后面出现一些 bug ,无脑听从 AI 解决方案,不去进行一个问题的理解,没有自己的主见,往往是非常被动的!
不会敲代码没关系,但是要学会提问,从问题解析中挖掘相应的知识,进行吸收,当做下一个项目时候,能够得心应手,对整体的一个项目框架和逻辑有初步的认知,否则根本无从下手,都不知道从哪个文件进行修改,对自己的学习应用能力提升也毫无作用,当初次打开这个开源项目的时候,并非上来直接就是开始修改,而是花了两三天,去理解每个文件的作用,小程序的文件特性,比如每个文件夹下这固定4个文件的作用是什么,还有运行环境,云开发等,去看官方文档,或者利用AI帮助理解,做笔记,画思维导图,写出自己的一个设计方案,记录初始每个文件所实现的一个功能,最后呢才开始着手进行一个修改。
总结思考
实现功能
login
用户一次性登录注册,修改个人信息。start
校园二手仓启动页。index
首页列表/宫格展示商品/需求,并可切换导航栏筛选转至相应类别。publish
用户发布:选择标签、分类,填写详情至发布成功。detail
详情展示,可跳转个人发布页。help
客服帮助跳转反馈。search
搜索及历史。my
“我的”个人界面。
体验版发布
存在问题
- 图片加载慢,考虑是自己选择沿用原代码版本语言格式,未顺应最新技术规范。
- 跳转等交互响应流畅度不够,考虑是自己小白水平有限,性能优化知识薄弱。
探索方向
-
在原有的基础上,优化响应加载性能。
-
增添用户留言,私信,点赞,收藏功能。
-
建立健全的发布审核机制。