refactor: 重构数据库配置为SQLite开发环境并移除冗余文档
This commit is contained in:
225
mini_program/cattle-trading/App.vue
Normal file
225
mini_program/cattle-trading/App.vue
Normal file
@@ -0,0 +1,225 @@
|
||||
<template>
|
||||
<view id="app">
|
||||
<!-- 牛只交易小程序入口 -->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'App',
|
||||
|
||||
onLaunch: function() {
|
||||
console.log('牛只交易小程序启动');
|
||||
this.initApp();
|
||||
},
|
||||
|
||||
onShow: function() {
|
||||
console.log('牛只交易小程序显示');
|
||||
},
|
||||
|
||||
onHide: function() {
|
||||
console.log('牛只交易小程序隐藏');
|
||||
},
|
||||
|
||||
methods: {
|
||||
// 初始化应用
|
||||
initApp() {
|
||||
// 检查更新
|
||||
this.checkUpdate();
|
||||
|
||||
// 初始化全局数据
|
||||
this.initGlobalData();
|
||||
|
||||
// 检查登录状态
|
||||
this.checkLoginStatus();
|
||||
},
|
||||
|
||||
// 检查小程序更新
|
||||
checkUpdate() {
|
||||
// #ifdef MP-WEIXIN
|
||||
if (uni.canIUse('getUpdateManager')) {
|
||||
const updateManager = uni.getUpdateManager();
|
||||
|
||||
updateManager.onCheckForUpdate((res) => {
|
||||
if (res.hasUpdate) {
|
||||
console.log('发现新版本');
|
||||
}
|
||||
});
|
||||
|
||||
updateManager.onUpdateReady(() => {
|
||||
uni.showModal({
|
||||
title: '更新提示',
|
||||
content: '新版本已经准备好,是否重启应用?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
updateManager.applyUpdate();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
updateManager.onUpdateFailed(() => {
|
||||
uni.showModal({
|
||||
title: '更新失败',
|
||||
content: '新版本下载失败,请检查网络后重试',
|
||||
showCancel: false
|
||||
});
|
||||
});
|
||||
}
|
||||
// #endif
|
||||
},
|
||||
|
||||
// 初始化全局数据
|
||||
initGlobalData() {
|
||||
getApp().globalData = {
|
||||
userInfo: null,
|
||||
token: '',
|
||||
baseUrl: 'https://api.cattle-trading.com',
|
||||
version: '1.0.0'
|
||||
};
|
||||
},
|
||||
|
||||
// 检查登录状态
|
||||
checkLoginStatus() {
|
||||
const token = uni.getStorageSync('token');
|
||||
if (token) {
|
||||
getApp().globalData.token = token;
|
||||
this.validateToken(token);
|
||||
}
|
||||
},
|
||||
|
||||
// 验证token
|
||||
async validateToken(token) {
|
||||
try {
|
||||
const res = await uni.request({
|
||||
url: getApp().globalData.baseUrl + '/auth/validate',
|
||||
method: 'POST',
|
||||
header: {
|
||||
'Authorization': 'Bearer ' + token
|
||||
}
|
||||
});
|
||||
|
||||
if (res.data.code === 200) {
|
||||
getApp().globalData.userInfo = res.data.data;
|
||||
} else {
|
||||
uni.removeStorageSync('token');
|
||||
getApp().globalData.token = '';
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('验证token失败:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import '@/uni_modules/uni-scss/index.scss';
|
||||
|
||||
page {
|
||||
background-color: #f5f5f5;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
.page-container {
|
||||
min-height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: #ffffff;
|
||||
border-radius: 16rpx;
|
||||
padding: 24rpx;
|
||||
margin-bottom: 20rpx;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: linear-gradient(135deg, #FF6B35, #FF8E53);
|
||||
color: #ffffff;
|
||||
border: none;
|
||||
border-radius: 12rpx;
|
||||
padding: 24rpx 48rpx;
|
||||
font-size: 32rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: #ffffff;
|
||||
color: #FF6B35;
|
||||
border: 2rpx solid #FF6B35;
|
||||
border-radius: 12rpx;
|
||||
padding: 22rpx 46rpx;
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
.text-primary {
|
||||
color: #FF6B35;
|
||||
}
|
||||
|
||||
.text-secondary {
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.text-muted {
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.flex-column {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.flex-center {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.flex-between {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.flex-around {
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.flex-1 {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.ellipsis {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.ellipsis-2 {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
.fade-in {
|
||||
animation: fadeIn 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20rpx);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,80 +1,302 @@
|
||||
// 牛只交易小程序
|
||||
App({
|
||||
onLaunch() {
|
||||
// 小程序初始化
|
||||
console.log('牛肉商城小程序初始化');
|
||||
|
||||
// 检查登录状态
|
||||
this.checkLoginStatus();
|
||||
},
|
||||
|
||||
onShow() {
|
||||
// 小程序显示
|
||||
console.log('牛肉商城小程序显示');
|
||||
},
|
||||
|
||||
onHide() {
|
||||
// 小程序隐藏
|
||||
console.log('牛肉商城小程序隐藏');
|
||||
},
|
||||
|
||||
onError(msg) {
|
||||
// 错误处理
|
||||
console.log('小程序发生错误:', msg);
|
||||
},
|
||||
|
||||
globalData: {
|
||||
userInfo: null,
|
||||
token: null,
|
||||
baseUrl: 'http://localhost:8000/api'
|
||||
apiBase: 'https://api.xlxumu.com',
|
||||
version: '1.0.0'
|
||||
},
|
||||
|
||||
// 检查登录状态
|
||||
checkLoginStatus() {
|
||||
try {
|
||||
const token = wx.getStorageSync('token');
|
||||
if (token) {
|
||||
this.globalData.token = token;
|
||||
// 验证token有效性
|
||||
this.verifyToken(token);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('检查登录状态失败:', e);
|
||||
|
||||
onLaunch() {
|
||||
console.log('牛只交易小程序启动');
|
||||
|
||||
// 检查更新
|
||||
this.checkForUpdate();
|
||||
|
||||
// 初始化用户信息
|
||||
this.initUserInfo();
|
||||
|
||||
// 设置网络状态监听
|
||||
this.setupNetworkListener();
|
||||
},
|
||||
|
||||
onShow() {
|
||||
console.log('牛只交易小程序显示');
|
||||
},
|
||||
|
||||
onHide() {
|
||||
console.log('牛只交易小程序隐藏');
|
||||
},
|
||||
|
||||
onError(msg) {
|
||||
console.error('小程序错误:', msg);
|
||||
},
|
||||
|
||||
// 检查小程序更新
|
||||
checkForUpdate() {
|
||||
if (wx.canIUse('getUpdateManager')) {
|
||||
const updateManager = wx.getUpdateManager();
|
||||
|
||||
updateManager.onCheckForUpdate((res) => {
|
||||
if (res.hasUpdate) {
|
||||
console.log('发现新版本');
|
||||
}
|
||||
});
|
||||
|
||||
updateManager.onUpdateReady(() => {
|
||||
wx.showModal({
|
||||
title: '更新提示',
|
||||
content: '新版本已经准备好,是否重启应用?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
updateManager.applyUpdate();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
updateManager.onUpdateFailed(() => {
|
||||
console.error('新版本下载失败');
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// 验证token
|
||||
verifyToken(token) {
|
||||
wx.request({
|
||||
url: `${this.globalData.baseUrl}/auth/verify`,
|
||||
method: 'GET',
|
||||
header: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
},
|
||||
success: (res) => {
|
||||
if (res.data.valid) {
|
||||
this.globalData.userInfo = res.data.user;
|
||||
} else {
|
||||
// token无效,清除本地存储
|
||||
wx.removeStorageSync('token');
|
||||
this.globalData.token = null;
|
||||
this.globalData.userInfo = null;
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('验证token失败:', err);
|
||||
|
||||
// 初始化用户信息
|
||||
initUserInfo() {
|
||||
try {
|
||||
const token = wx.getStorageSync('token');
|
||||
const userInfo = wx.getStorageSync('userInfo');
|
||||
|
||||
if (token && userInfo) {
|
||||
this.globalData.token = token;
|
||||
this.globalData.userInfo = userInfo;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('初始化用户信息失败:', error);
|
||||
}
|
||||
},
|
||||
|
||||
// 设置网络状态监听
|
||||
setupNetworkListener() {
|
||||
wx.onNetworkStatusChange((res) => {
|
||||
if (!res.isConnected) {
|
||||
this.showError('网络连接已断开');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// 登录方法
|
||||
login(userInfo) {
|
||||
this.globalData.userInfo = userInfo;
|
||||
|
||||
// 微信登录
|
||||
async wxLogin() {
|
||||
try {
|
||||
// 检查登录状态
|
||||
const loginRes = await this.checkSession();
|
||||
if (loginRes) {
|
||||
return this.globalData.userInfo;
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('登录状态已过期,重新登录');
|
||||
}
|
||||
|
||||
try {
|
||||
// 获取登录code
|
||||
const { code } = await this.promisify(wx.login)();
|
||||
|
||||
// 获取用户信息
|
||||
const userProfile = await this.getUserProfile();
|
||||
|
||||
// 发送登录请求
|
||||
const response = await this.request({
|
||||
url: '/auth/wechat/login',
|
||||
method: 'POST',
|
||||
data: {
|
||||
code,
|
||||
encrypted_data: userProfile.encryptedData,
|
||||
iv: userProfile.iv
|
||||
}
|
||||
});
|
||||
|
||||
const { token, user } = response.data;
|
||||
|
||||
// 保存用户信息
|
||||
this.globalData.token = token;
|
||||
this.globalData.userInfo = user;
|
||||
|
||||
wx.setStorageSync('token', token);
|
||||
wx.setStorageSync('userInfo', user);
|
||||
|
||||
return user;
|
||||
|
||||
} catch (error) {
|
||||
console.error('微信登录失败:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// 登出方法
|
||||
logout() {
|
||||
this.globalData.userInfo = null;
|
||||
|
||||
// 检查登录状态
|
||||
checkSession() {
|
||||
return new Promise((resolve, reject) => {
|
||||
wx.checkSession({
|
||||
success: () => {
|
||||
if (this.globalData.userInfo) {
|
||||
resolve(this.globalData.userInfo);
|
||||
} else {
|
||||
reject(new Error('用户信息不存在'));
|
||||
}
|
||||
},
|
||||
fail: reject
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// 获取用户信息
|
||||
getUserProfile() {
|
||||
return new Promise((resolve, reject) => {
|
||||
wx.getUserProfile({
|
||||
desc: '用于完善用户资料',
|
||||
success: resolve,
|
||||
fail: reject
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// 网络请求封装
|
||||
async request(options) {
|
||||
const {
|
||||
url,
|
||||
method = 'GET',
|
||||
data = {},
|
||||
header = {}
|
||||
} = options;
|
||||
|
||||
// 添加认证头
|
||||
if (this.globalData.token) {
|
||||
header.Authorization = `Bearer ${this.globalData.token}`;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await this.promisify(wx.request)({
|
||||
url: this.globalData.apiBase + url,
|
||||
method,
|
||||
data,
|
||||
header: {
|
||||
'Content-Type': 'application/json',
|
||||
...header
|
||||
}
|
||||
});
|
||||
|
||||
const { statusCode, data: responseData } = response;
|
||||
|
||||
if (statusCode === 200) {
|
||||
if (responseData.code === 0) {
|
||||
return responseData;
|
||||
} else {
|
||||
throw new Error(responseData.message || '请求失败');
|
||||
}
|
||||
} else if (statusCode === 401) {
|
||||
// 登录过期,清除用户信息
|
||||
this.clearUserInfo();
|
||||
wx.navigateTo({
|
||||
url: '/pages/auth/login'
|
||||
});
|
||||
throw new Error('登录已过期,请重新登录');
|
||||
} else {
|
||||
throw new Error(`请求失败:${statusCode}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('请求错误:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// 清除用户信息
|
||||
clearUserInfo() {
|
||||
this.globalData.token = null;
|
||||
this.globalData.userInfo = null;
|
||||
wx.removeStorageSync('token');
|
||||
wx.removeStorageSync('userInfo');
|
||||
},
|
||||
|
||||
// Promise化微信API
|
||||
promisify(fn) {
|
||||
return (options = {}) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
fn({
|
||||
...options,
|
||||
success: resolve,
|
||||
fail: reject
|
||||
});
|
||||
});
|
||||
};
|
||||
},
|
||||
|
||||
// 显示加载提示
|
||||
showLoading(title = '加载中...') {
|
||||
wx.showLoading({
|
||||
title,
|
||||
mask: true
|
||||
});
|
||||
},
|
||||
|
||||
// 隐藏加载提示
|
||||
hideLoading() {
|
||||
wx.hideLoading();
|
||||
},
|
||||
|
||||
// 显示成功提示
|
||||
showSuccess(title) {
|
||||
wx.showToast({
|
||||
title,
|
||||
icon: 'success',
|
||||
duration: 2000
|
||||
});
|
||||
},
|
||||
|
||||
// 显示错误提示
|
||||
showError(title) {
|
||||
wx.showToast({
|
||||
title,
|
||||
icon: 'none',
|
||||
duration: 3000
|
||||
});
|
||||
},
|
||||
|
||||
// 显示确认对话框
|
||||
showConfirm(options) {
|
||||
return new Promise((resolve) => {
|
||||
wx.showModal({
|
||||
title: options.title || '提示',
|
||||
content: options.content,
|
||||
confirmText: options.confirmText || '确定',
|
||||
cancelText: options.cancelText || '取消',
|
||||
success: (res) => {
|
||||
resolve(res.confirm);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// 格式化价格
|
||||
formatPrice(price) {
|
||||
if (!price) return '面议';
|
||||
return `¥${parseFloat(price).toLocaleString()}`;
|
||||
},
|
||||
|
||||
// 格式化时间
|
||||
formatTime(timestamp) {
|
||||
const date = new Date(timestamp);
|
||||
const now = new Date();
|
||||
const diff = now - date;
|
||||
|
||||
if (diff < 60000) { // 1分钟内
|
||||
return '刚刚';
|
||||
} else if (diff < 3600000) { // 1小时内
|
||||
return `${Math.floor(diff / 60000)}分钟前`;
|
||||
} else if (diff < 86400000) { // 1天内
|
||||
return `${Math.floor(diff / 3600000)}小时前`;
|
||||
} else if (diff < 2592000000) { // 30天内
|
||||
return `${Math.floor(diff / 86400000)}天前`;
|
||||
} else {
|
||||
return date.toLocaleDateString();
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
@@ -1,14 +1,84 @@
|
||||
{
|
||||
"pages": [
|
||||
"pages/index/index",
|
||||
"pages/logs/logs"
|
||||
"pages/auth/login",
|
||||
"pages/market/list",
|
||||
"pages/market/detail",
|
||||
"pages/market/publish",
|
||||
"pages/market/search",
|
||||
"pages/order/list",
|
||||
"pages/order/detail",
|
||||
"pages/profile/index",
|
||||
"pages/profile/edit",
|
||||
"pages/profile/verification",
|
||||
"pages/message/index",
|
||||
"pages/message/detail",
|
||||
"pages/common/webview"
|
||||
],
|
||||
"tabBar": {
|
||||
"color": "#666666",
|
||||
"selectedColor": "#2E8B57",
|
||||
"backgroundColor": "#ffffff",
|
||||
"borderStyle": "black",
|
||||
"list": [
|
||||
{
|
||||
"pagePath": "pages/index/index",
|
||||
"text": "首页",
|
||||
"iconPath": "images/tabbar/home.png",
|
||||
"selectedIconPath": "images/tabbar/home-active.png"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/market/list",
|
||||
"text": "市场",
|
||||
"iconPath": "images/tabbar/market.png",
|
||||
"selectedIconPath": "images/tabbar/market-active.png"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/order/list",
|
||||
"text": "订单",
|
||||
"iconPath": "images/tabbar/order.png",
|
||||
"selectedIconPath": "images/tabbar/order-active.png"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/message/index",
|
||||
"text": "消息",
|
||||
"iconPath": "images/tabbar/message.png",
|
||||
"selectedIconPath": "images/tabbar/message-active.png"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/profile/index",
|
||||
"text": "我的",
|
||||
"iconPath": "images/tabbar/profile.png",
|
||||
"selectedIconPath": "images/tabbar/profile-active.png"
|
||||
}
|
||||
]
|
||||
},
|
||||
"window": {
|
||||
"backgroundTextStyle": "light",
|
||||
"navigationBarBackgroundColor": "#fff",
|
||||
"navigationBarTitleText": "锡林郭勒盟智慧养殖",
|
||||
"navigationBarTextStyle": "black"
|
||||
"navigationBarBackgroundColor": "#2E8B57",
|
||||
"navigationBarTitleText": "牛只交易平台",
|
||||
"navigationBarTextStyle": "white",
|
||||
"backgroundColor": "#f5f5f5",
|
||||
"enablePullDownRefresh": true,
|
||||
"onReachBottomDistance": 50
|
||||
},
|
||||
"style": "v2",
|
||||
"sitemapLocation": "sitemap.json"
|
||||
}
|
||||
"networkTimeout": {
|
||||
"request": 10000,
|
||||
"downloadFile": 10000
|
||||
},
|
||||
"debug": false,
|
||||
"permission": {
|
||||
"scope.userLocation": {
|
||||
"desc": "您的位置信息将用于展示附近的牛只交易信息"
|
||||
}
|
||||
},
|
||||
"requiredBackgroundModes": ["location"],
|
||||
"plugins": {},
|
||||
"preloadRule": {
|
||||
"pages/market/list": {
|
||||
"network": "all",
|
||||
"packages": ["market"]
|
||||
}
|
||||
},
|
||||
"lazyCodeLoading": "requiredComponents"
|
||||
}
|
||||
54
mini_program/cattle-trading/manifest.json
Normal file
54
mini_program/cattle-trading/manifest.json
Normal file
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"name": "cattle-trading",
|
||||
"appid": "wx2345678901bcdefg",
|
||||
"description": "安全便捷的牲畜交易平台",
|
||||
"versionName": "1.0.0",
|
||||
"versionCode": "100",
|
||||
"transformPx": false,
|
||||
"mp-weixin": {
|
||||
"appid": "wx2345678901bcdefg",
|
||||
"setting": {
|
||||
"urlCheck": false,
|
||||
"es6": true,
|
||||
"enhance": true,
|
||||
"postcss": true,
|
||||
"minified": true,
|
||||
"newFeature": false,
|
||||
"coverView": true,
|
||||
"nodeModules": false,
|
||||
"autoAudits": false,
|
||||
"showShadowRootInWxmlPanel": true,
|
||||
"scopeDataCheck": false,
|
||||
"checkInvalidKey": true,
|
||||
"checkSiteMap": true,
|
||||
"uploadWithSourceMap": true,
|
||||
"useMultiFrameRuntime": true,
|
||||
"useApiHook": true,
|
||||
"useApiHostProcess": true,
|
||||
"enableEngineNative": false,
|
||||
"useIsolateContext": true,
|
||||
"minifyWXSS": true,
|
||||
"disableUseStrict": false,
|
||||
"minifyWXML": true
|
||||
},
|
||||
"usingComponents": true,
|
||||
"permission": {
|
||||
"scope.userLocation": {
|
||||
"desc": "用于定位交易地点和物流配送"
|
||||
},
|
||||
"scope.camera": {
|
||||
"desc": "需要使用摄像头拍摄牲畜照片"
|
||||
},
|
||||
"scope.album": {
|
||||
"desc": "需要访问相册选择牲畜照片"
|
||||
}
|
||||
},
|
||||
"requiredPrivateInfos": [
|
||||
"getLocation",
|
||||
"chooseLocation",
|
||||
"chooseImage",
|
||||
"chooseVideo"
|
||||
]
|
||||
},
|
||||
"vueVersion": "3"
|
||||
}
|
||||
100
mini_program/cattle-trading/pages.json
Normal file
100
mini_program/cattle-trading/pages.json
Normal file
@@ -0,0 +1,100 @@
|
||||
{
|
||||
"pages": [
|
||||
{
|
||||
"path": "pages/index/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "牲畜交易",
|
||||
"enablePullDownRefresh": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/market/list",
|
||||
"style": {
|
||||
"navigationBarTitleText": "交易市场",
|
||||
"enablePullDownRefresh": true,
|
||||
"onReachBottomDistance": 50
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/market/detail",
|
||||
"style": {
|
||||
"navigationBarTitleText": "交易详情"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/market/analysis",
|
||||
"style": {
|
||||
"navigationBarTitleText": "市场分析",
|
||||
"enablePullDownRefresh": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/publish/publish",
|
||||
"style": {
|
||||
"navigationBarTitleText": "发布交易"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/orders/list",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的订单",
|
||||
"enablePullDownRefresh": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/orders/detail",
|
||||
"style": {
|
||||
"navigationBarTitleText": "订单详情"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/profile/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "个人中心"
|
||||
}
|
||||
}
|
||||
],
|
||||
"globalStyle": {
|
||||
"navigationBarTextStyle": "black",
|
||||
"navigationBarTitleText": "牲畜交易",
|
||||
"navigationBarBackgroundColor": "#F8F8F8",
|
||||
"backgroundColor": "#F8F8F8"
|
||||
},
|
||||
"tabBar": {
|
||||
"color": "#7A7E83",
|
||||
"selectedColor": "#1890ff",
|
||||
"backgroundColor": "#ffffff",
|
||||
"list": [
|
||||
{
|
||||
"pagePath": "pages/index/index",
|
||||
"iconPath": "static/tabbar/home.png",
|
||||
"selectedIconPath": "static/tabbar/home-active.png",
|
||||
"text": "首页"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/market/list",
|
||||
"iconPath": "static/tabbar/market.png",
|
||||
"selectedIconPath": "static/tabbar/market-active.png",
|
||||
"text": "市场"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/publish/publish",
|
||||
"iconPath": "static/tabbar/publish.png",
|
||||
"selectedIconPath": "static/tabbar/publish-active.png",
|
||||
"text": "发布"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/orders/list",
|
||||
"iconPath": "static/tabbar/orders.png",
|
||||
"selectedIconPath": "static/tabbar/orders-active.png",
|
||||
"text": "订单"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/profile/index",
|
||||
"iconPath": "static/tabbar/profile.png",
|
||||
"selectedIconPath": "static/tabbar/profile-active.png",
|
||||
"text": "我的"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
1020
mini_program/cattle-trading/pages/detail/detail.vue
Normal file
1020
mini_program/cattle-trading/pages/detail/detail.vue
Normal file
File diff suppressed because it is too large
Load Diff
251
mini_program/cattle-trading/pages/index/index.js
Normal file
251
mini_program/cattle-trading/pages/index/index.js
Normal file
@@ -0,0 +1,251 @@
|
||||
// 牛只交易首页
|
||||
const app = getApp();
|
||||
|
||||
Page({
|
||||
data: {
|
||||
userInfo: null,
|
||||
bannerList: [],
|
||||
categoryList: [],
|
||||
hotProducts: [],
|
||||
recentProducts: [],
|
||||
marketStats: {
|
||||
totalProducts: 0,
|
||||
todayDeals: 0,
|
||||
avgPrice: 0
|
||||
},
|
||||
loading: true,
|
||||
refreshing: false
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
this.checkLogin();
|
||||
},
|
||||
|
||||
onShow() {
|
||||
if (app.globalData.userInfo) {
|
||||
this.setData({
|
||||
userInfo: app.globalData.userInfo
|
||||
});
|
||||
this.loadData();
|
||||
}
|
||||
},
|
||||
|
||||
onPullDownRefresh() {
|
||||
this.setData({ refreshing: true });
|
||||
this.loadData().finally(() => {
|
||||
this.setData({ refreshing: false });
|
||||
wx.stopPullDownRefresh();
|
||||
});
|
||||
},
|
||||
|
||||
// 检查登录状态
|
||||
checkLogin() {
|
||||
if (!app.globalData.userInfo) {
|
||||
wx.navigateTo({
|
||||
url: '/pages/auth/login'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.setData({
|
||||
userInfo: app.globalData.userInfo
|
||||
});
|
||||
this.loadData();
|
||||
},
|
||||
|
||||
// 加载数据
|
||||
async loadData() {
|
||||
try {
|
||||
this.setData({ loading: true });
|
||||
|
||||
await Promise.all([
|
||||
this.loadBanners(),
|
||||
this.loadCategories(),
|
||||
this.loadHotProducts(),
|
||||
this.loadRecentProducts(),
|
||||
this.loadMarketStats()
|
||||
]);
|
||||
} catch (error) {
|
||||
console.error('加载数据失败:', error);
|
||||
app.showError('加载数据失败');
|
||||
} finally {
|
||||
this.setData({ loading: false });
|
||||
}
|
||||
},
|
||||
|
||||
// 加载轮播图
|
||||
async loadBanners() {
|
||||
try {
|
||||
const res = await app.request({
|
||||
url: '/banners',
|
||||
method: 'GET',
|
||||
data: { type: 'home' }
|
||||
});
|
||||
|
||||
this.setData({
|
||||
bannerList: res.data.list || []
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('加载轮播图失败:', error);
|
||||
}
|
||||
},
|
||||
|
||||
// 加载分类
|
||||
async loadCategories() {
|
||||
try {
|
||||
const res = await app.request({
|
||||
url: '/categories',
|
||||
method: 'GET'
|
||||
});
|
||||
|
||||
this.setData({
|
||||
categoryList: res.data.list || []
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('加载分类失败:', error);
|
||||
}
|
||||
},
|
||||
|
||||
// 加载热门商品
|
||||
async loadHotProducts() {
|
||||
try {
|
||||
const res = await app.request({
|
||||
url: '/products',
|
||||
method: 'GET',
|
||||
data: {
|
||||
sort: 'hot',
|
||||
limit: 6
|
||||
}
|
||||
});
|
||||
|
||||
this.setData({
|
||||
hotProducts: res.data.list || []
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('加载热门商品失败:', error);
|
||||
}
|
||||
},
|
||||
|
||||
// 加载最新商品
|
||||
async loadRecentProducts() {
|
||||
try {
|
||||
const res = await app.request({
|
||||
url: '/products',
|
||||
method: 'GET',
|
||||
data: {
|
||||
sort: 'latest',
|
||||
limit: 10
|
||||
}
|
||||
});
|
||||
|
||||
this.setData({
|
||||
recentProducts: res.data.list || []
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('加载最新商品失败:', error);
|
||||
}
|
||||
},
|
||||
|
||||
// 加载市场统计
|
||||
async loadMarketStats() {
|
||||
try {
|
||||
const res = await app.request({
|
||||
url: '/market/stats',
|
||||
method: 'GET'
|
||||
});
|
||||
|
||||
this.setData({
|
||||
marketStats: res.data || {}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('加载市场统计失败:', error);
|
||||
}
|
||||
},
|
||||
|
||||
// 轮播图点击
|
||||
onBannerTap(e) {
|
||||
const { banner } = e.currentTarget.dataset;
|
||||
if (banner.link_type === 'product') {
|
||||
wx.navigateTo({
|
||||
url: `/pages/market/detail?id=${banner.link_value}`
|
||||
});
|
||||
} else if (banner.link_type === 'category') {
|
||||
wx.navigateTo({
|
||||
url: `/pages/market/list?category=${banner.link_value}`
|
||||
});
|
||||
} else if (banner.link_type === 'url') {
|
||||
wx.navigateTo({
|
||||
url: `/pages/common/webview?url=${encodeURIComponent(banner.link_value)}`
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// 分类点击
|
||||
onCategoryTap(e) {
|
||||
const { category } = e.currentTarget.dataset;
|
||||
wx.navigateTo({
|
||||
url: `/pages/market/list?category=${category.id}`
|
||||
});
|
||||
},
|
||||
|
||||
// 商品点击
|
||||
onProductTap(e) {
|
||||
const { product } = e.currentTarget.dataset;
|
||||
wx.navigateTo({
|
||||
url: `/pages/market/detail?id=${product.id}`
|
||||
});
|
||||
},
|
||||
|
||||
// 搜索
|
||||
onSearch() {
|
||||
wx.navigateTo({
|
||||
url: '/pages/market/search'
|
||||
});
|
||||
},
|
||||
|
||||
// 发布商品
|
||||
onPublish() {
|
||||
wx.navigateTo({
|
||||
url: '/pages/market/publish'
|
||||
});
|
||||
},
|
||||
|
||||
// 查看更多热门
|
||||
onViewMoreHot() {
|
||||
wx.navigateTo({
|
||||
url: '/pages/market/list?sort=hot'
|
||||
});
|
||||
},
|
||||
|
||||
// 查看更多最新
|
||||
onViewMoreRecent() {
|
||||
wx.navigateTo({
|
||||
url: '/pages/market/list?sort=latest'
|
||||
});
|
||||
},
|
||||
|
||||
// 查看全部商品
|
||||
onViewAllProducts() {
|
||||
wx.switchTab({
|
||||
url: '/pages/market/list'
|
||||
});
|
||||
},
|
||||
|
||||
// 格式化价格
|
||||
formatPrice(price) {
|
||||
return app.formatPrice(price);
|
||||
},
|
||||
|
||||
// 格式化时间
|
||||
formatTime(timestamp) {
|
||||
return app.formatTime(timestamp);
|
||||
},
|
||||
|
||||
// 格式化数量
|
||||
formatNumber(num) {
|
||||
if (num >= 10000) {
|
||||
return (num / 10000).toFixed(1) + '万';
|
||||
}
|
||||
return num.toString();
|
||||
}
|
||||
});
|
||||
642
mini_program/cattle-trading/pages/index/index.vue
Normal file
642
mini_program/cattle-trading/pages/index/index.vue
Normal file
@@ -0,0 +1,642 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<!-- 顶部搜索栏 -->
|
||||
<view class="search-bar">
|
||||
<view class="search-input">
|
||||
<uni-icons type="search" size="20" color="#999"></uni-icons>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="搜索牛只品种、价格等"
|
||||
v-model="searchKeyword"
|
||||
@confirm="handleSearch"
|
||||
/>
|
||||
</view>
|
||||
<view class="filter-btn" @click="showFilter = true">
|
||||
<uni-icons type="tune" size="20" color="#007AFF"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 分类导航 -->
|
||||
<view class="category-nav">
|
||||
<scroll-view scroll-x="true" class="category-scroll">
|
||||
<view class="category-list">
|
||||
<view
|
||||
class="category-item"
|
||||
:class="{ active: activeCategory === item.id }"
|
||||
v-for="item in categories"
|
||||
:key="item.id"
|
||||
@click="selectCategory(item.id)"
|
||||
>
|
||||
{{ item.name }}
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<!-- 轮播图 -->
|
||||
<view class="banner-section">
|
||||
<swiper
|
||||
class="banner-swiper"
|
||||
indicator-dots="true"
|
||||
autoplay="true"
|
||||
interval="3000"
|
||||
duration="500"
|
||||
>
|
||||
<swiper-item v-for="(banner, index) in banners" :key="index">
|
||||
<image :src="banner.image" class="banner-image" mode="aspectFill"></image>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
</view>
|
||||
|
||||
<!-- 快捷功能 -->
|
||||
<view class="quick-actions">
|
||||
<view class="action-item" @click="navigateTo('/pages/publish/publish')">
|
||||
<view class="action-icon publish">
|
||||
<uni-icons type="plus" size="24" color="#fff"></uni-icons>
|
||||
</view>
|
||||
<text class="action-text">发布交易</text>
|
||||
</view>
|
||||
<view class="action-item" @click="navigateTo('/pages/market/market')">
|
||||
<view class="action-icon market">
|
||||
<uni-icons type="shop" size="24" color="#fff"></uni-icons>
|
||||
</view>
|
||||
<text class="action-text">交易市场</text>
|
||||
</view>
|
||||
<view class="action-item" @click="navigateTo('/pages/orders/orders')">
|
||||
<view class="action-icon orders">
|
||||
<uni-icons type="list" size="24" color="#fff"></uni-icons>
|
||||
</view>
|
||||
<text class="action-text">我的订单</text>
|
||||
</view>
|
||||
<view class="action-item" @click="navigateTo('/pages/profile/profile')">
|
||||
<view class="action-icon profile">
|
||||
<uni-icons type="person" size="24" color="#fff"></uni-icons>
|
||||
</view>
|
||||
<text class="action-text">个人中心</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 推荐交易 -->
|
||||
<view class="recommended-section">
|
||||
<view class="section-header">
|
||||
<text class="section-title">推荐交易</text>
|
||||
<text class="more-btn" @click="navigateTo('/pages/market/market')">更多 ></text>
|
||||
</view>
|
||||
<view class="cattle-list">
|
||||
<view
|
||||
class="cattle-item"
|
||||
v-for="cattle in recommendedCattle"
|
||||
:key="cattle.id"
|
||||
@click="navigateTo(`/pages/detail/detail?id=${cattle.id}`)"
|
||||
>
|
||||
<image :src="cattle.image" class="cattle-image" mode="aspectFill"></image>
|
||||
<view class="cattle-info">
|
||||
<text class="cattle-breed">{{ cattle.breed }}</text>
|
||||
<text class="cattle-age">{{ cattle.age }}月龄</text>
|
||||
<text class="cattle-weight">{{ cattle.weight }}kg</text>
|
||||
<view class="cattle-price">
|
||||
<text class="price-label">价格:</text>
|
||||
<text class="price-value">¥{{ cattle.price }}</text>
|
||||
</view>
|
||||
<view class="cattle-location">
|
||||
<uni-icons type="location" size="12" color="#999"></uni-icons>
|
||||
<text class="location-text">{{ cattle.location }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 筛选弹窗 -->
|
||||
<uni-popup ref="filterPopup" type="bottom" :mask-click="false">
|
||||
<view class="filter-popup">
|
||||
<view class="popup-header">
|
||||
<text class="popup-title">筛选条件</text>
|
||||
<text class="popup-close" @click="showFilter = false">关闭</text>
|
||||
</view>
|
||||
<view class="filter-content">
|
||||
<!-- 品种筛选 -->
|
||||
<view class="filter-group">
|
||||
<text class="filter-label">品种</text>
|
||||
<view class="filter-options">
|
||||
<text
|
||||
class="filter-option"
|
||||
:class="{ active: selectedBreed === breed }"
|
||||
v-for="breed in breeds"
|
||||
:key="breed"
|
||||
@click="selectedBreed = breed"
|
||||
>
|
||||
{{ breed }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 价格筛选 -->
|
||||
<view class="filter-group">
|
||||
<text class="filter-label">价格范围</text>
|
||||
<view class="price-range">
|
||||
<input
|
||||
type="number"
|
||||
placeholder="最低价"
|
||||
v-model="priceRange.min"
|
||||
class="price-input"
|
||||
/>
|
||||
<text class="price-separator">-</text>
|
||||
<input
|
||||
type="number"
|
||||
placeholder="最高价"
|
||||
v-model="priceRange.max"
|
||||
class="price-input"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 年龄筛选 -->
|
||||
<view class="filter-group">
|
||||
<text class="filter-label">年龄</text>
|
||||
<view class="filter-options">
|
||||
<text
|
||||
class="filter-option"
|
||||
:class="{ active: selectedAge === age }"
|
||||
v-for="age in ageRanges"
|
||||
:key="age"
|
||||
@click="selectedAge = age"
|
||||
>
|
||||
{{ age }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="popup-footer">
|
||||
<button class="reset-btn" @click="resetFilter">重置</button>
|
||||
<button class="confirm-btn" @click="applyFilter">确定</button>
|
||||
</view>
|
||||
</view>
|
||||
</uni-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Index',
|
||||
|
||||
data() {
|
||||
return {
|
||||
bannerList: [
|
||||
{
|
||||
id: 1,
|
||||
title: '优质肉牛交易',
|
||||
description: '精选优质肉牛,品质保证',
|
||||
image: '/static/images/banner1.jpg',
|
||||
url: '/pages/market/list?category=meat'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: '奶牛专区',
|
||||
description: '高产奶牛,产奶量高',
|
||||
image: '/static/images/banner2.jpg',
|
||||
url: '/pages/market/list?category=dairy'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: '种牛繁育',
|
||||
description: '优良种牛,繁育首选',
|
||||
image: '/static/images/banner3.jpg',
|
||||
url: '/pages/market/list?category=breeding'
|
||||
}
|
||||
],
|
||||
categoryList: [
|
||||
{
|
||||
id: 1,
|
||||
name: '肉牛',
|
||||
icon: '/static/images/category-meat.png',
|
||||
type: 'meat'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '奶牛',
|
||||
icon: '/static/images/category-dairy.png',
|
||||
type: 'dairy'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: '种牛',
|
||||
icon: '/static/images/category-breeding.png',
|
||||
type: 'breeding'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: '小牛',
|
||||
icon: '/static/images/category-calf.png',
|
||||
type: 'calf'
|
||||
}
|
||||
],
|
||||
hotList: [],
|
||||
latestList: [],
|
||||
loadStatus: 'loading',
|
||||
loadText: {
|
||||
contentdown: '上拉显示更多',
|
||||
contentrefresh: '正在加载...',
|
||||
contentnomore: '没有更多数据了'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
this.initPage()
|
||||
},
|
||||
|
||||
onShow() {
|
||||
this.refreshData()
|
||||
},
|
||||
|
||||
onPullDownRefresh() {
|
||||
this.refreshData().finally(() => {
|
||||
uni.stopPullDownRefresh()
|
||||
})
|
||||
},
|
||||
|
||||
methods: {
|
||||
// 初始化页面
|
||||
async initPage() {
|
||||
try {
|
||||
await this.loadData()
|
||||
} catch (error) {
|
||||
console.error('初始化页面失败:', error)
|
||||
}
|
||||
},
|
||||
|
||||
// 加载数据
|
||||
async loadData() {
|
||||
this.loadStatus = 'loading'
|
||||
|
||||
try {
|
||||
await Promise.all([
|
||||
this.loadHotProducts(),
|
||||
this.loadLatestProducts()
|
||||
])
|
||||
|
||||
this.loadStatus = 'noMore'
|
||||
} catch (error) {
|
||||
console.error('加载数据失败:', error)
|
||||
this.loadStatus = 'noMore'
|
||||
uni.showToast({
|
||||
title: '加载数据失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
// 刷新数据
|
||||
async refreshData() {
|
||||
return this.loadData()
|
||||
},
|
||||
|
||||
// 加载热门商品
|
||||
async loadHotProducts() {
|
||||
try {
|
||||
const res = await this.$request({
|
||||
url: '/products/hot',
|
||||
method: 'GET',
|
||||
data: {
|
||||
limit: 10
|
||||
}
|
||||
})
|
||||
|
||||
if (res.data && res.data.list) {
|
||||
this.hotList = res.data.list
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载热门商品失败:', error)
|
||||
}
|
||||
},
|
||||
|
||||
// 加载最新商品
|
||||
async loadLatestProducts() {
|
||||
try {
|
||||
const res = await this.$request({
|
||||
url: '/products/latest',
|
||||
method: 'GET',
|
||||
data: {
|
||||
limit: 10
|
||||
}
|
||||
})
|
||||
|
||||
if (res.data && res.data.list) {
|
||||
this.latestList = res.data.list
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载最新商品失败:', error)
|
||||
}
|
||||
},
|
||||
|
||||
// 轮播图点击
|
||||
onBannerClick(banner) {
|
||||
if (banner.url) {
|
||||
uni.navigateTo({
|
||||
url: banner.url
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
// 分类点击
|
||||
onCategoryClick(category) {
|
||||
uni.navigateTo({
|
||||
url: `/pages/market/list?category=${category.type}`
|
||||
})
|
||||
},
|
||||
|
||||
// 搜索点击
|
||||
onSearchClick() {
|
||||
uni.navigateTo({
|
||||
url: '/pages/search/index'
|
||||
})
|
||||
},
|
||||
|
||||
// 商品点击
|
||||
onProductClick(product) {
|
||||
uni.navigateTo({
|
||||
url: `/pages/market/detail?id=${product.id}`
|
||||
})
|
||||
},
|
||||
|
||||
// 查看更多
|
||||
onViewMore(type) {
|
||||
let url = '/pages/market/list'
|
||||
if (type === 'hot') {
|
||||
url += '?sort=hot'
|
||||
} else if (type === 'latest') {
|
||||
url += '?sort=latest'
|
||||
}
|
||||
|
||||
uni.switchTab({
|
||||
url: '/pages/market/list'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page-container {
|
||||
min-height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
/* 轮播图 */
|
||||
.banner-section {
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.banner-swiper {
|
||||
height: 400rpx;
|
||||
border-radius: 0 0 20rpx 20rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.banner-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.banner-overlay {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: linear-gradient(transparent, rgba(0, 0, 0, 0.6));
|
||||
padding: 40rpx 30rpx 30rpx;
|
||||
|
||||
.banner-title {
|
||||
display: block;
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.banner-desc {
|
||||
font-size: 28rpx;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
}
|
||||
|
||||
/* 分类导航 */
|
||||
.category-section {
|
||||
margin: 0 30rpx 30rpx;
|
||||
}
|
||||
|
||||
.category-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.category-item {
|
||||
background: #ffffff;
|
||||
border-radius: 16rpx;
|
||||
padding: 30rpx 20rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.1);
|
||||
|
||||
.category-icon {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
margin-bottom: 16rpx;
|
||||
|
||||
.icon-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.category-name {
|
||||
font-size: 28rpx;
|
||||
color: #333333;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
/* 搜索栏 */
|
||||
.search-section {
|
||||
margin: 0 30rpx 30rpx;
|
||||
}
|
||||
|
||||
.search-bar {
|
||||
background: #ffffff;
|
||||
border-radius: 50rpx;
|
||||
padding: 20rpx 30rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.1);
|
||||
|
||||
.search-icon {
|
||||
font-size: 32rpx;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.search-placeholder {
|
||||
flex: 1;
|
||||
font-size: 28rpx;
|
||||
color: #999999;
|
||||
}
|
||||
}
|
||||
|
||||
/* 推荐区域 */
|
||||
.recommend-section {
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 30rpx 20rpx;
|
||||
|
||||
.section-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.more-text {
|
||||
font-size: 28rpx;
|
||||
color: #FF6B35;
|
||||
}
|
||||
}
|
||||
|
||||
.recommend-scroll {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.recommend-list {
|
||||
display: flex;
|
||||
padding: 0 30rpx;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.recommend-item {
|
||||
width: 280rpx;
|
||||
background: #ffffff;
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.1);
|
||||
flex-shrink: 0;
|
||||
|
||||
.product-image {
|
||||
width: 100%;
|
||||
height: 200rpx;
|
||||
}
|
||||
|
||||
.product-info {
|
||||
padding: 20rpx;
|
||||
|
||||
.product-title {
|
||||
display: block;
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
margin-bottom: 10rpx;
|
||||
@extend .ellipsis;
|
||||
}
|
||||
|
||||
.product-price {
|
||||
display: block;
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #FF6B35;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.product-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8rpx;
|
||||
|
||||
.tag {
|
||||
background: #f0f0f0;
|
||||
color: #666666;
|
||||
font-size: 22rpx;
|
||||
padding: 4rpx 8rpx;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 最新发布 */
|
||||
.latest-section {
|
||||
margin: 0 30rpx 30rpx;
|
||||
}
|
||||
|
||||
.latest-list {
|
||||
background: #ffffff;
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.latest-item {
|
||||
display: flex;
|
||||
padding: 20rpx;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.item-image {
|
||||
width: 160rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 12rpx;
|
||||
margin-right: 20rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.item-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
|
||||
.item-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
margin-bottom: 8rpx;
|
||||
@extend .ellipsis;
|
||||
}
|
||||
|
||||
.item-desc {
|
||||
font-size: 28rpx;
|
||||
color: #666666;
|
||||
margin-bottom: 8rpx;
|
||||
@extend .ellipsis;
|
||||
}
|
||||
|
||||
.item-meta {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.item-price {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #FF6B35;
|
||||
}
|
||||
|
||||
.item-location {
|
||||
font-size: 24rpx;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.item-time {
|
||||
font-size: 24rpx;
|
||||
color: #999999;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
147
mini_program/cattle-trading/pages/index/index.wxml
Normal file
147
mini_program/cattle-trading/pages/index/index.wxml
Normal file
@@ -0,0 +1,147 @@
|
||||
<!--牛只交易首页-->
|
||||
<view class="page-container">
|
||||
<!-- 顶部搜索栏 -->
|
||||
<view class="header-search">
|
||||
<view class="search-bar" bindtap="onSearch">
|
||||
<image class="search-icon" src="/images/icons/search.png" mode="aspectFit"></image>
|
||||
<text class="search-placeholder">搜索牛只品种、价格...</text>
|
||||
</view>
|
||||
<button class="publish-btn" bindtap="onPublish">发布</button>
|
||||
</view>
|
||||
|
||||
<!-- 轮播图 -->
|
||||
<swiper
|
||||
class="banner-swiper"
|
||||
indicator-dots
|
||||
autoplay
|
||||
interval="5000"
|
||||
duration="500"
|
||||
wx:if="{{bannerList.length > 0}}"
|
||||
>
|
||||
<swiper-item
|
||||
wx:for="{{bannerList}}"
|
||||
wx:key="id"
|
||||
data-banner="{{item}}"
|
||||
bindtap="onBannerTap"
|
||||
>
|
||||
<image class="banner-image" src="{{item.image}}" mode="aspectFill"></image>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
|
||||
<!-- 市场统计 -->
|
||||
<view class="market-stats">
|
||||
<view class="stats-item">
|
||||
<text class="stats-number">{{formatNumber(marketStats.totalProducts)}}</text>
|
||||
<text class="stats-label">在售商品</text>
|
||||
</view>
|
||||
<view class="stats-item">
|
||||
<text class="stats-number">{{formatNumber(marketStats.todayDeals)}}</text>
|
||||
<text class="stats-label">今日成交</text>
|
||||
</view>
|
||||
<view class="stats-item">
|
||||
<text class="stats-number">{{formatPrice(marketStats.avgPrice)}}</text>
|
||||
<text class="stats-label">平均价格</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 分类导航 -->
|
||||
<view class="category-nav" wx:if="{{categoryList.length > 0}}">
|
||||
<scroll-view class="category-scroll" scroll-x>
|
||||
<view class="category-list">
|
||||
<view
|
||||
class="category-item"
|
||||
wx:for="{{categoryList}}"
|
||||
wx:key="id"
|
||||
data-category="{{item}}"
|
||||
bindtap="onCategoryTap"
|
||||
>
|
||||
<image class="category-icon" src="{{item.icon}}" mode="aspectFit"></image>
|
||||
<text class="category-name">{{item.name}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<!-- 热门商品 -->
|
||||
<view class="section" wx:if="{{hotProducts.length > 0}}">
|
||||
<view class="section-header">
|
||||
<text class="section-title">热门推荐</text>
|
||||
<text class="section-more" bindtap="onViewMoreHot">查看更多</text>
|
||||
</view>
|
||||
<view class="hot-products">
|
||||
<view class="product-grid">
|
||||
<view
|
||||
class="product-item"
|
||||
wx:for="{{hotProducts}}"
|
||||
wx:key="id"
|
||||
data-product="{{item}}"
|
||||
bindtap="onProductTap"
|
||||
>
|
||||
<image class="product-image" src="{{item.cover_image}}" mode="aspectFill"></image>
|
||||
<view class="product-info">
|
||||
<text class="product-title">{{item.title}}</text>
|
||||
<text class="product-breed">{{item.breed}}</text>
|
||||
<view class="product-price-row">
|
||||
<text class="product-price">{{formatPrice(item.price)}}</text>
|
||||
<text class="product-location">{{item.location}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 最新商品 -->
|
||||
<view class="section" wx:if="{{recentProducts.length > 0}}">
|
||||
<view class="section-header">
|
||||
<text class="section-title">最新发布</text>
|
||||
<text class="section-more" bindtap="onViewMoreRecent">查看更多</text>
|
||||
</view>
|
||||
<view class="recent-products">
|
||||
<view
|
||||
class="recent-item"
|
||||
wx:for="{{recentProducts}}"
|
||||
wx:key="id"
|
||||
data-product="{{item}}"
|
||||
bindtap="onProductTap"
|
||||
>
|
||||
<image class="recent-image" src="{{item.cover_image}}" mode="aspectFill"></image>
|
||||
<view class="recent-info">
|
||||
<text class="recent-title">{{item.title}}</text>
|
||||
<text class="recent-breed">{{item.breed}} · {{item.age}}月龄</text>
|
||||
<view class="recent-meta">
|
||||
<text class="recent-price">{{formatPrice(item.price)}}</text>
|
||||
<text class="recent-time">{{formatTime(item.created_at)}}</text>
|
||||
</view>
|
||||
<text class="recent-location">{{item.location}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 快捷操作 -->
|
||||
<view class="quick-actions">
|
||||
<button class="action-btn primary" bindtap="onViewAllProducts">
|
||||
<image src="/images/icons/market.png" mode="aspectFit"></image>
|
||||
<text>浏览市场</text>
|
||||
</button>
|
||||
<button class="action-btn secondary" bindtap="onPublish">
|
||||
<image src="/images/icons/publish.png" mode="aspectFit"></image>
|
||||
<text>发布商品</text>
|
||||
</button>
|
||||
</view>
|
||||
|
||||
<!-- 加载状态 -->
|
||||
<view class="loading" wx:if="{{loading}}">
|
||||
<view class="loading-spinner"></view>
|
||||
<text>加载中...</text>
|
||||
</view>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<view class="empty" wx:if="{{!loading && hotProducts.length === 0 && recentProducts.length === 0}}">
|
||||
<image class="empty-icon" src="/images/empty-market.png" mode="aspectFit"></image>
|
||||
<text class="empty-text">暂无商品</text>
|
||||
<text class="empty-desc">成为第一个发布商品的用户</text>
|
||||
<button class="btn btn-primary mt-3" bindtap="onPublish">发布商品</button>
|
||||
</view>
|
||||
</view>
|
||||
419
mini_program/cattle-trading/pages/index/index.wxss
Normal file
419
mini_program/cattle-trading/pages/index/index.wxss
Normal file
@@ -0,0 +1,419 @@
|
||||
/* 牛只交易首页样式 */
|
||||
.page-container {
|
||||
min-height: 100vh;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
/* 顶部搜索栏 */
|
||||
.header-search {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 20rpx 24rpx;
|
||||
background: #2E8B57;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.search-bar {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
border-radius: 24rpx;
|
||||
padding: 0 20rpx;
|
||||
height: 72rpx;
|
||||
}
|
||||
|
||||
.search-icon {
|
||||
width: 28rpx;
|
||||
height: 28rpx;
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
|
||||
.search-placeholder {
|
||||
flex: 1;
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.publish-btn {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
color: #fff;
|
||||
border: 1rpx solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 20rpx;
|
||||
padding: 16rpx 24rpx;
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 轮播图 */
|
||||
.banner-swiper {
|
||||
height: 320rpx;
|
||||
margin: 0 24rpx 20rpx;
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.banner-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* 市场统计 */
|
||||
.market-stats {
|
||||
display: flex;
|
||||
background: #fff;
|
||||
margin: 0 24rpx 20rpx;
|
||||
border-radius: 16rpx;
|
||||
padding: 32rpx 24rpx;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.stats-item {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.stats-number {
|
||||
font-size: 36rpx;
|
||||
font-weight: 700;
|
||||
color: #2E8B57;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.stats-label {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
/* 分类导航 */
|
||||
.category-nav {
|
||||
margin: 0 0 20rpx;
|
||||
}
|
||||
|
||||
.category-scroll {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.category-list {
|
||||
display: inline-flex;
|
||||
padding: 0 24rpx;
|
||||
gap: 32rpx;
|
||||
}
|
||||
|
||||
.category-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
min-width: 120rpx;
|
||||
}
|
||||
|
||||
.category-icon {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
background: #fff;
|
||||
border-radius: 50%;
|
||||
padding: 16rpx;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.category-name {
|
||||
font-size: 24rpx;
|
||||
color: #333;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* 区块样式 */
|
||||
.section {
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 24rpx 20rpx;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.section-more {
|
||||
font-size: 24rpx;
|
||||
color: #2E8B57;
|
||||
}
|
||||
|
||||
/* 热门商品 */
|
||||
.hot-products {
|
||||
padding: 0 24rpx;
|
||||
}
|
||||
|
||||
.product-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.product-item {
|
||||
background: #fff;
|
||||
border-radius: 12rpx;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.08);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.product-item:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.product-image {
|
||||
width: 100%;
|
||||
height: 240rpx;
|
||||
}
|
||||
|
||||
.product-info {
|
||||
padding: 16rpx;
|
||||
}
|
||||
|
||||
.product-title {
|
||||
display: block;
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
margin-bottom: 8rpx;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.product-breed {
|
||||
display: block;
|
||||
font-size: 22rpx;
|
||||
color: #666;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.product-price-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.product-price {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #e74c3c;
|
||||
}
|
||||
|
||||
.product-location {
|
||||
font-size: 20rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* 最新商品 */
|
||||
.recent-products {
|
||||
padding: 0 24rpx;
|
||||
}
|
||||
|
||||
.recent-item {
|
||||
display: flex;
|
||||
background: #fff;
|
||||
border-radius: 12rpx;
|
||||
padding: 20rpx;
|
||||
margin-bottom: 16rpx;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.08);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.recent-item:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.recent-image {
|
||||
width: 160rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 8rpx;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.recent-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.recent-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
margin-bottom: 8rpx;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.recent-breed {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.recent-meta {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.recent-price {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #e74c3c;
|
||||
}
|
||||
|
||||
.recent-time {
|
||||
font-size: 20rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.recent-location {
|
||||
font-size: 22rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* 快捷操作 */
|
||||
.quick-actions {
|
||||
display: flex;
|
||||
gap: 16rpx;
|
||||
padding: 0 24rpx 40rpx;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 32rpx 20rpx;
|
||||
border-radius: 16rpx;
|
||||
border: none;
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.action-btn.primary {
|
||||
background: #2E8B57;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.action-btn.secondary {
|
||||
background: #fff;
|
||||
color: #333;
|
||||
border: 1rpx solid #e9ecef;
|
||||
}
|
||||
|
||||
.action-btn image {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
/* 加载状态 */
|
||||
.loading {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 80rpx 40rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
border: 3rpx solid #f0f0f0;
|
||||
border-top: 3rpx solid #2E8B57;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
/* 空状态 */
|
||||
.empty {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 80rpx 40rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
margin-bottom: 32rpx;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 32rpx;
|
||||
font-weight: 500;
|
||||
color: #666;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.empty-desc {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
line-height: 1.5;
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 20rpx 40rpx;
|
||||
border-radius: 24rpx;
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: #2E8B57;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.mt-3 {
|
||||
margin-top: 32rpx;
|
||||
}
|
||||
|
||||
/* 动画 */
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* 响应式适配 */
|
||||
@media (max-width: 750rpx) {
|
||||
.product-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.recent-item {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.recent-image {
|
||||
width: 100%;
|
||||
height: 200rpx;
|
||||
margin-right: 0;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.quick-actions {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
1295
mini_program/cattle-trading/pages/market/analysis.vue
Normal file
1295
mini_program/cattle-trading/pages/market/analysis.vue
Normal file
File diff suppressed because it is too large
Load Diff
693
mini_program/cattle-trading/pages/market/market.vue
Normal file
693
mini_program/cattle-trading/pages/market/market.vue
Normal file
@@ -0,0 +1,693 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<!-- 顶部搜索和筛选 -->
|
||||
<view class="header-section">
|
||||
<view class="search-bar">
|
||||
<view class="search-input">
|
||||
<uni-icons type="search" size="20" color="#999"></uni-icons>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="搜索牛只品种、价格等"
|
||||
v-model="searchKeyword"
|
||||
@confirm="handleSearch"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="filter-bar">
|
||||
<view class="filter-item" @click="showSortPopup = true">
|
||||
<text class="filter-text">{{ sortText }}</text>
|
||||
<uni-icons type="arrowdown" size="16" color="#666"></uni-icons>
|
||||
</view>
|
||||
<view class="filter-item" @click="showFilterPopup = true">
|
||||
<text class="filter-text">筛选</text>
|
||||
<uni-icons type="tune" size="16" color="#666"></uni-icons>
|
||||
</view>
|
||||
<view class="filter-item" @click="toggleViewMode">
|
||||
<uni-icons :type="viewMode === 'grid' ? 'list' : 'grid'" size="16" color="#666"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 牛只列表 -->
|
||||
<view class="cattle-list" :class="viewMode">
|
||||
<view
|
||||
class="cattle-item"
|
||||
v-for="cattle in cattleList"
|
||||
:key="cattle.id"
|
||||
@click="navigateToDetail(cattle.id)"
|
||||
>
|
||||
<image :src="cattle.image" class="cattle-image" mode="aspectFill"></image>
|
||||
<view class="cattle-info">
|
||||
<view class="cattle-header">
|
||||
<text class="cattle-breed">{{ cattle.breed }}</text>
|
||||
<view class="cattle-status" :class="cattle.status">
|
||||
{{ getStatusText(cattle.status) }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="cattle-details">
|
||||
<view class="detail-row">
|
||||
<text class="detail-label">年龄:</text>
|
||||
<text class="detail-value">{{ cattle.age }}月龄</text>
|
||||
</view>
|
||||
<view class="detail-row">
|
||||
<text class="detail-label">重量:</text>
|
||||
<text class="detail-value">{{ cattle.weight }}kg</text>
|
||||
</view>
|
||||
<view class="detail-row">
|
||||
<text class="detail-label">性别:</text>
|
||||
<text class="detail-value">{{ cattle.gender }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="cattle-price">
|
||||
<text class="price-label">价格:</text>
|
||||
<text class="price-value">¥{{ cattle.price }}</text>
|
||||
</view>
|
||||
|
||||
<view class="cattle-footer">
|
||||
<view class="location-info">
|
||||
<uni-icons type="location" size="12" color="#999"></uni-icons>
|
||||
<text class="location-text">{{ cattle.location }}</text>
|
||||
</view>
|
||||
<text class="publish-time">{{ formatTime(cattle.publishTime) }}</text>
|
||||
</view>
|
||||
|
||||
<view class="cattle-actions" v-if="viewMode === 'list'">
|
||||
<button class="action-btn contact" @click.stop="contactSeller(cattle)">
|
||||
联系卖家
|
||||
</button>
|
||||
<button class="action-btn favorite" @click.stop="toggleFavorite(cattle)">
|
||||
{{ cattle.isFavorite ? '已收藏' : '收藏' }}
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 加载更多 -->
|
||||
<uni-load-more :status="loadStatus" :content-text="loadText"></uni-load-more>
|
||||
|
||||
<!-- 排序弹窗 -->
|
||||
<uni-popup ref="sortPopup" type="bottom" :mask-click="true">
|
||||
<view class="sort-popup">
|
||||
<view class="popup-header">
|
||||
<text class="popup-title">排序方式</text>
|
||||
</view>
|
||||
<view class="sort-options">
|
||||
<view
|
||||
class="sort-option"
|
||||
:class="{ active: currentSort === option.value }"
|
||||
v-for="option in sortOptions"
|
||||
:key="option.value"
|
||||
@click="selectSort(option.value)"
|
||||
>
|
||||
<text class="option-text">{{ option.label }}</text>
|
||||
<uni-icons
|
||||
v-if="currentSort === option.value"
|
||||
type="checkmarkempty"
|
||||
size="20"
|
||||
color="#007AFF"
|
||||
></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</uni-popup>
|
||||
|
||||
<!-- 筛选弹窗 -->
|
||||
<uni-popup ref="filterPopup" type="bottom" :mask-click="false">
|
||||
<view class="filter-popup">
|
||||
<view class="popup-header">
|
||||
<text class="popup-title">筛选条件</text>
|
||||
<text class="popup-close" @click="showFilterPopup = false">关闭</text>
|
||||
</view>
|
||||
<scroll-view class="filter-content" scroll-y="true">
|
||||
<!-- 品种筛选 -->
|
||||
<view class="filter-group">
|
||||
<text class="filter-label">品种</text>
|
||||
<view class="filter-options">
|
||||
<text
|
||||
class="filter-option"
|
||||
:class="{ active: filterParams.breed === breed }"
|
||||
v-for="breed in breedOptions"
|
||||
:key="breed"
|
||||
@click="selectBreed(breed)"
|
||||
>
|
||||
{{ breed }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 价格筛选 -->
|
||||
<view class="filter-group">
|
||||
<text class="filter-label">价格范围</text>
|
||||
<view class="price-range">
|
||||
<input
|
||||
type="number"
|
||||
placeholder="最低价"
|
||||
v-model="filterParams.priceMin"
|
||||
class="price-input"
|
||||
/>
|
||||
<text class="price-separator">-</text>
|
||||
<input
|
||||
type="number"
|
||||
placeholder="最高价"
|
||||
v-model="filterParams.priceMax"
|
||||
class="price-input"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 年龄筛选 -->
|
||||
<view class="filter-group">
|
||||
<text class="filter-label">年龄</text>
|
||||
<view class="filter-options">
|
||||
<text
|
||||
class="filter-option"
|
||||
:class="{ active: filterParams.age === age }"
|
||||
v-for="age in ageOptions"
|
||||
:key="age"
|
||||
@click="selectAge(age)"
|
||||
>
|
||||
{{ age }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 重量筛选 -->
|
||||
<view class="filter-group">
|
||||
<text class="filter-label">重量</text>
|
||||
<view class="filter-options">
|
||||
<text
|
||||
class="filter-option"
|
||||
:class="{ active: filterParams.weight === weight }"
|
||||
v-for="weight in weightOptions"
|
||||
:key="weight"
|
||||
@click="selectWeight(weight)"
|
||||
>
|
||||
{{ weight }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 地区筛选 -->
|
||||
<view class="filter-group">
|
||||
<text class="filter-label">地区</text>
|
||||
<view class="filter-options">
|
||||
<text
|
||||
class="filter-option"
|
||||
:class="{ active: filterParams.location === location }"
|
||||
v-for="location in locationOptions"
|
||||
:key="location"
|
||||
@click="selectLocation(location)"
|
||||
>
|
||||
{{ location }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="popup-footer">
|
||||
<button class="reset-btn" @click="resetFilter">重置</button>
|
||||
<button class="confirm-btn" @click="applyFilter">确定</button>
|
||||
</view>
|
||||
</view>
|
||||
</uni-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, reactive, onMounted, computed } from 'vue'
|
||||
import { useRequest } from '@/common/utils/request'
|
||||
import { showToast, showLoading, hideLoading } from '@/common/utils/uni-helper'
|
||||
|
||||
export default {
|
||||
name: 'MarketPage',
|
||||
setup() {
|
||||
const request = useRequest()
|
||||
|
||||
// 响应式数据
|
||||
const searchKeyword = ref('')
|
||||
const activeCategory = ref(0)
|
||||
const productList = ref([])
|
||||
const loading = ref(false)
|
||||
const refreshing = ref(false)
|
||||
const hasMore = ref(true)
|
||||
const currentPage = ref(1)
|
||||
const showFilter = ref(false)
|
||||
|
||||
// 分类数据
|
||||
const categories = ref([
|
||||
{ id: 0, name: '全部' },
|
||||
{ id: 1, name: '肉牛' },
|
||||
{ id: 2, name: '奶牛' },
|
||||
{ id: 3, name: '种牛' },
|
||||
{ id: 4, name: '小牛' }
|
||||
])
|
||||
|
||||
// 筛选数据
|
||||
const filterData = reactive({
|
||||
minPrice: '',
|
||||
maxPrice: '',
|
||||
breeds: [],
|
||||
region: null
|
||||
})
|
||||
|
||||
// 品种选项
|
||||
const breedOptions = ref([
|
||||
{ id: 1, name: '西门塔尔' },
|
||||
{ id: 2, name: '安格斯' },
|
||||
{ id: 3, name: '夏洛莱' },
|
||||
{ id: 4, name: '利木赞' },
|
||||
{ id: 5, name: '荷斯坦' }
|
||||
])
|
||||
|
||||
// 地区选项
|
||||
const regionOptions = ref([
|
||||
{ id: 1, name: '北京市' },
|
||||
{ id: 2, name: '上海市' },
|
||||
{ id: 3, name: '广东省' },
|
||||
{ id: 4, name: '山东省' },
|
||||
{ id: 5, name: '河南省' }
|
||||
])
|
||||
|
||||
// 获取商品列表
|
||||
const getProductList = async (isRefresh = false) => {
|
||||
if (loading.value && !isRefresh) return
|
||||
|
||||
try {
|
||||
loading.value = true
|
||||
|
||||
const params = {
|
||||
page: isRefresh ? 1 : currentPage.value,
|
||||
pageSize: 10,
|
||||
keyword: searchKeyword.value,
|
||||
category: activeCategory.value,
|
||||
...filterData
|
||||
}
|
||||
|
||||
const response = await request.get('/api/cattle/list', params)
|
||||
|
||||
if (isRefresh) {
|
||||
productList.value = response.data.list
|
||||
currentPage.value = 1
|
||||
} else {
|
||||
productList.value.push(...response.data.list)
|
||||
}
|
||||
|
||||
hasMore.value = response.data.hasMore
|
||||
currentPage.value++
|
||||
|
||||
} catch (error) {
|
||||
showToast('获取数据失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
refreshing.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 搜索
|
||||
const onSearch = () => {
|
||||
currentPage.value = 1
|
||||
getProductList(true)
|
||||
}
|
||||
|
||||
// 选择分类
|
||||
const selectCategory = (categoryId) => {
|
||||
activeCategory.value = categoryId
|
||||
currentPage.value = 1
|
||||
getProductList(true)
|
||||
}
|
||||
|
||||
// 下拉刷新
|
||||
const onRefresh = () => {
|
||||
refreshing.value = true
|
||||
getProductList(true)
|
||||
}
|
||||
|
||||
// 加载更多
|
||||
const loadMore = () => {
|
||||
if (hasMore.value && !loading.value) {
|
||||
getProductList()
|
||||
}
|
||||
}
|
||||
|
||||
// 切换品种选择
|
||||
const toggleBreed = (breedId) => {
|
||||
const index = filterData.breeds.indexOf(breedId)
|
||||
if (index > -1) {
|
||||
filterData.breeds.splice(index, 1)
|
||||
} else {
|
||||
filterData.breeds.push(breedId)
|
||||
}
|
||||
}
|
||||
|
||||
// 地区选择
|
||||
const onRegionChange = (e) => {
|
||||
filterData.region = e.detail.value
|
||||
}
|
||||
|
||||
// 重置筛选
|
||||
const resetFilter = () => {
|
||||
filterData.minPrice = ''
|
||||
filterData.maxPrice = ''
|
||||
filterData.breeds = []
|
||||
filterData.region = null
|
||||
}
|
||||
|
||||
// 应用筛选
|
||||
const applyFilter = () => {
|
||||
showFilter.value = false
|
||||
currentPage.value = 1
|
||||
getProductList(true)
|
||||
}
|
||||
|
||||
// 跳转到详情页
|
||||
const goToDetail = (productId) => {
|
||||
uni.navigateTo({
|
||||
url: `/pages/detail/detail?id=${productId}`
|
||||
})
|
||||
}
|
||||
|
||||
// 跳转到发布页
|
||||
const goToPublish = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/publish/publish'
|
||||
})
|
||||
}
|
||||
|
||||
// 页面加载
|
||||
onMounted(() => {
|
||||
getProductList(true)
|
||||
})
|
||||
|
||||
return {
|
||||
searchKeyword,
|
||||
activeCategory,
|
||||
productList,
|
||||
loading,
|
||||
refreshing,
|
||||
hasMore,
|
||||
showFilter,
|
||||
categories,
|
||||
filterData,
|
||||
breedOptions,
|
||||
regionOptions,
|
||||
onSearch,
|
||||
selectCategory,
|
||||
onRefresh,
|
||||
loadMore,
|
||||
toggleBreed,
|
||||
onRegionChange,
|
||||
resetFilter,
|
||||
applyFilter,
|
||||
goToDetail,
|
||||
goToPublish
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.market-page {
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: $bg-secondary;
|
||||
}
|
||||
|
||||
.search-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: $spacing-md;
|
||||
background-color: $white;
|
||||
gap: $spacing-sm;
|
||||
|
||||
.search-input {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color: $bg-light;
|
||||
border-radius: $border-radius-lg;
|
||||
padding: $spacing-sm $spacing-md;
|
||||
gap: $spacing-sm;
|
||||
|
||||
input {
|
||||
flex: 1;
|
||||
font-size: $font-size-base;
|
||||
}
|
||||
}
|
||||
|
||||
.filter-btn {
|
||||
padding: $spacing-sm;
|
||||
background-color: $primary-color;
|
||||
border-radius: $border-radius-md;
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
|
||||
.category-tabs {
|
||||
background-color: $white;
|
||||
white-space: nowrap;
|
||||
padding: $spacing-sm $spacing-md;
|
||||
|
||||
.category-item {
|
||||
display: inline-block;
|
||||
padding: $spacing-sm $spacing-md;
|
||||
margin-right: $spacing-sm;
|
||||
background-color: $bg-light;
|
||||
border-radius: $border-radius-full;
|
||||
font-size: $font-size-sm;
|
||||
color: $text-secondary;
|
||||
|
||||
&.active {
|
||||
background-color: $primary-color;
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.product-list {
|
||||
flex: 1;
|
||||
padding: $spacing-md;
|
||||
}
|
||||
|
||||
.product-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: $spacing-md;
|
||||
}
|
||||
|
||||
.product-card {
|
||||
background-color: $white;
|
||||
border-radius: $border-radius-lg;
|
||||
overflow: hidden;
|
||||
box-shadow: $shadow-sm;
|
||||
|
||||
.product-image {
|
||||
position: relative;
|
||||
height: 120px;
|
||||
|
||||
image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.hot-tag {
|
||||
position: absolute;
|
||||
top: $spacing-sm;
|
||||
left: $spacing-sm;
|
||||
background-color: $error-color;
|
||||
color: $white;
|
||||
padding: 2px $spacing-sm;
|
||||
border-radius: $border-radius-sm;
|
||||
font-size: $font-size-xs;
|
||||
}
|
||||
}
|
||||
|
||||
.product-info {
|
||||
padding: $spacing-md;
|
||||
|
||||
.product-title {
|
||||
font-size: $font-size-base;
|
||||
font-weight: $font-weight-medium;
|
||||
color: $text-primary;
|
||||
@include text-ellipsis(1);
|
||||
margin-bottom: $spacing-xs;
|
||||
}
|
||||
|
||||
.product-desc {
|
||||
font-size: $font-size-sm;
|
||||
color: $text-secondary;
|
||||
@include text-ellipsis(2);
|
||||
margin-bottom: $spacing-sm;
|
||||
}
|
||||
|
||||
.product-tags {
|
||||
margin-bottom: $spacing-sm;
|
||||
|
||||
.tag {
|
||||
display: inline-block;
|
||||
background-color: $bg-light;
|
||||
color: $text-muted;
|
||||
padding: 2px $spacing-xs;
|
||||
border-radius: $border-radius-sm;
|
||||
font-size: $font-size-xs;
|
||||
margin-right: $spacing-xs;
|
||||
}
|
||||
}
|
||||
|
||||
.product-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.price {
|
||||
.currency {
|
||||
font-size: $font-size-sm;
|
||||
color: $error-color;
|
||||
}
|
||||
|
||||
.amount {
|
||||
font-size: $font-size-lg;
|
||||
font-weight: $font-weight-bold;
|
||||
color: $error-color;
|
||||
}
|
||||
|
||||
.unit {
|
||||
font-size: $font-size-sm;
|
||||
color: $text-muted;
|
||||
}
|
||||
}
|
||||
|
||||
.location {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
font-size: $font-size-xs;
|
||||
color: $text-muted;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.loading-more {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: $spacing-lg;
|
||||
gap: $spacing-sm;
|
||||
color: $text-muted;
|
||||
font-size: $font-size-sm;
|
||||
}
|
||||
|
||||
.no-more {
|
||||
text-align: center;
|
||||
padding: $spacing-lg;
|
||||
color: $text-muted;
|
||||
font-size: $font-size-sm;
|
||||
}
|
||||
|
||||
.filter-popup {
|
||||
background-color: $white;
|
||||
border-radius: $border-radius-lg $border-radius-lg 0 0;
|
||||
max-height: 70vh;
|
||||
|
||||
.filter-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: $spacing-lg;
|
||||
border-bottom: 1px solid $border-color;
|
||||
|
||||
.filter-actions {
|
||||
display: flex;
|
||||
gap: $spacing-lg;
|
||||
|
||||
text {
|
||||
color: $primary-color;
|
||||
font-size: $font-size-base;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.filter-content {
|
||||
padding: $spacing-lg;
|
||||
max-height: 50vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.filter-section {
|
||||
margin-bottom: $spacing-xl;
|
||||
|
||||
.section-title {
|
||||
font-size: $font-size-base;
|
||||
font-weight: $font-weight-medium;
|
||||
color: $text-primary;
|
||||
margin-bottom: $spacing-md;
|
||||
}
|
||||
|
||||
.price-range {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: $spacing-sm;
|
||||
|
||||
.price-input {
|
||||
flex: 1;
|
||||
padding: $spacing-sm;
|
||||
border: 1px solid $border-color;
|
||||
border-radius: $border-radius-md;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.separator {
|
||||
color: $text-muted;
|
||||
}
|
||||
}
|
||||
|
||||
.breed-options {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: $spacing-sm;
|
||||
|
||||
.breed-item {
|
||||
padding: $spacing-sm $spacing-md;
|
||||
background-color: $bg-light;
|
||||
border-radius: $border-radius-full;
|
||||
font-size: $font-size-sm;
|
||||
color: $text-secondary;
|
||||
|
||||
&.active {
|
||||
background-color: $primary-color;
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.picker-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: $spacing-md;
|
||||
background-color: $bg-light;
|
||||
border-radius: $border-radius-md;
|
||||
color: $text-primary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.fab-button {
|
||||
position: fixed;
|
||||
right: $spacing-lg;
|
||||
bottom: 100px;
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
background-color: $primary-color;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: $shadow-lg;
|
||||
z-index: 999;
|
||||
}
|
||||
</style>
|
||||
604
mini_program/cattle-trading/pages/profile/profile.vue
Normal file
604
mini_program/cattle-trading/pages/profile/profile.vue
Normal file
@@ -0,0 +1,604 @@
|
||||
<template>
|
||||
<view class="profile-page">
|
||||
<!-- 用户信息卡片 -->
|
||||
<view class="user-card">
|
||||
<view class="user-info">
|
||||
<image
|
||||
:src="userInfo.avatar || '/static/images/default-avatar.png'"
|
||||
class="user-avatar"
|
||||
@click="chooseAvatar"
|
||||
/>
|
||||
<view class="user-details">
|
||||
<view class="user-name">{{ userInfo.nickname || '未设置昵称' }}</view>
|
||||
<view class="user-phone">{{ userInfo.phone || '未绑定手机' }}</view>
|
||||
<view class="user-location">
|
||||
<text class="location-icon">📍</text>
|
||||
<text>{{ userInfo.location || '未设置地区' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="edit-btn" @click="editProfile">
|
||||
<text class="edit-icon">✏️</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 统计信息 -->
|
||||
<view class="stats-row">
|
||||
<view class="stat-item" @click="goToMyPublish">
|
||||
<view class="stat-number">{{ stats.publishCount }}</view>
|
||||
<view class="stat-label">发布</view>
|
||||
</view>
|
||||
<view class="stat-item" @click="goToMyFavorites">
|
||||
<view class="stat-number">{{ stats.favoriteCount }}</view>
|
||||
<view class="stat-label">收藏</view>
|
||||
</view>
|
||||
<view class="stat-item" @click="goToMyOrders">
|
||||
<view class="stat-number">{{ stats.orderCount }}</view>
|
||||
<view class="stat-label">交易</view>
|
||||
</view>
|
||||
<view class="stat-item" @click="goToMyFollows">
|
||||
<view class="stat-number">{{ stats.followCount }}</view>
|
||||
<view class="stat-label">关注</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 快捷功能 -->
|
||||
<view class="quick-actions">
|
||||
<view class="action-item" @click="goToPublish">
|
||||
<view class="action-icon">📝</view>
|
||||
<view class="action-text">发布牛只</view>
|
||||
</view>
|
||||
<view class="action-item" @click="goToSearch">
|
||||
<view class="action-icon">🔍</view>
|
||||
<view class="action-text">找牛只</view>
|
||||
</view>
|
||||
<view class="action-item" @click="goToMessages">
|
||||
<view class="action-icon">💬</view>
|
||||
<view class="action-text">消息</view>
|
||||
<view v-if="unreadCount > 0" class="badge">{{ unreadCount }}</view>
|
||||
</view>
|
||||
<view class="action-item" @click="goToWallet">
|
||||
<view class="action-icon">💰</view>
|
||||
<view class="action-text">钱包</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 功能菜单 -->
|
||||
<view class="menu-section">
|
||||
<view class="menu-title">我的服务</view>
|
||||
<view class="menu-list">
|
||||
<view class="menu-item" @click="goToMyPublish">
|
||||
<view class="menu-left">
|
||||
<text class="menu-icon">📋</text>
|
||||
<text class="menu-text">我的发布</text>
|
||||
</view>
|
||||
<view class="menu-right">
|
||||
<text class="menu-arrow">></text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="menu-item" @click="goToMyOrders">
|
||||
<view class="menu-left">
|
||||
<text class="menu-icon">📦</text>
|
||||
<text class="menu-text">我的交易</text>
|
||||
</view>
|
||||
<view class="menu-right">
|
||||
<text class="menu-arrow">></text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="menu-item" @click="goToMyFavorites">
|
||||
<view class="menu-left">
|
||||
<text class="menu-icon">❤️</text>
|
||||
<text class="menu-text">我的收藏</text>
|
||||
</view>
|
||||
<view class="menu-right">
|
||||
<text class="menu-arrow">></text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="menu-item" @click="goToMyFollows">
|
||||
<view class="menu-left">
|
||||
<text class="menu-icon">👥</text>
|
||||
<text class="menu-text">我的关注</text>
|
||||
</view>
|
||||
<view class="menu-right">
|
||||
<text class="menu-arrow">></text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="menu-section">
|
||||
<view class="menu-title">工具服务</view>
|
||||
<view class="menu-list">
|
||||
<view class="menu-item" @click="goToCalculator">
|
||||
<view class="menu-left">
|
||||
<text class="menu-icon">🧮</text>
|
||||
<text class="menu-text">价格计算器</text>
|
||||
</view>
|
||||
<view class="menu-right">
|
||||
<text class="menu-arrow">></text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="menu-item" @click="goToMarketPrice">
|
||||
<view class="menu-left">
|
||||
<text class="menu-icon">📈</text>
|
||||
<text class="menu-text">市场行情</text>
|
||||
</view>
|
||||
<view class="menu-right">
|
||||
<text class="menu-arrow">></text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="menu-item" @click="goToNews">
|
||||
<view class="menu-left">
|
||||
<text class="menu-icon">📰</text>
|
||||
<text class="menu-text">行业资讯</text>
|
||||
</view>
|
||||
<view class="menu-right">
|
||||
<text class="menu-arrow">></text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="menu-section">
|
||||
<view class="menu-title">设置</view>
|
||||
<view class="menu-list">
|
||||
<view class="menu-item" @click="goToSettings">
|
||||
<view class="menu-left">
|
||||
<text class="menu-icon">⚙️</text>
|
||||
<text class="menu-text">设置</text>
|
||||
</view>
|
||||
<view class="menu-right">
|
||||
<text class="menu-arrow">></text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="menu-item" @click="goToHelp">
|
||||
<view class="menu-left">
|
||||
<text class="menu-icon">❓</text>
|
||||
<text class="menu-text">帮助与反馈</text>
|
||||
</view>
|
||||
<view class="menu-right">
|
||||
<text class="menu-arrow">></text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="menu-item" @click="goToAbout">
|
||||
<view class="menu-left">
|
||||
<text class="menu-icon">ℹ️</text>
|
||||
<text class="menu-text">关于我们</text>
|
||||
</view>
|
||||
<view class="menu-right">
|
||||
<text class="menu-arrow">></text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 退出登录 -->
|
||||
<view class="logout-section">
|
||||
<button class="logout-btn" @click="logout">退出登录</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
userInfo: {
|
||||
avatar: '',
|
||||
nickname: '牛老板',
|
||||
phone: '138****8888',
|
||||
location: '山东省 济南市'
|
||||
},
|
||||
stats: {
|
||||
publishCount: 12,
|
||||
favoriteCount: 8,
|
||||
orderCount: 5,
|
||||
followCount: 23
|
||||
},
|
||||
unreadCount: 3
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
// 选择头像
|
||||
chooseAvatar() {
|
||||
uni.chooseImage({
|
||||
count: 1,
|
||||
sizeType: ['compressed'],
|
||||
sourceType: ['album', 'camera'],
|
||||
success: (res) => {
|
||||
this.userInfo.avatar = res.tempFilePaths[0]
|
||||
// 这里应该上传到服务器
|
||||
this.uploadAvatar(res.tempFilePaths[0])
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 上传头像
|
||||
uploadAvatar(filePath) {
|
||||
uni.showLoading({ title: '上传中...' })
|
||||
// 模拟上传
|
||||
setTimeout(() => {
|
||||
uni.hideLoading()
|
||||
uni.showToast({ title: '头像更新成功', icon: 'success' })
|
||||
}, 1500)
|
||||
},
|
||||
|
||||
// 编辑个人资料
|
||||
editProfile() {
|
||||
uni.navigateTo({
|
||||
url: '/pages/profile/edit-profile'
|
||||
})
|
||||
},
|
||||
|
||||
// 我的发布
|
||||
goToMyPublish() {
|
||||
uni.navigateTo({
|
||||
url: '/pages/profile/my-publish'
|
||||
})
|
||||
},
|
||||
|
||||
// 我的收藏
|
||||
goToMyFavorites() {
|
||||
uni.navigateTo({
|
||||
url: '/pages/profile/my-favorites'
|
||||
})
|
||||
},
|
||||
|
||||
// 我的交易
|
||||
goToMyOrders() {
|
||||
uni.navigateTo({
|
||||
url: '/pages/profile/my-orders'
|
||||
})
|
||||
},
|
||||
|
||||
// 我的关注
|
||||
goToMyFollows() {
|
||||
uni.navigateTo({
|
||||
url: '/pages/profile/my-follows'
|
||||
})
|
||||
},
|
||||
|
||||
// 发布牛只
|
||||
goToPublish() {
|
||||
uni.navigateTo({
|
||||
url: '/pages/publish/publish'
|
||||
})
|
||||
},
|
||||
|
||||
// 搜索
|
||||
goToSearch() {
|
||||
uni.switchTab({
|
||||
url: '/pages/market/market'
|
||||
})
|
||||
},
|
||||
|
||||
// 消息
|
||||
goToMessages() {
|
||||
uni.navigateTo({
|
||||
url: '/pages/messages/messages'
|
||||
})
|
||||
},
|
||||
|
||||
// 钱包
|
||||
goToWallet() {
|
||||
uni.navigateTo({
|
||||
url: '/pages/wallet/wallet'
|
||||
})
|
||||
},
|
||||
|
||||
// 价格计算器
|
||||
goToCalculator() {
|
||||
uni.navigateTo({
|
||||
url: '/pages/tools/calculator'
|
||||
})
|
||||
},
|
||||
|
||||
// 市场行情
|
||||
goToMarketPrice() {
|
||||
uni.navigateTo({
|
||||
url: '/pages/tools/market-price'
|
||||
})
|
||||
},
|
||||
|
||||
// 行业资讯
|
||||
goToNews() {
|
||||
uni.navigateTo({
|
||||
url: '/pages/news/news'
|
||||
})
|
||||
},
|
||||
|
||||
// 设置
|
||||
goToSettings() {
|
||||
uni.navigateTo({
|
||||
url: '/pages/settings/settings'
|
||||
})
|
||||
},
|
||||
|
||||
// 帮助与反馈
|
||||
goToHelp() {
|
||||
uni.navigateTo({
|
||||
url: '/pages/help/help'
|
||||
})
|
||||
},
|
||||
|
||||
// 关于我们
|
||||
goToAbout() {
|
||||
uni.navigateTo({
|
||||
url: '/pages/about/about'
|
||||
})
|
||||
},
|
||||
|
||||
// 退出登录
|
||||
logout() {
|
||||
uni.showModal({
|
||||
title: '确认退出',
|
||||
content: '确定要退出登录吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
// 清除用户信息
|
||||
uni.removeStorageSync('userInfo')
|
||||
uni.removeStorageSync('token')
|
||||
|
||||
// 跳转到登录页
|
||||
uni.reLaunch({
|
||||
url: '/pages/login/login'
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 加载用户信息
|
||||
loadUserInfo() {
|
||||
// 模拟从服务器获取用户信息
|
||||
const userInfo = uni.getStorageSync('userInfo')
|
||||
if (userInfo) {
|
||||
this.userInfo = { ...this.userInfo, ...userInfo }
|
||||
}
|
||||
},
|
||||
|
||||
// 加载统计数据
|
||||
loadStats() {
|
||||
// 模拟从服务器获取统计数据
|
||||
// 这里可以调用API获取真实数据
|
||||
}
|
||||
},
|
||||
|
||||
onShow() {
|
||||
this.loadUserInfo()
|
||||
this.loadStats()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.profile-page {
|
||||
min-height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
padding-bottom: 40rpx;
|
||||
}
|
||||
|
||||
/* 用户信息卡片 */
|
||||
.user-card {
|
||||
background-color: #fff;
|
||||
margin: 20rpx;
|
||||
border-radius: 16rpx;
|
||||
padding: 30rpx;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 60rpx;
|
||||
margin-right: 30rpx;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.user-details {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.user-phone {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.user-location {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.location-icon {
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
|
||||
.edit-btn {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 30rpx;
|
||||
}
|
||||
|
||||
.edit-icon {
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
/* 统计信息 */
|
||||
.stats-row {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
padding-top: 30rpx;
|
||||
border-top: 1rpx solid #eee;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 10rpx;
|
||||
}
|
||||
|
||||
.stat-number {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
/* 快捷功能 */
|
||||
.quick-actions {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
background-color: #fff;
|
||||
margin: 20rpx;
|
||||
border-radius: 16rpx;
|
||||
padding: 30rpx 20rpx;
|
||||
}
|
||||
|
||||
.action-item {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 15rpx;
|
||||
}
|
||||
|
||||
.action-icon {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 40rpx;
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
.action-text {
|
||||
font-size: 24rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.badge {
|
||||
position: absolute;
|
||||
top: -5rpx;
|
||||
right: 15rpx;
|
||||
min-width: 32rpx;
|
||||
height: 32rpx;
|
||||
background-color: #ff4757;
|
||||
color: #fff;
|
||||
border-radius: 16rpx;
|
||||
font-size: 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 8rpx;
|
||||
}
|
||||
|
||||
/* 菜单部分 */
|
||||
.menu-section {
|
||||
margin: 20rpx;
|
||||
background-color: #fff;
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.menu-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
padding: 30rpx 30rpx 20rpx;
|
||||
}
|
||||
|
||||
.menu-list {
|
||||
padding: 0 30rpx;
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 100rpx;
|
||||
border-bottom: 1rpx solid #f5f5f5;
|
||||
}
|
||||
|
||||
.menu-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.menu-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.menu-icon {
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
.menu-text {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.menu-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.menu-arrow {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* 退出登录 */
|
||||
.logout-section {
|
||||
margin: 40rpx 20rpx 20rpx;
|
||||
}
|
||||
|
||||
.logout-btn {
|
||||
width: 100%;
|
||||
height: 80rpx;
|
||||
background-color: #fff;
|
||||
color: #ff4757;
|
||||
border: 1rpx solid #ff4757;
|
||||
border-radius: 40rpx;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.logout-btn:active {
|
||||
background-color: #ff4757;
|
||||
color: #fff;
|
||||
}
|
||||
</style>
|
||||
728
mini_program/cattle-trading/pages/publish/publish.vue
Normal file
728
mini_program/cattle-trading/pages/publish/publish.vue
Normal file
@@ -0,0 +1,728 @@
|
||||
<template>
|
||||
<view class="publish-page">
|
||||
<!-- 顶部导航 -->
|
||||
<view class="nav-bar">
|
||||
<view class="nav-left" @click="goBack">
|
||||
<text class="nav-icon">←</text>
|
||||
</view>
|
||||
<view class="nav-title">发布牛只</view>
|
||||
<view class="nav-right"></view>
|
||||
</view>
|
||||
|
||||
<!-- 表单内容 -->
|
||||
<view class="form-container">
|
||||
<!-- 图片上传 -->
|
||||
<view class="form-section">
|
||||
<view class="section-title">牛只照片</view>
|
||||
<view class="image-upload">
|
||||
<view class="image-list">
|
||||
<view
|
||||
v-for="(image, index) in formData.images"
|
||||
:key="index"
|
||||
class="image-item"
|
||||
>
|
||||
<image :src="image" class="uploaded-image" @click="previewImage(index)" />
|
||||
<view class="delete-btn" @click="deleteImage(index)">×</view>
|
||||
</view>
|
||||
<view
|
||||
v-if="formData.images.length < 9"
|
||||
class="upload-btn"
|
||||
@click="chooseImage"
|
||||
>
|
||||
<text class="upload-icon">+</text>
|
||||
<text class="upload-text">添加照片</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="upload-tip">最多上传9张照片,第一张为封面</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 基本信息 -->
|
||||
<view class="form-section">
|
||||
<view class="section-title">基本信息</view>
|
||||
|
||||
<view class="form-item">
|
||||
<view class="item-label">牛只编号 <text class="required">*</text></view>
|
||||
<input
|
||||
class="form-input"
|
||||
v-model="formData.cattleId"
|
||||
placeholder="请输入牛只编号"
|
||||
maxlength="20"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<view class="form-item">
|
||||
<view class="item-label">品种 <text class="required">*</text></view>
|
||||
<picker
|
||||
mode="selector"
|
||||
:range="breedOptions"
|
||||
range-key="label"
|
||||
:value="formData.breedIndex"
|
||||
@change="handleBreedChange"
|
||||
>
|
||||
<view class="picker-input">
|
||||
{{ formData.breed || '请选择品种' }}
|
||||
<text class="picker-arrow">></text>
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
|
||||
<view class="form-item">
|
||||
<view class="item-label">性别 <text class="required">*</text></view>
|
||||
<picker
|
||||
mode="selector"
|
||||
:range="genderOptions"
|
||||
range-key="label"
|
||||
:value="formData.genderIndex"
|
||||
@change="handleGenderChange"
|
||||
>
|
||||
<view class="picker-input">
|
||||
{{ formData.gender || '请选择性别' }}
|
||||
<text class="picker-arrow">></text>
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
|
||||
<view class="form-item">
|
||||
<view class="item-label">年龄 <text class="required">*</text></view>
|
||||
<view class="age-input">
|
||||
<input
|
||||
class="form-input"
|
||||
type="number"
|
||||
v-model="formData.age"
|
||||
placeholder="请输入年龄"
|
||||
/>
|
||||
<text class="age-unit">个月</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="form-item">
|
||||
<view class="item-label">体重 <text class="required">*</text></view>
|
||||
<view class="weight-input">
|
||||
<input
|
||||
class="form-input"
|
||||
type="digit"
|
||||
v-model="formData.weight"
|
||||
placeholder="请输入体重"
|
||||
/>
|
||||
<text class="weight-unit">kg</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 健康状况 -->
|
||||
<view class="form-section">
|
||||
<view class="section-title">健康状况</view>
|
||||
|
||||
<view class="form-item">
|
||||
<view class="item-label">健康状态 <text class="required">*</text></view>
|
||||
<picker
|
||||
mode="selector"
|
||||
:range="healthOptions"
|
||||
range-key="label"
|
||||
:value="formData.healthIndex"
|
||||
@change="handleHealthChange"
|
||||
>
|
||||
<view class="picker-input">
|
||||
{{ formData.healthStatus || '请选择健康状态' }}
|
||||
<text class="picker-arrow">></text>
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
|
||||
<view class="form-item">
|
||||
<view class="item-label">疫苗接种</view>
|
||||
<view class="checkbox-group">
|
||||
<label
|
||||
class="checkbox-item"
|
||||
v-for="vaccine in vaccineOptions"
|
||||
:key="vaccine.value"
|
||||
>
|
||||
<checkbox
|
||||
:value="vaccine.value"
|
||||
:checked="formData.vaccination.includes(vaccine.value)"
|
||||
@change="handleVaccineChange"
|
||||
/>
|
||||
<text class="checkbox-label">{{ vaccine.label }}</text>
|
||||
</label>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 交易信息 -->
|
||||
<view class="form-section">
|
||||
<view class="section-title">交易信息</view>
|
||||
|
||||
<view class="form-item">
|
||||
<view class="item-label">出售价格 <text class="required">*</text></view>
|
||||
<view class="price-input">
|
||||
<text class="price-symbol">¥</text>
|
||||
<input
|
||||
class="form-input"
|
||||
type="digit"
|
||||
v-model="formData.price"
|
||||
placeholder="请输入价格"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="form-item">
|
||||
<view class="item-label">价格类型 <text class="required">*</text></view>
|
||||
<picker
|
||||
mode="selector"
|
||||
:range="priceTypeOptions"
|
||||
range-key="label"
|
||||
:value="formData.priceTypeIndex"
|
||||
@change="handlePriceTypeChange"
|
||||
>
|
||||
<view class="picker-input">
|
||||
{{ formData.priceType || '请选择价格类型' }}
|
||||
<text class="picker-arrow">></text>
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
|
||||
<view class="form-item">
|
||||
<view class="item-label">交易方式 <text class="required">*</text></view>
|
||||
<view class="checkbox-group">
|
||||
<label
|
||||
class="checkbox-item"
|
||||
v-for="trade in tradeTypeOptions"
|
||||
:key="trade.value"
|
||||
>
|
||||
<checkbox
|
||||
:value="trade.value"
|
||||
:checked="formData.tradeType.includes(trade.value)"
|
||||
@change="handleTradeTypeChange"
|
||||
/>
|
||||
<text class="checkbox-label">{{ trade.label }}</text>
|
||||
</label>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="form-item">
|
||||
<view class="item-label">所在地区 <text class="required">*</text></view>
|
||||
<picker
|
||||
mode="region"
|
||||
:value="formData.region"
|
||||
@change="handleRegionChange"
|
||||
>
|
||||
<view class="picker-input">
|
||||
{{ formData.location || '请选择地区' }}
|
||||
<text class="picker-arrow">></text>
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
|
||||
<view class="form-item">
|
||||
<view class="item-label">详细描述</view>
|
||||
<textarea
|
||||
class="form-textarea"
|
||||
v-model="formData.description"
|
||||
placeholder="请详细描述牛只情况,包括饲养环境、饲料情况等"
|
||||
maxlength="500"
|
||||
/>
|
||||
<view class="char-count">{{ formData.description.length }}/500</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 联系信息 -->
|
||||
<view class="form-section">
|
||||
<view class="section-title">联系信息</view>
|
||||
|
||||
<view class="form-item">
|
||||
<view class="item-label">联系人 <text class="required">*</text></view>
|
||||
<input
|
||||
class="form-input"
|
||||
v-model="formData.contactName"
|
||||
placeholder="请输入联系人姓名"
|
||||
maxlength="20"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<view class="form-item">
|
||||
<view class="item-label">联系电话 <text class="required">*</text></view>
|
||||
<input
|
||||
class="form-input"
|
||||
type="number"
|
||||
v-model="formData.contactPhone"
|
||||
placeholder="请输入联系电话"
|
||||
maxlength="11"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部按钮 -->
|
||||
<view class="bottom-bar">
|
||||
<button class="save-draft-btn" @click="saveDraft">保存草稿</button>
|
||||
<button class="publish-btn" @click="publishCattle">立即发布</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
formData: {
|
||||
images: [],
|
||||
cattleId: '',
|
||||
breed: '',
|
||||
breedIndex: 0,
|
||||
gender: '',
|
||||
genderIndex: 0,
|
||||
age: '',
|
||||
weight: '',
|
||||
healthStatus: '',
|
||||
healthIndex: 0,
|
||||
vaccination: [],
|
||||
price: '',
|
||||
priceType: '',
|
||||
priceTypeIndex: 0,
|
||||
tradeType: [],
|
||||
region: [],
|
||||
location: '',
|
||||
description: '',
|
||||
contactName: '',
|
||||
contactPhone: ''
|
||||
},
|
||||
breedOptions: [
|
||||
{ label: '西门塔尔牛', value: 'simmental' },
|
||||
{ label: '安格斯牛', value: 'angus' },
|
||||
{ label: '夏洛莱牛', value: 'charolais' },
|
||||
{ label: '利木赞牛', value: 'limousin' },
|
||||
{ label: '海福特牛', value: 'hereford' },
|
||||
{ label: '鲁西黄牛', value: 'luxi' },
|
||||
{ label: '秦川牛', value: 'qinchuan' },
|
||||
{ label: '南阳牛', value: 'nanyang' }
|
||||
],
|
||||
genderOptions: [
|
||||
{ label: '公牛', value: 'male' },
|
||||
{ label: '母牛', value: 'female' },
|
||||
{ label: '阉牛', value: 'castrated' }
|
||||
],
|
||||
healthOptions: [
|
||||
{ label: '健康', value: 'healthy' },
|
||||
{ label: '良好', value: 'good' },
|
||||
{ label: '一般', value: 'normal' },
|
||||
{ label: '需要关注', value: 'attention' }
|
||||
],
|
||||
vaccineOptions: [
|
||||
{ label: '口蹄疫疫苗', value: 'fmd' },
|
||||
{ label: '牛瘟疫苗', value: 'rinderpest' },
|
||||
{ label: '布鲁氏菌病疫苗', value: 'brucellosis' },
|
||||
{ label: '结核病疫苗', value: 'tuberculosis' }
|
||||
],
|
||||
priceTypeOptions: [
|
||||
{ label: '按头计价', value: 'per_head' },
|
||||
{ label: '按重量计价', value: 'per_weight' }
|
||||
],
|
||||
tradeTypeOptions: [
|
||||
{ label: '现场交易', value: 'offline' },
|
||||
{ label: '线上交易', value: 'online' },
|
||||
{ label: '支持配送', value: 'delivery' }
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 返回上一页
|
||||
goBack() {
|
||||
uni.navigateBack()
|
||||
},
|
||||
|
||||
// 选择图片
|
||||
chooseImage() {
|
||||
const remainCount = 9 - this.formData.images.length
|
||||
uni.chooseImage({
|
||||
count: remainCount,
|
||||
sizeType: ['compressed'],
|
||||
sourceType: ['album', 'camera'],
|
||||
success: (res) => {
|
||||
this.formData.images.push(...res.tempFilePaths)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 预览图片
|
||||
previewImage(index) {
|
||||
uni.previewImage({
|
||||
current: index,
|
||||
urls: this.formData.images
|
||||
})
|
||||
},
|
||||
|
||||
// 删除图片
|
||||
deleteImage(index) {
|
||||
this.formData.images.splice(index, 1)
|
||||
},
|
||||
|
||||
// 品种选择
|
||||
handleBreedChange(e) {
|
||||
const index = e.detail.value
|
||||
this.formData.breedIndex = index
|
||||
this.formData.breed = this.breedOptions[index].label
|
||||
},
|
||||
|
||||
// 性别选择
|
||||
handleGenderChange(e) {
|
||||
const index = e.detail.value
|
||||
this.formData.genderIndex = index
|
||||
this.formData.gender = this.genderOptions[index].label
|
||||
},
|
||||
|
||||
// 健康状态选择
|
||||
handleHealthChange(e) {
|
||||
const index = e.detail.value
|
||||
this.formData.healthIndex = index
|
||||
this.formData.healthStatus = this.healthOptions[index].label
|
||||
},
|
||||
|
||||
// 疫苗选择
|
||||
handleVaccineChange(e) {
|
||||
this.formData.vaccination = e.detail.value
|
||||
},
|
||||
|
||||
// 价格类型选择
|
||||
handlePriceTypeChange(e) {
|
||||
const index = e.detail.value
|
||||
this.formData.priceTypeIndex = index
|
||||
this.formData.priceType = this.priceTypeOptions[index].label
|
||||
},
|
||||
|
||||
// 交易方式选择
|
||||
handleTradeTypeChange(e) {
|
||||
this.formData.tradeType = e.detail.value
|
||||
},
|
||||
|
||||
// 地区选择
|
||||
handleRegionChange(e) {
|
||||
this.formData.region = e.detail.value
|
||||
this.formData.location = e.detail.value.join(' ')
|
||||
},
|
||||
|
||||
// 表单验证
|
||||
validateForm() {
|
||||
if (!this.formData.cattleId) {
|
||||
uni.showToast({ title: '请输入牛只编号', icon: 'none' })
|
||||
return false
|
||||
}
|
||||
if (!this.formData.breed) {
|
||||
uni.showToast({ title: '请选择品种', icon: 'none' })
|
||||
return false
|
||||
}
|
||||
if (!this.formData.gender) {
|
||||
uni.showToast({ title: '请选择性别', icon: 'none' })
|
||||
return false
|
||||
}
|
||||
if (!this.formData.age) {
|
||||
uni.showToast({ title: '请输入年龄', icon: 'none' })
|
||||
return false
|
||||
}
|
||||
if (!this.formData.weight) {
|
||||
uni.showToast({ title: '请输入体重', icon: 'none' })
|
||||
return false
|
||||
}
|
||||
if (!this.formData.price) {
|
||||
uni.showToast({ title: '请输入价格', icon: 'none' })
|
||||
return false
|
||||
}
|
||||
if (!this.formData.contactName) {
|
||||
uni.showToast({ title: '请输入联系人', icon: 'none' })
|
||||
return false
|
||||
}
|
||||
if (!this.formData.contactPhone) {
|
||||
uni.showToast({ title: '请输入联系电话', icon: 'none' })
|
||||
return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
|
||||
// 保存草稿
|
||||
saveDraft() {
|
||||
uni.setStorageSync('cattle_draft', this.formData)
|
||||
uni.showToast({ title: '草稿已保存', icon: 'success' })
|
||||
},
|
||||
|
||||
// 发布牛只
|
||||
publishCattle() {
|
||||
if (!this.validateForm()) return
|
||||
|
||||
uni.showLoading({ title: '发布中...' })
|
||||
|
||||
// 模拟API调用
|
||||
setTimeout(() => {
|
||||
uni.hideLoading()
|
||||
uni.showToast({ title: '发布成功', icon: 'success' })
|
||||
setTimeout(() => {
|
||||
uni.navigateBack()
|
||||
}, 1500)
|
||||
}, 2000)
|
||||
}
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
// 加载草稿
|
||||
const draft = uni.getStorageSync('cattle_draft')
|
||||
if (draft) {
|
||||
this.formData = { ...this.formData, ...draft }
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.publish-page {
|
||||
min-height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
padding-bottom: 120rpx;
|
||||
}
|
||||
|
||||
/* 导航栏 */
|
||||
.nav-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 88rpx;
|
||||
padding: 0 30rpx;
|
||||
background-color: #fff;
|
||||
border-bottom: 1rpx solid #eee;
|
||||
}
|
||||
|
||||
.nav-left {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.nav-icon {
|
||||
font-size: 36rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.nav-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.nav-right {
|
||||
width: 60rpx;
|
||||
}
|
||||
|
||||
/* 表单容器 */
|
||||
.form-container {
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
.form-section {
|
||||
background-color: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 30rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
/* 图片上传 */
|
||||
.image-upload {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.image-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.image-item {
|
||||
position: relative;
|
||||
width: 200rpx;
|
||||
height: 200rpx;
|
||||
}
|
||||
|
||||
.uploaded-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 12rpx;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.delete-btn {
|
||||
position: absolute;
|
||||
top: -10rpx;
|
||||
right: -10rpx;
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
background-color: #ff4757;
|
||||
color: #fff;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.upload-btn {
|
||||
width: 200rpx;
|
||||
height: 200rpx;
|
||||
border: 2rpx dashed #ddd;
|
||||
border-radius: 12rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.upload-icon {
|
||||
font-size: 48rpx;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.upload-text {
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.upload-tip {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
|
||||
/* 表单项 */
|
||||
.form-item {
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.item-label {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.required {
|
||||
color: #ff4757;
|
||||
}
|
||||
|
||||
.form-input {
|
||||
width: 100%;
|
||||
height: 80rpx;
|
||||
padding: 0 20rpx;
|
||||
border: 1rpx solid #ddd;
|
||||
border-radius: 8rpx;
|
||||
font-size: 28rpx;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.picker-input {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 80rpx;
|
||||
padding: 0 20rpx;
|
||||
border: 1rpx solid #ddd;
|
||||
border-radius: 8rpx;
|
||||
font-size: 28rpx;
|
||||
background-color: #fff;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.picker-arrow {
|
||||
color: #999;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.age-input, .weight-input, .price-input {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.age-unit, .weight-unit {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.price-symbol {
|
||||
font-size: 32rpx;
|
||||
color: #333;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.form-textarea {
|
||||
width: 100%;
|
||||
min-height: 200rpx;
|
||||
padding: 20rpx;
|
||||
border: 1rpx solid #ddd;
|
||||
border-radius: 8rpx;
|
||||
font-size: 28rpx;
|
||||
background-color: #fff;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
.char-count {
|
||||
text-align: right;
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
|
||||
/* 复选框组 */
|
||||
.checkbox-group {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 30rpx;
|
||||
}
|
||||
|
||||
.checkbox-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10rpx;
|
||||
}
|
||||
|
||||
.checkbox-label {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* 底部按钮 */
|
||||
.bottom-bar {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
padding: 20rpx 30rpx;
|
||||
background-color: #fff;
|
||||
border-top: 1rpx solid #eee;
|
||||
}
|
||||
|
||||
.save-draft-btn {
|
||||
flex: 1;
|
||||
height: 80rpx;
|
||||
background-color: #f5f5f5;
|
||||
color: #666;
|
||||
border: none;
|
||||
border-radius: 40rpx;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.publish-btn {
|
||||
flex: 2;
|
||||
height: 80rpx;
|
||||
background-color: #007AFF;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 40rpx;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user