This commit is contained in:
LingandRX 2025-04-21 22:59:44 +08:00
commit 087dacbea7
23 changed files with 877 additions and 0 deletions

View File

@ -0,0 +1 @@
{"containers":[],"config":{}}

57
app.js Normal file
View File

@ -0,0 +1,57 @@
App({
globalData: {
userInfo: null,
items: []
},
onLaunch: function() {
// 获取本地存储的物品数据
try {
const items = wx.getStorageSync('items') || []
this.globalData.items = items
} catch (e) {
console.error('获取本地存储失败:', e)
}
// 调试信息
if (wx.getSystemInfoSync) {
const systemInfo = wx.getSystemInfoSync()
console.log('系统信息:', systemInfo)
}
// 检查更新
if (wx.canIUse('getUpdateManager')) {
const updateManager = wx.getUpdateManager()
updateManager.onCheckForUpdate(function(res) {
if (res.hasUpdate) {
updateManager.onUpdateReady(function() {
wx.showModal({
title: '更新提示',
content: '新版本已经准备好,是否重启应用?',
success: function(res) {
if (res.confirm) {
updateManager.applyUpdate()
}
}
})
})
updateManager.onUpdateFailed(function() {
wx.showModal({
title: '更新提示',
content: '新版本下载失败,请检查网络设置',
showCancel: false
})
})
}
})
}
},
onError: function(err) {
console.error('小程序发生错误:', err)
},
onUnhandledRejection: function(err) {
console.error('未处理的 Promise 错误:', err)
}
})

16
app.json Normal file
View File

@ -0,0 +1,16 @@
{
"pages": [
"pages/index/index",
"pages/items/items",
"pages/add/add",
"pages/detail/detail"
],
"window": {
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "个人物品管理",
"navigationBarTextStyle": "black"
},
"style": "v2",
"sitemapLocation": "sitemap.json"
}

33
app.wxss Normal file
View File

@ -0,0 +1,33 @@
page {
background-color: #f8f8f8;
font-size: 28rpx;
color: #333;
font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', Helvetica,
Segoe UI, Arial, Roboto, 'PingFang SC', 'miui', 'Hiragino Sans GB', 'Microsoft Yahei',
sans-serif;
}
.container {
display: flex;
flex-direction: column;
min-height: 100vh;
box-sizing: border-box;
}
button {
margin: 0;
padding: 0;
border: none;
background: none;
line-height: 1;
}
button::after {
border: none;
}
image {
display: block;
width: 100%;
height: 100%;
}

5
images/add.svg Normal file
View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="80" height="80" viewBox="0 0 80 80" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="10" y="10" width="60" height="60" rx="8" fill="#2196F3"/>
<path d="M40 25V55M25 40H55" stroke="white" stroke-width="6" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 299 B

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="128" height="128" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="64" cy="64" r="64" fill="#F0F0F0"/>
<circle cx="64" cy="48" r="24" fill="#CCCCCC"/>
<path d="M64 80C42.9086 80 24.6768 92.804 20.0288 110.424C28.0656 120.808 40.1408 128 64 128C87.8592 128 99.9344 120.808 107.971 110.424C103.323 92.804 85.0914 80 64 80Z" fill="#CCCCCC"/>
</svg>

After

Width:  |  Height:  |  Size: 436 B

7
images/items.svg Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="80" height="80" viewBox="0 0 80 80" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="10" y="10" width="60" height="60" rx="8" fill="#4CAF50"/>
<rect x="20" y="25" width="40" height="6" rx="2" fill="white"/>
<rect x="20" y="37" width="40" height="6" rx="2" fill="white"/>
<rect x="20" y="49" width="40" height="6" rx="2" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 409 B

86
pages/add/add.js Normal file
View File

@ -0,0 +1,86 @@
const app = getApp()
Page({
data: {
name: '',
description: '',
price: 0,
imageUrl: ''
},
onNameInput: function(e) {
this.setData({
name: e.detail.value
})
},
onDescInput: function(e) {
this.setData({
description: e.detail.value
})
},
onPriceInput: function(e) {
this.setData({
price: e.detail.value
})
},
chooseImage: function() {
wx.chooseImage({
count: 1,
sizeType: ['compressed'],
sourceType: ['album', 'camera'],
success: (res) => {
this.setData({
imageUrl: res.tempFilePaths[0]
})
}
})
},
submit: function() {
if (!this.data.name) {
wx.showToast({
title: '请输入物品名称',
icon: 'none'
})
return
}
const newItem = {
id: Date.now().toString(),
name: this.data.name,
description: this.data.description,
imageUrl: this.data.imageUrl,
createTime: new Date().toISOString()
}
// 更新全局数据
app.globalData.items.unshift(newItem)
wx.setStorageSync('items', app.globalData.items)
wx.showToast({
title: '添加成功',
icon: 'success',
duration: 2000,
success: () => {
setTimeout(() => {
wx.navigateBack()
}, 2000)
}
})
},
goBack: function () {
wx.navigateBack({
delta: 1,
success: function(res) {
},
fail: function(err) {
console.log(err)
}
})
}
})

29
pages/add/add.wxml Normal file
View File

@ -0,0 +1,29 @@
<view class="container">
<view class="form-item">
<text class="label">物品名称</text>
<input type="text" placeholder="请输入物品名称" bindinput="onNameInput" value="{{name}}"/>
</view>
<view class="form-item">
<text class="label">物品描述</text>
<textarea placeholder="请输入物品描述" bindinput="onDescInput" value="{{description}}"></textarea>
</view>
<view class="form-item">
<text class="label">价格</text>
<input type="digit" placeholder="请输入价格" bindinput="onPriceInput"/>
</view>
<view class="form-item">
<text class="label">物品图片</text>
<view class="image-upload" bindtap="chooseImage">
<image wx:if="{{imageUrl}}" src="{{imageUrl}}" mode="aspectFill"></image>
<view wx:else class="upload-placeholder">
<text>点击上传图片</text>
</view>
</view>
</view>
<button class="submit-btn" type="primary" bindtap="submit">保存</button>
<button class="submit-btn" type="primary" bindtap="goBack">取消</button>
</view>

51
pages/add/add.wxss Normal file
View File

@ -0,0 +1,51 @@
.container {
padding: 30rpx;
}
.form-item {
margin-bottom: 30rpx;
}
.label {
display: block;
font-size: 28rpx;
color: #333;
margin-bottom: 10rpx;
}
input, textarea {
width: 80%;
padding: 20rpx;
border: 1rpx solid #ddd;
border-radius: 8rpx;
font-size: 28rpx;
}
textarea {
height: 200rpx;
}
.image-upload {
width: 300rpx;
height: 300rpx;
border: 2rpx dashed #ddd;
border-radius: 8rpx;
display: flex;
justify-content: center;
align-items: center;
}
.image-upload image {
width: 100%;
height: 100%;
border-radius: 8rpx;
}
.upload-placeholder {
color: #999;
font-size: 28rpx;
}
.submit-btn {
margin-top: 50rpx;
}

42
pages/detail/detail.js Normal file
View File

@ -0,0 +1,42 @@
const app = getApp()
Page({
data: {
item: null
},
onLoad: function(options) {
const id = options.id
const item = app.globalData.items.find(item => item.id === id)
if (item) {
this.setData({ item })
}
},
deleteItem: function() {
wx.showModal({
title: '确认删除',
content: '确定要删除这个物品吗?',
success: (res) => {
if (res.confirm) {
const index = app.globalData.items.findIndex(item => item.id === this.data.item.id)
if (index !== -1) {
app.globalData.items.splice(index, 1)
wx.setStorageSync('items', app.globalData.items)
wx.showToast({
title: '删除成功',
icon: 'success',
duration: 2000,
success: () => {
setTimeout(() => {
wx.navigateBack()
}, 2000)
}
})
}
}
}
})
}
})

24
pages/detail/detail.wxml Normal file
View File

@ -0,0 +1,24 @@
<view class="container">
<image class="item-image" src="{{item.imageUrl}}" mode="aspectFill"></image>
<view class="item-info">
<view class="info-item">
<text class="label">物品名称</text>
<text class="value">{{item.name}}</text>
</view>
<view class="info-item">
<text class="label">物品描述</text>
<text class="value">{{item.description}}</text>
</view>
<view class="info-item">
<text class="label">添加时间</text>
<text class="value">{{item.createTime}}</text>
</view>
</view>
<view class="action-buttons">
<button class="delete-btn" type="warn" bindtap="deleteItem">删除</button>
</view>
</view>

46
pages/detail/detail.wxss Normal file
View File

@ -0,0 +1,46 @@
.container {
padding: 30rpx;
}
.item-image {
width: 100%;
height: 400rpx;
border-radius: 12rpx;
margin-bottom: 30rpx;
}
.item-info {
background: #fff;
border-radius: 12rpx;
padding: 30rpx;
margin-bottom: 30rpx;
}
.info-item {
margin-bottom: 20rpx;
}
.info-item:last-child {
margin-bottom: 0;
}
.label {
font-size: 28rpx;
color: #999;
margin-bottom: 10rpx;
display: block;
}
.value {
font-size: 32rpx;
color: #333;
line-height: 1.5;
}
.action-buttons {
margin-top: 50rpx;
}
.delete-btn {
width: 100%;
}

63
pages/index/index.js Normal file
View File

@ -0,0 +1,63 @@
const app = getApp()
Page({
data: {
userInfo: null,
hasUserInfo: false,
canIUseGetUserProfile: false,
items: [],
todayItems: []
},
onLoad: function() {
if (wx.getUserProfile) {
this.setData({
canIUseGetUserProfile: true
})
}
if (app.globalData.userInfo) {
this.setData({
userInfo: app.globalData.userInfo,
hasUserInfo: true
})
}
},
onShow: function() {
// 更新物品数据
const items = app.globalData.items || []
const today = new Date().toISOString().split('T')[0]
const todayItems = items.filter(item => item.createTime.startsWith(today))
this.setData({
items: items,
todayItems: todayItems
})
},
getUserProfile() {
wx.getUserProfile({
desc: '用于完善用户资料',
success: (res) => {
app.globalData.userInfo = res.userInfo
this.setData({
userInfo: res.userInfo,
hasUserInfo: true
})
}
})
},
navigateToItems: function() {
wx.navigateTo({
url: '/pages/items/items'
})
},
navigateToAdd: function() {
wx.navigateTo({
url: '/pages/add/add'
})
}
})

47
pages/index/index.wxml Normal file
View File

@ -0,0 +1,47 @@
<view class="container">
<view class="header">
<view class="user-info">
<image class="avatar" src="{{userInfo.avatarUrl || '/images/default-avatar.svg'}}" mode="aspectFill"></image>
<view class="user-detail">
<text class="nickname">{{userInfo.nickName || '未登录'}}</text>
<text class="welcome">欢迎使用个人物品管理</text>
</view>
<button wx:if="{{!hasUserInfo && canIUseGetUserProfile}}" class="login-btn" bindtap="getUserProfile">点击登录</button>
</view>
</view>
<view class="stats">
<view class="stat-item">
<text class="stat-num">{{items.length}}</text>
<text class="stat-label">物品总数</text>
</view>
<view class="stat-item">
<text class="stat-num">{{todayItems.length}}</text>
<text class="stat-label">今日新增</text>
</view>
</view>
<view class="menu-list">
<view class="menu-item" bindtap="navigateToItems">
<view class="menu-icon">
<image src="/images/items.svg" mode="aspectFit"></image>
</view>
<view class="menu-text">
<text class="menu-title">我的物品</text>
<text class="menu-desc">查看所有物品记录</text>
</view>
<view class="menu-arrow">></view>
</view>
<view class="menu-item" bindtap="navigateToAdd">
<view class="menu-icon">
<image src="/images/add.svg" mode="aspectFit"></image>
</view>
<view class="menu-text">
<text class="menu-title">添加物品</text>
<text class="menu-desc">记录新的物品</text>
</view>
<view class="menu-arrow">></view>
</view>
</view>
</view>

117
pages/index/index.wxss Normal file
View File

@ -0,0 +1,117 @@
.container {
padding: 0;
background: #f8f8f8;
min-height: 100vh;
}
.header {
background: #2196F3;
padding: 40rpx 30rpx;
color: #fff;
}
.user-info {
display: flex;
align-items: center;
}
.avatar {
width: 120rpx;
height: 120rpx;
border-radius: 50%;
border: 4rpx solid rgba(255, 255, 255, 0.3);
margin-right: 30rpx;
}
.user-detail {
flex: 1;
}
.nickname {
font-size: 36rpx;
font-weight: bold;
margin-bottom: 10rpx;
display: block;
}
.welcome {
font-size: 28rpx;
opacity: 0.8;
}
.login-btn {
background: rgba(255, 255, 255, 0.2);
color: #fff;
font-size: 28rpx;
padding: 10rpx 30rpx;
border-radius: 30rpx;
margin: 0;
}
.stats {
display: flex;
background: #fff;
margin: 20rpx;
border-radius: 16rpx;
padding: 30rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
}
.stat-item {
flex: 1;
text-align: center;
}
.stat-num {
font-size: 48rpx;
font-weight: bold;
color: #2196F3;
display: block;
margin-bottom: 10rpx;
}
.stat-label {
font-size: 28rpx;
color: #666;
}
.menu-list {
margin: 20rpx;
}
.menu-item {
display: flex;
align-items: center;
background: #fff;
padding: 30rpx;
border-radius: 16rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
}
.menu-icon {
width: 80rpx;
height: 80rpx;
margin-right: 30rpx;
}
.menu-text {
flex: 1;
}
.menu-title {
font-size: 32rpx;
color: #333;
display: block;
margin-bottom: 8rpx;
}
.menu-desc {
font-size: 24rpx;
color: #999;
}
.menu-arrow {
color: #999;
font-size: 32rpx;
}

47
pages/items/items.js Normal file
View File

@ -0,0 +1,47 @@
const app = getApp()
Page({
data: {
items: [],
filteredItems: [],
searchText: ''
},
onLoad: function() {
this.setData({
items: app.globalData.items,
filteredItems: app.globalData.items
})
},
onShow: function() {
// 每次页面显示时更新数据
this.setData({
items: app.globalData.items,
filteredItems: this.filterItems(app.globalData.items, this.data.searchText)
})
},
onSearch: function(e) {
const searchText = e.detail.value
this.setData({
searchText,
filteredItems: this.filterItems(this.data.items, searchText)
})
},
filterItems: function(items, searchText) {
if (!searchText) return items
return items.filter(item =>
item.name.includes(searchText) ||
item.description.includes(searchText)
)
},
navigateToDetail: function(e) {
const id = e.currentTarget.dataset.id
wx.navigateTo({
url: `/pages/detail/detail?id=${id}`
})
}
})

21
pages/items/items.wxml Normal file
View File

@ -0,0 +1,21 @@
<view class="container">
<view class="search-bar">
<input type="text" placeholder="搜索物品" bindinput="onSearch" value="{{searchText}}"/>
</view>
<view class="items-list">
<block wx:for="{{filteredItems}}" wx:key="id">
<view class="item-card" bindtap="navigateToDetail" data-id="{{item.id}}">
<image class="item-image" src="{{item.imageUrl}}" mode="aspectFill"></image>
<view class="item-info">
<text class="item-name">{{item.name}}</text>
<text class="item-desc">{{item.description}}</text>
</view>
</view>
</block>
</view>
<view class="empty-tip" wx:if="{{filteredItems.length === 0}}">
<text>暂无物品记录</text>
</view>
</view>

66
pages/items/items.wxss Normal file
View File

@ -0,0 +1,66 @@
.container {
padding: 20rpx;
}
.search-bar {
padding: 20rpx;
background: #f5f5f5;
border-radius: 10rpx;
margin-bottom: 20rpx;
}
.search-bar input {
background: #fff;
padding: 10rpx 20rpx;
border-radius: 8rpx;
}
.items-list {
display: flex;
flex-direction: column;
gap: 20rpx;
}
.item-card {
display: flex;
background: #fff;
padding: 20rpx;
border-radius: 10rpx;
box-shadow: 0 2rpx 10rpx rgba(0,0,0,0.1);
}
.item-image {
width: 160rpx;
height: 160rpx;
border-radius: 8rpx;
margin-right: 20rpx;
}
.item-info {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.item-name {
font-size: 32rpx;
font-weight: bold;
color: #333;
}
.item-desc {
font-size: 28rpx;
color: #666;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
}
.empty-tip {
text-align: center;
padding: 100rpx 0;
color: #999;
font-size: 28rpx;
}

59
project.config.json Normal file
View File

@ -0,0 +1,59 @@
{
"description": "项目配置文件",
"packOptions": {
"ignore": [],
"include": []
},
"setting": {
"urlCheck": false,
"es6": true,
"enhance": false,
"postcss": true,
"preloadBackgroundData": false,
"minified": true,
"newFeature": false,
"coverView": true,
"nodeModules": false,
"autoAudits": false,
"showShadowRootInWxmlPanel": true,
"scopeDataCheck": false,
"uglifyFileName": false,
"checkInvalidKey": true,
"checkSiteMap": true,
"uploadWithSourceMap": true,
"compileHotReLoad": false,
"useMultiFrameRuntime": false,
"useApiHook": false,
"useApiHostProcess": false,
"babelSetting": {
"ignore": [],
"disablePlugins": [],
"outputPath": ""
},
"enableEngineNative": false,
"bundle": false,
"useIsolateContext": false,
"useCompilerModule": true,
"userConfirmedUseCompilerModuleSwitch": false,
"userConfirmedBundleSwitch": false,
"packNpmManually": false,
"packNpmRelationList": [],
"minifyWXSS": true,
"showES6CompileOption": false,
"useCompilerPlugins": false,
"ignoreUploadUnusedFiles": true,
"disableUseStrict": false,
"minifyJS": true,
"useStaticServer": true,
"enableWasm": false
},
"compileType": "miniprogram",
"libVersion": "2.19.4",
"appid": "wx44eba9dbd5555ee9",
"projectname": "个人物品管理",
"condition": {},
"editorSetting": {
"tabIndent": "insertSpaces",
"tabSize": 2
}
}

View File

@ -0,0 +1,20 @@
{
"description": "项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html",
"projectname": "个人物品管理",
"miniprogramRoot": "",
"setting": {
"compileHotReLoad": false,
"urlCheck": false,
"showShadowRootInWxmlPanel": true,
"useStaticServer": true,
"showES6CompileOption": false
},
"debugOptions": {
"hidedInDevtools": []
},
"scripts": {},
"staticServerOptions": {
"baseURL": "",
"servePath": ""
}
}

7
sitemap.json Normal file
View File

@ -0,0 +1,7 @@
{
"desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html",
"rules": [{
"action": "allow",
"page": "*"
}]
}

27
utils/debug.js Normal file
View File

@ -0,0 +1,27 @@
const debug = {
log: function(...args) {
if (wx.getLogManager) {
const logManager = wx.getLogManager()
logManager.log(...args)
}
console.log(...args)
},
error: function(...args) {
if (wx.getLogManager) {
const logManager = wx.getLogManager()
logManager.error(...args)
}
console.error(...args)
},
warn: function(...args) {
if (wx.getLogManager) {
const logManager = wx.getLogManager()
logManager.warn(...args)
}
console.warn(...args)
}
}
module.exports = debug