5 Commits 37af825c38 ... 3ac69e0139

Auteur SHA1 Message Date
  lalalashen 3ac69e0139 Merge branch 'master' of http://150.158.33.144:3000/lalalashen/MoeNovaClient il y a 2 mois
  lalalashen 13b2b306d9 上传 il y a 2 mois
  lalalashen 1298425bbd Merge branch 'master' of http://150.158.33.144:3000/lalalashen/MoeNovaClient il y a 2 mois
  lalalashen 05b9bb5c85 Merge branch 'master' of http://150.158.33.144:3000/lalalashen/MoeNovaClient il y a 2 mois
  lalalashen d1e3ef0e9e 农场的部分修改 il y a 2 mois

+ 43 - 21
components/CustomDialog/CustomDialog.vue

@@ -1,11 +1,12 @@
 <template>
   <view class="custom-dialog" v-if="visible">
     <view class="dialog-mask" @click="closeDialog"></view>
-    <view class="dialog-content" :class="{'dialog-show': visible}" :animation="animationData">
-      <view class="dialog-header">
+    <view class="dialog-content" :class="{'dialog-show': visible}" :animation="animationData" :style="{ width: contentWidth }">
+      <view class="dialog-header" v-if="title && title.length > 0">
         <text class="dialog-title">{{title}}</text>
-        <view class="dialog-close" @click="closeDialog">×</view>
       </view>
+      <view class="dialog-close" @click="closeDialog" v-if="!closeImg">×</view>
+      <image v-if="closeImg" class="dialog-close-img" :src="closeImg" @click="closeDialog" mode="aspectFit"></image>
       <view class="dialog-body">
         <slot></slot>
       </view>
@@ -24,6 +25,14 @@ export default {
     title: {
       type: String,
       default: '提示'
+    },
+    contentWidth: {
+      type: String,
+      default: '600rpx'
+    },
+    closeImg: {
+      type: String,
+      default: ''
     }
   },
   data() {
@@ -80,7 +89,7 @@ export default {
   left: 0;
   right: 0;
   bottom: 0;
-  z-index: 9999;
+  z-index: 900;
   display: flex;
   align-items: center;
   justify-content: center;
@@ -96,7 +105,6 @@ export default {
   
   .dialog-content {
     position: relative;
-    width: 600rpx;
     background: #fff;
     border-radius: 16rpx;
     overflow: hidden;
@@ -120,23 +128,37 @@ export default {
       font-weight: 500;
       color: #333;
     }
+  }
+  
+  .dialog-close {
+    position: absolute;
+    right: 20rpx;
+    top: 20rpx;
+    width: 60rpx;
+    height: 60rpx;
+    line-height: 60rpx;
+    text-align: center;
+    font-size: 40rpx;
+    color: #999;
+    cursor: pointer;
+    z-index: 1;
     
-    .dialog-close {
-      position: absolute;
-      right: 20rpx;
-      top: 50%;
-      transform: translateY(-50%);
-      width: 60rpx;
-      height: 60rpx;
-      line-height: 60rpx;
-      text-align: center;
-      font-size: 40rpx;
-      color: #999;
-      cursor: pointer;
-      
-      &:active {
-        opacity: 0.7;
-      }
+    &:active {
+      opacity: 0.7;
+    }
+  }
+
+  .dialog-close-img {
+    position: absolute;
+    right: 20rpx;
+    top: 20rpx;
+    width: 60rpx;
+    height: 60rpx;
+    z-index: 1;
+    cursor: pointer;
+    
+    &:active {
+      opacity: 0.7;
     }
   }
   

+ 156 - 0
components/dialogs/ShopDialog.scss

@@ -0,0 +1,156 @@
+.shop-content {
+  background: #f8f3e9;
+  min-height: 200rpx;
+  padding: 20rpx;
+  height: 680rpx;
+  display: flex;
+  flex-direction: column;
+
+  .shop-tabs {
+    display: flex;
+    margin-bottom: 20rpx;
+    background: #8b5e3c;
+    border-radius: 8rpx;
+    padding: 10rpx;
+
+    .tab-item {
+      flex: 1;
+      text-align: center;
+      padding: 15rpx 0;
+      font-size: 28rpx;
+      color: #f8f3e9;
+      background: #6b4423;
+      margin: 0 5rpx;
+      border-radius: 6rpx;
+
+      &.active {
+        background: #f8f3e9;
+        color: #6b4423;
+        font-weight: bold;
+      }
+    }
+  }
+
+  .shop-items {
+    flex: 1;
+    overflow-y: auto;
+
+    .shop-item {
+      width: 100%;
+      padding: 0 10rpx;
+      margin-top: 10rpx;
+      margin-bottom: 20rpx;
+      height: 220rpx;
+
+      &:last-child {
+        margin-bottom: 0;
+      }
+    }
+
+    .item-card {
+      background: #fff;
+      border-radius: 12rpx;
+      padding: 5px;
+      position: relative;
+      border: 2rpx solid #ddd;
+      display: flex;
+      height: 100%;
+
+      .new-tag {
+        position: absolute;
+        top: -10rpx;
+        left: -10rpx;
+        background: #ff6b6b;
+        color: white;
+        padding: 4rpx 12rpx;
+        border-radius: 20rpx;
+        font-size: 20rpx;
+      }
+
+      .item-left {
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        margin-right: 20rpx;
+        width: 80rpx;
+        padding-left: 10rpx;
+        .item-icon {
+          width: 80rpx;
+          height: 80rpx;
+          margin-bottom: 28rpx;
+        }
+
+        .owned-text {
+          font-size: 22rpx;
+          color: #666;
+          white-space: nowrap;
+        }
+      }
+
+      .item-info {
+        padding-left: 80rpx;
+        flex: 1;
+        position: relative;
+        display: flex;
+        flex-direction: column;
+        
+        .item-name {
+          font-size: 26rpx;
+          color: #933;
+          font-weight: bold;
+          margin-bottom: 8rpx;
+        }
+
+        .item-details {
+          font-size: 20rpx;
+          color: #666;
+          padding-left: 10rpx;
+
+          .detail-text {
+            display: block;
+          }
+        }
+
+        .item-bottom {
+          display: flex;
+          align-items: center;
+          justify-content: space-between;
+          margin-top: auto;
+          margin-bottom: 10rpx;
+          position: absolute;
+          bottom: 0;
+          left: 80rpx;
+          right: 0;
+
+          .item-price {
+            display: flex;
+            align-items: center;
+            gap: 4rpx;
+            margin-top: 25rpx;
+
+            .currency-icon {
+              width: 24rpx;
+              height: 24rpx;
+            }
+
+            text {
+              font-size: 24rpx;
+              color: #ff9500;
+              font-weight: bold;
+            }
+          }
+
+          .buy-btn {
+            background: #8b5e3c;
+            color: white;
+            font-size: 24rpx;
+            padding: 4rpx 16rpx;
+            border-radius: 30rpx;
+            border: none;
+            margin-right: 10rpx; 
+          }
+        }
+      }
+    }
+  }
+} 

+ 59 - 104
components/dialogs/ShopDialog.vue

@@ -1,7 +1,7 @@
 <template>
-  <custom-dialog :visible.sync="dialogVisible" title="商" @close="onClose">
+  <custom-dialog :visible.sync="dialogVisible" title="商" @close="onClose">
     <view class="shop-content">
-      <view class="shop-tabs">
+      <!-- <view class="shop-tabs">
         <view 
           v-for="(tab, index) in tabs" 
           :key="index"
@@ -11,17 +11,30 @@
         >
           {{tab}}
         </view>
-      </view>
+      </view> -->
       <view class="shop-items">
         <view class="shop-item" v-for="(item, index) in currentItems" :key="index" @click="onItemClick(item)">
-          <image class="item-icon" :src="item.icon" mode="aspectFit"></image>
-          <view class="item-info">
-            <text class="item-name">{{item.name}}</text>
-            <text class="item-desc">{{item.description}}</text>
-          </view>
-          <view class="item-price">
-            <image class="currency-icon" src="/static/currency/coin.png" mode="aspectFit"></image>
-            <text>{{item.price}}</text>
+          <view class="item-card">
+            <view class="new-tag" v-if="item.isNew">新品</view>
+            <view class="item-left">
+              <image class="item-icon" :src="item.icon" mode="aspectFit"></image>
+              <text class="owned-text">已持有: {{item.owned}}</text>
+            </view>
+            <view class="item-info">
+              <text class="item-name">{{item.name}}</text>
+              <view class="item-details">
+                <text class="detail-text">收获期: {{item.harvestTime}}</text>
+                <text class="detail-text">成长期: {{item.growthTime}}</text>
+                <text class="detail-text">产量: {{item.yield}}</text>
+              </view>
+              <view class="item-bottom">
+                <view class="item-price">
+                  <image class="currency-icon" src="/static/currency/coin.png" mode="aspectFit"></image>
+                  <text>{{item.price}}</text>
+                </view>
+                <button class="buy-btn">购买</button>
+              </view>
+            </view>
           </view>
         </view>
       </view>
@@ -31,6 +44,7 @@
 
 <script>
 import CustomDialog from '../CustomDialog/CustomDialog.vue'
+import './ShopDialog.scss'
 
 export default {
   name: 'ShopDialog',
@@ -47,21 +61,42 @@ export default {
     return {
       dialogVisible: false,
       currentTab: 0,
-      tabs: ['道具', '装备', '材料'],
+      tabs: ['种子', '道具', '钻石'],
       items: {
         0: [
-          { icon: '/static/items/potion1.png', name: '生命药水', description: '恢复100点生命值', price: 100 },
-          { icon: '/static/items/potion2.png', name: '魔法药水', description: '恢复50点魔法值', price: 150 },
-          { icon: '/static/items/potion3.png', name: '体力药水', description: '恢复50点体力值', price: 120 }
-        ],
-        1: [
-          { icon: '/static/items/weapon1.png', name: '铁剑', description: '攻击力+10', price: 500 },
-          { icon: '/static/items/armor1.png', name: '皮甲', description: '防御力+5', price: 300 }
+          { 
+            icon: '/static/items/seed1.png', 
+            name: '草莓种子', 
+            harvestTime: '160时', 
+            growthTime: '60时',
+            yield: '10',
+            price: 100,
+            owned: 3,
+            isNew: true
+          },
+          { 
+            icon: '/static/items/seed1.png', 
+            name: '草莓种子', 
+            harvestTime: '160时', 
+            growthTime: '60时',
+            yield: '10',
+            price: 100,
+            owned: 3,
+            isNew: true
+          },
+          { 
+            icon: '/static/items/seed1.png', 
+            name: '草莓种子', 
+            harvestTime: '160时', 
+            growthTime: '60时',
+            yield: '10',
+            price: 100,
+            owned: 3,
+            isNew: true
+          }
         ],
-        2: [
-          { icon: '/static/items/material1.png', name: '木材', description: '基础材料', price: 50 },
-          { icon: '/static/items/material2.png', name: '铁矿', description: '基础材料', price: 80 }
-        ]
+        1: [],
+        2: []
       }
     }
   },
@@ -83,7 +118,6 @@ export default {
       this.dialogVisible = false
     },
     onItemClick(item) {
-      // 处理购买逻辑
       this.$emit('buy', item)
     }
   }
@@ -91,84 +125,5 @@ export default {
 </script>
 
 <style lang="scss" scoped>
-.shop-content {
-  .shop-tabs {
-    display: flex;
-    border-bottom: 2rpx solid #eee;
-    margin-bottom: 20rpx;
-
-    .tab-item {
-      flex: 1;
-      text-align: center;
-      padding: 20rpx 0;
-      font-size: 28rpx;
-      color: #666;
-      position: relative;
-
-      &.active {
-        color: #007AFF;
-        font-weight: 500;
-
-        &::after {
-          content: '';
-          position: absolute;
-          bottom: 0;
-          left: 50%;
-          transform: translateX(-50%);
-          width: 40rpx;
-          height: 4rpx;
-          background: #007AFF;
-          border-radius: 2rpx;
-        }
-      }
-    }
-  }
-
-  .shop-items {
-    .shop-item {
-      display: flex;
-      align-items: center;
-      padding: 20rpx;
-      border-bottom: 2rpx solid #f5f5f5;
-
-      .item-icon {
-        width: 80rpx;
-        height: 80rpx;
-        margin-right: 20rpx;
-      }
-
-      .item-info {
-        flex: 1;
-
-        .item-name {
-          font-size: 28rpx;
-          color: #333;
-          margin-bottom: 4rpx;
-        }
-
-        .item-desc {
-          font-size: 24rpx;
-          color: #999;
-        }
-      }
-
-      .item-price {
-        display: flex;
-        align-items: center;
-        gap: 8rpx;
-
-        .currency-icon {
-          width: 32rpx;
-          height: 32rpx;
-        }
-
-        text {
-          font-size: 28rpx;
-          color: #FF9500;
-          font-weight: 500;
-        }
-      }
-    }
-  }
-}
+@import './ShopDialog.scss';
 </style> 

+ 138 - 10
pages/isLand/FarmDialog.scss

@@ -5,27 +5,86 @@
   gap: 20rpx;
 }
 
+.farm-beds {
+  display: flex;
+  flex-direction: column;
+  gap: 30rpx;
+}
+
 .farm-grid {
-  display: grid;
-  grid-template-columns: repeat(4, 1fr);
-  grid-template-rows: repeat(3, 1fr);
+  display: flex;
+  flex-direction: column;
   gap: 10rpx;
-  aspect-ratio: 4/3;
   background-color: #8B4513;
   padding: 20rpx;
   border-radius: 10rpx;
+  position: relative;
+
+  &::before {
+    content: '';
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    background: rgba(173, 216, 230, 0.3);
+    border-radius: 10rpx;
+    pointer-events: none;
+  }
+
+  .row {
+    display: flex;
+    justify-content: center;
+    gap: 10rpx;
+    
+    &.row-1, &.row-3 {
+      padding: 0 60rpx;
+    }
+  }
 }
 
 .farm-plot {
+  flex: 1;
   aspect-ratio: 1;
+  max-width: 120rpx;
   background-color: #654321;
   border-radius: 8rpx;
   position: relative;
   cursor: pointer;
-  transition: transform 0.2s;
+  transition: all 0.2s;
 
-  &:hover {
-    transform: scale(1.05);
+  &.is-locked {
+    background-color: #333333;
+    &::after {
+      content: '🔒';
+      position: absolute;
+      top: 50%;
+      left: 50%;
+      transform: translate(-50%, -50%);
+      font-size: 24rpx;
+    }
+  }
+
+  &.is-empty {
+    background-color: #8B4513;
+  }
+
+  &.is-growing {
+    background-color: #556B2F;
+  }
+
+  &.is-harvestable {
+    background-color: #228B22;
+  }
+
+  &.highlight-empty {
+    animation: pulse 1s infinite;
+    border: 2rpx solid #FFD700;
+  }
+
+  &.highlight-harvestable {
+    animation: pulse 1s infinite;
+    border: 2rpx solid #FFD700;
   }
 
   .plot-content {
@@ -60,6 +119,25 @@
     }
   }
 
+  .plot-state-text {
+    position: absolute;
+    bottom: 0;
+    left: 0;
+    right: 0;
+    background: rgba(0, 0, 0, 0.6);
+    color: white;
+    font-size: 20rpx;
+    padding: 2rpx 0;
+    text-align: center;
+    border-bottom-left-radius: 8rpx;
+    border-bottom-right-radius: 8rpx;
+    transition: all 0.3s;
+    
+    &.state-changed {
+      animation: stateChange 0.3s ease-in-out;
+    }
+  }
+
   .crop-stage {
     width: 80%;
     height: 80%;
@@ -75,6 +153,36 @@
   }
 }
 
+@keyframes pulse {
+  0% {
+    transform: scale(1);
+    box-shadow: 0 0 0 0 rgba(255, 215, 0, 0.4);
+  }
+  70% {
+    transform: scale(1.05);
+    box-shadow: 0 0 0 10rpx rgba(255, 215, 0, 0);
+  }
+  100% {
+    transform: scale(1);
+    box-shadow: 0 0 0 0 rgba(255, 215, 0, 0);
+  }
+}
+
+@keyframes stateChange {
+  0% {
+    transform: scale(1);
+    opacity: 0.5;
+  }
+  50% {
+    transform: scale(1.1);
+    opacity: 1;
+  }
+  100% {
+    transform: scale(1);
+    opacity: 1;
+  }
+}
+
 .action-panel {
   display: flex;
   flex-direction: column;
@@ -82,21 +190,41 @@
   padding: 20rpx;
   background-color: rgba(255, 255, 255, 0.9);
   border-radius: 10rpx;
+  position: relative;
+
+  .farm-state-indicator {
+    position: absolute;
+    bottom: 100%;
+    left: 0;
+    background: rgba(0, 0, 0, 0.7);
+    color: white;
+    padding: 10rpx 20rpx;
+    border-top-left-radius: 10rpx;
+    border-top-right-radius: 10rpx;
+    font-size: 24rpx;
+    margin-bottom: 10rpx;
+    transition: all 0.3s;
+    
+    &.state-changed {
+      animation: stateChange 0.3s ease-in-out;
+    }
+  }
 
   .action-buttons {
     display: flex;
     gap: 10rpx;
-    flex-wrap: wrap;
+    justify-content: space-around;
 
     button {
       flex: 1;
-      min-width: 120rpx;
-      padding: 10rpx 20rpx;
+      min-width: 160rpx;
+      padding: 15rpx 30rpx;
       border: none;
       border-radius: 5rpx;
       background-color: #4CAF50;
       color: white;
       font-size: 28rpx;
+      transition: all 0.2s;
 
       &:disabled {
         background-color: #cccccc;

+ 318 - 160
pages/isLand/FarmDialog.vue

@@ -1,30 +1,139 @@
 <template>
   <custom-dialog :visible.sync="dialogVisible" title="我的农场" @close="onClose">
-    <view class="farm-container">
-      <view class="farm-grid">
-        <view v-for="(plot, index) in plots" :key="index" class="farm-plot" @click="onPlotClick(index)">
-          <view class="plot-content" :class="getPlotClass(plot)">
-            <view v-if="plot.crop" class="crop-stage">
-              <image :src="getCropImage(plot)" mode="aspectFit" class="crop-image"></image>
+    <view class="farm-container" @click="onContainerClick">
+      <view class="farm-beds">
+        <view class="farm-grid upper-bed">
+          <view class="row row-1">
+            <view v-for="index in 3" :key="index-1" 
+                  class="farm-plot" 
+                  :class="getPlotClass(plots[index-1])"
+                  @click.stop="onPlotClick(index-1)">
+              <view class="plot-content">
+                <view v-if="plots[index-1].state !== 'locked'" class="crop-stage">
+                  <image v-if="plots[index-1].state === 'growing' || plots[index-1].state === 'harvestable'" 
+                         :src="getCropImage(plots[index-1])" 
+                         mode="aspectFit" 
+                         class="crop-image">
+                  </image>
+                </view>
+                <view class="plot-state-text" :class="{ 'state-changed': plots[index-1].stateChanged }">
+                  {{ getPlotStateText(plots[index-1].state) }}
+                </view>
+              </view>
+            </view>
+          </view>
+          <view class="row row-2">
+            <view v-for="index in 4" :key="index+2" 
+                  class="farm-plot" 
+                  :class="getPlotClass(plots[index+2])"
+                  @click.stop="onPlotClick(index+2)">
+              <view class="plot-content">
+                <view v-if="plots[index+2].state !== 'locked'" class="crop-stage">
+                  <image v-if="plots[index+2].state === 'growing' || plots[index+2].state === 'harvestable'" 
+                         :src="getCropImage(plots[index+2])" 
+                         mode="aspectFit" 
+                         class="crop-image">
+                  </image>
+                </view>
+                <view class="plot-state-text" :class="{ 'state-changed': plots[index+2].stateChanged }">
+                  {{ getPlotStateText(plots[index+2].state) }}
+                </view>
+              </view>
+            </view>
+          </view>
+          <view class="row row-3">
+            <view v-for="index in 3" :key="index+6" 
+                  class="farm-plot" 
+                  :class="getPlotClass(plots[index+6])"
+                  @click.stop="onPlotClick(index+6)">
+              <view class="plot-content">
+                <view v-if="plots[index+6].state !== 'locked'" class="crop-stage">
+                  <image v-if="plots[index+6].state === 'growing' || plots[index+6].state === 'harvestable'" 
+                         :src="getCropImage(plots[index+6])" 
+                         mode="aspectFit" 
+                         class="crop-image">
+                  </image>
+                </view>
+                <view class="plot-state-text" :class="{ 'state-changed': plots[index+6].stateChanged }">
+                  {{ getPlotStateText(plots[index+6].state) }}
+                </view>
+              </view>
+            </view>
+          </view>
+        </view>
+        
+        <view class="farm-grid lower-bed">
+          <view class="row row-1">
+            <view v-for="index in 3" :key="index+9" 
+                  class="farm-plot" 
+                  :class="getPlotClass(plots[index+9])"
+                  @click.stop="onPlotClick(index+9)">
+              <view class="plot-content">
+                <view v-if="plots[index+9].state !== 'locked'" class="crop-stage">
+                  <image v-if="plots[index+9].state === 'growing' || plots[index+9].state === 'harvestable'" 
+                         :src="getCropImage(plots[index+9])" 
+                         mode="aspectFit" 
+                         class="crop-image">
+                  </image>
+                </view>
+                <view class="plot-state-text" :class="{ 'state-changed': plots[index+9].stateChanged }">
+                  {{ getPlotStateText(plots[index+9].state) }}
+                </view>
+              </view>
+            </view>
+          </view>
+          <view class="row row-2">
+            <view v-for="index in 4" :key="index+12" 
+                  class="farm-plot" 
+                  :class="getPlotClass(plots[index+12])"
+                  @click.stop="onPlotClick(index+12)">
+              <view class="plot-content">
+                <view v-if="plots[index+12].state !== 'locked'" class="crop-stage">
+                  <image v-if="plots[index+12].state === 'growing' || plots[index+12].state === 'harvestable'" 
+                         :src="getCropImage(plots[index+12])" 
+                         mode="aspectFit" 
+                         class="crop-image">
+                  </image>
+                </view>
+                <view class="plot-state-text" :class="{ 'state-changed': plots[index+12].stateChanged }">
+                  {{ getPlotStateText(plots[index+12].state) }}
+                </view>
+              </view>
+            </view>
+          </view>
+          <view class="row row-3">
+            <view v-for="index in 3" :key="index+16" 
+                  class="farm-plot" 
+                  :class="getPlotClass(plots[index+16])"
+                  @click.stop="onPlotClick(index+16)">
+              <view class="plot-content">
+                <view v-if="plots[index+16].state !== 'locked'" class="crop-stage">
+                  <image v-if="plots[index+16].state === 'growing' || plots[index+16].state === 'harvestable'" 
+                         :src="getCropImage(plots[index+16])" 
+                         mode="aspectFit" 
+                         class="crop-image">
+                  </image>
+                </view>
+                <view class="plot-state-text" :class="{ 'state-changed': plots[index+16].stateChanged }">
+                  {{ getPlotStateText(plots[index+16].state) }}
+                </view>
+              </view>
             </view>
-            <view v-if="plot.weeds" class="weeds"></view>
-            <view v-if="plot.needsWater" class="water-indicator"></view>
           </view>
         </view>
       </view>
       
-      <view class="action-panel">
-        <view class="action-buttons">
-          <button @click="selectAction('plant')" :disabled="!canPlant">播种</button>
-          <button @click="selectAction('water')" :disabled="!canWater">浇水</button>
-          <button @click="selectAction('fertilize')" :disabled="!canFertilize">施肥</button>
-          <button @click="selectAction('weed')" :disabled="!canWeed">除草</button>
-          <button @click="selectAction('harvest')" :disabled="!canHarvest">收获</button>
+      <view class="action-panel" >
+        <view class="farm-state-indicator" :class="{ 'state-changed': farmStateChanged }">
+          当前状态: {{ getFarmStateText(farmState) }}
         </view>
-        <view class="selected-plot-info" v-if="selectedPlot !== null">
-          <text>状态: {{ getPlotStatus(plots[selectedPlot]) }}</text>
-          <text v-if="plots[selectedPlot].crop">作物: {{ plots[selectedPlot].crop.name }}</text>
-          <text v-if="plots[selectedPlot].crop">生长进度: {{ getGrowthProgress(plots[selectedPlot]) }}%</text>
+        <view class="action-buttons">
+          <button @click.stop="onUnlockClick" >解锁</button>
+          <button @click.stop="onPlantClick" >播种</button>
+          <button @click.stop="onHarvestClick" >收获</button>
+          <!-- <button @click.stop="onUnlockClick" :disabled="!canUnlock">解锁</button>
+          <button @click.stop="onPlantClick" :disabled="!canPlant">播种</button>
+          <button @click.stop="onHarvestClick" :disabled="!canHarvest">收获</button> -->
         </view>
       </view>
     </view>
@@ -33,20 +142,19 @@
 
 <script>
 import CustomDialog from '@/components/CustomDialog/CustomDialog.vue'
+// import { showModal, showToast } from '@/utils/uniapi'
 
-const CROPS = {
-  carrot: {
-    name: '胡萝卜',
-    growthTime: 30, // minutes
-    stages: ['/static/crops/carrot_stage1.png', '/static/crops/carrot_stage2.png', '/static/crops/carrot_stage3.png'],
-    value: 100
-  },
-  tomato: {
-    name: '番茄',
-    growthTime: 60,
-    stages: ['/static/crops/tomato_stage1.png', '/static/crops/tomato_stage2.png', '/static/crops/tomato_stage3.png'],
-    value: 200
-  }
+const PLOT_STATES = {
+  LOCKED: 'locked',
+  EMPTY: 'empty',
+  GROWING: 'growing',
+  HARVESTABLE: 'harvestable'
+}
+
+const FARM_STATES = {
+  IDLE: 'idle',
+  PLANTING: 'planting',
+  HARVESTING: 'harvesting'
 }
 
 export default {
@@ -58,172 +166,222 @@ export default {
     visible: {
       type: Boolean,
       default: false
+    },
+    coins: {
+      type: Number,
+      default: 0
     }
   },
   data() {
     return {
       dialogVisible: false,
-      plots: Array(12).fill().map(() => ({
-        crop: null,
+      farmState: FARM_STATES.IDLE,
+      farmStateChanged: false,
+      plots: Array(20).fill().map(() => ({
+        state: PLOT_STATES.LOCKED,
         plantedAt: null,
-        watered: false,
-        fertilized: false,
-        weeds: false,
-        needsWater: false
-      })),
-      selectedPlot: null,
-      selectedAction: null,
-      growthTimer: null
+        stateChanged: false
+      }))
     }
   },
   computed: {
+    canUnlock() {
+      return this.farmState === FARM_STATES.IDLE && this.hasLockedPlots
+    },
     canPlant() {
-      return this.selectedPlot !== null && !this.plots[this.selectedPlot].crop
+      return this.farmState === FARM_STATES.IDLE && this.hasEmptyPlots
+    },
+    canHarvest() {
+      return this.farmState === FARM_STATES.IDLE && this.hasHarvestablePlots
     },
-    canWater() {
-      return this.selectedPlot !== null && 
-             this.plots[this.selectedPlot].crop && 
-             !this.plots[this.selectedPlot].watered
+    hasLockedPlots() {
+      return this.plots.some(plot => plot.state === PLOT_STATES.LOCKED)
     },
-    canFertilize() {
-      return this.selectedPlot !== null && 
-             this.plots[this.selectedPlot].crop && 
-             !this.plots[this.selectedPlot].fertilized
+    hasEmptyPlots() {
+      return this.plots.some(plot => plot.state === PLOT_STATES.EMPTY)
     },
-    canWeed() {
-      return this.selectedPlot !== null && 
-             this.plots[this.selectedPlot].weeds
+    hasHarvestablePlots() {
+      return this.plots.some(plot => plot.state === PLOT_STATES.HARVESTABLE)
     },
-    canHarvest() {
-      return this.selectedPlot !== null && 
-             this.plots[this.selectedPlot].crop && 
-             this.isCropReady(this.plots[this.selectedPlot])
+    nextUnlockedPlotIndex() {
+      return this.plots.findIndex(plot => plot.state === PLOT_STATES.LOCKED)
     }
   },
   watch: {
     visible(newVal) {
       this.dialogVisible = newVal
       if (newVal) {
-        this.startGrowthTimer()
-      } else {
-        this.stopGrowthTimer()
+        // 初始化第一块地为空地状态
+        if (this.plots[0].state === PLOT_STATES.LOCKED) {
+          this.plots[0].state = PLOT_STATES.EMPTY
+        }
+        console.log('农场对话框打开,当前农场状态:', this.farmState)
+      }
+    },
+    farmState: {
+      handler(newVal, oldVal) {
+        if (newVal !== oldVal) {
+          console.log(`农场状态发生变化: ${oldVal} -> ${newVal}`)
+          this.farmStateChanged = true;
+          setTimeout(() => {
+            this.farmStateChanged = false;
+          }, 300);
+        }
       }
     }
   },
   methods: {
     onClose() {
+      console.log('关闭农场对话框,重置农场状态为空闲')
+      this.farmState = FARM_STATES.IDLE
       this.$emit('update:visible', false)
     },
-    onPlotClick(index) {
-      this.selectedPlot = index
-      if (this.selectedAction) {
-        this.performAction(index)
+    getPlotClass(plot) {
+      return {
+        'is-locked': plot.state === PLOT_STATES.LOCKED,
+        'is-empty': plot.state === PLOT_STATES.EMPTY,
+        'is-growing': plot.state === PLOT_STATES.GROWING,
+        'is-harvestable': plot.state === PLOT_STATES.HARVESTABLE,
+        'highlight-empty': this.farmState === FARM_STATES.PLANTING && plot.state === PLOT_STATES.EMPTY,
+        'highlight-harvestable': this.farmState === FARM_STATES.HARVESTING && plot.state === PLOT_STATES.HARVESTABLE
       }
     },
-    selectAction(action) {
-      this.selectedAction = action
+    getCropImage(plot) {
+      if (plot.state === PLOT_STATES.GROWING) {
+        return '/static/crops/growing.png'
+      } else if (plot.state === PLOT_STATES.HARVESTABLE) {
+        return '/static/crops/harvestable.png'
+      }
+      return ''
     },
-    performAction(plotIndex) {
-      const plot = this.plots[plotIndex]
-      
-      switch (this.selectedAction) {
-        case 'plant':
-          if (this.canPlant) {
-            plot.crop = { ...CROPS.carrot }
-            plot.plantedAt = Date.now()
-            plot.watered = false
-            plot.fertilized = false
-            plot.weeds = false
-            plot.needsWater = true
-          }
-          break
-        case 'water':
-          if (this.canWater) {
-            plot.watered = true
-            plot.needsWater = false
-          }
-          break
-        case 'fertilize':
-          if (this.canFertilize) {
-            plot.fertilized = true
-          }
-          break
-        case 'weed':
-          if (this.canWeed) {
-            plot.weeds = false
-          }
-          break
-        case 'harvest':
-          if (this.canHarvest) {
-            // TODO: Add harvested items to inventory
-            plot.crop = null
-            plot.plantedAt = null
-            plot.watered = false
-            plot.fertilized = false
-            plot.weeds = false
-            plot.needsWater = false
-          }
-          break
+    updatePlotState(index, newState) {
+      const plot = this.plots[index];
+      if (plot.state !== newState) {
+        console.log(`更新土地 ${index} 状态: ${plot.state} -> ${newState}`)
+        plot.state = newState;
+        plot.stateChanged = true;
+        setTimeout(() => {
+          plot.stateChanged = false;
+        }, 300);
       }
-      
-      this.selectedAction = null
     },
-    getPlotClass(plot) {
-      return {
-        'has-crop': plot.crop,
-        'needs-water': plot.needsWater,
-        'has-weeds': plot.weeds
+    async onUnlockClick() {
+      if (!this.hasLockedPlots) {
+        console.log('没有可解锁的土地,保持当前状态:', this.farmState)
+        uni.showToast({ title: '已经解锁所有土地', icon: 'none' })
+        return
+      }
+
+      const unlockCost = 100
+      console.log(`当前金币: ${this.coins}, 解锁费用: ${unlockCost}`)
+      const result = await uni.showModal({
+        title: '解锁新土地',
+        content: `是否使用${unlockCost}金币解锁新土地?\n当前金币:${this.coins}`
+      })
+
+      if (result.confirm) {
+        if (this.coins < unlockCost) {
+          console.log(`金币不足,需要 ${unlockCost} 金币,当前只有 ${this.coins} 金币`)
+          uni.showToast({ title: '金币不够', icon: 'none' })
+          return
+        }
+
+        console.log(`解锁成功,扣除 ${unlockCost} 金币,剩余 ${this.coins - unlockCost} 金币`)
+        this.$emit('update:coins', this.coins - unlockCost)
+        this.updatePlotState(this.nextUnlockedPlotIndex, PLOT_STATES.EMPTY)
+      } else {
+        console.log('用户取消解锁,保持当前状态:', this.farmState)
       }
     },
-    getCropImage(plot) {
-      if (!plot.crop) return ''
-      const growthStage = this.getGrowthStage(plot)
-      return plot.crop.stages[growthStage]
-    },
-    getGrowthStage(plot) {
-      if (!plot.crop || !plot.plantedAt) return 0
-      const growthProgress = this.getGrowthProgress(plot)
-      if (growthProgress >= 100) return 2
-      if (growthProgress >= 50) return 1
-      return 0
-    },
-    getGrowthProgress(plot) {
-      if (!plot.crop || !plot.plantedAt) return 0
-      const elapsed = (Date.now() - plot.plantedAt) / 1000 / 60 // minutes
-      const progress = (elapsed / plot.crop.growthTime) * 100
-      return Math.min(100, Math.floor(progress))
-    },
-    isCropReady(plot) {
-      return this.getGrowthProgress(plot) >= 100
-    },
-    getPlotStatus(plot) {
-      if (!plot.crop) return '空地'
-      if (plot.weeds) return '需要除草'
-      if (plot.needsWater) return '需要浇水'
-      if (this.isCropReady(plot)) return '可以收获'
-      return '生长中'
-    },
-    startGrowthTimer() {
-      this.growthTimer = setInterval(() => {
-        this.plots.forEach(plot => {
-          if (plot.crop && !plot.watered) {
-            plot.needsWater = true
-          }
-          if (plot.crop && Math.random() < 0.1) { // 10% chance to spawn weeds
-            plot.weeds = true
+    onPlantClick() {
+      console.log(`点击播种按钮,当前农场状态: ${this.farmState}`);
+      if (this.farmState === FARM_STATES.PLANTING) {
+        console.log('取消播种模式,切换为空闲状态');
+        this.farmState = FARM_STATES.IDLE;
+      } else {
+        console.log('进入播种模式,请选择要播种的空地');
+        this.farmState = FARM_STATES.PLANTING;
+      }
+    },
+    onHarvestClick() {
+      console.log(`点击收获按钮,当前农场状态: ${this.farmState}`);
+      if (this.farmState === FARM_STATES.HARVESTING) {
+        console.log('取消收获模式,切换为空闲状态');
+        this.farmState = FARM_STATES.IDLE;
+      } else {
+        console.log('进入收获模式,请选择要收获的土地');
+        this.farmState = FARM_STATES.HARVESTING;
+      }
+    },
+    onPlotClick(index) {
+      const plot = this.plots[index]
+      console.log(`点击土地 ${index},当前状态: ${plot.state},农场状态: ${this.farmState}`)
+
+      if (plot.state === PLOT_STATES.LOCKED) {
+        console.log(`土地 ${index} 未解锁,无法操作`)
+        uni.showToast({ title: '请先解锁该土地', icon: 'none' })
+        return
+      }
+
+      if (this.farmState === FARM_STATES.PLANTING) {
+        console.log(`当前为播种状态,尝试在土地 ${index} 播种`)
+        if (plot.state !== PLOT_STATES.EMPTY) {
+          console.log(`土地 ${index} 不是空地,无法播种,当前状态: ${plot.state}`)
+          uni.showToast({ title: '只有空地可以播种', icon: 'none' })
+          return
+        }
+        console.log(`在土地 ${index} 播种成功,状态更新为生长中`)
+        this.updatePlotState(index, PLOT_STATES.GROWING)
+        plot.plantedAt = Date.now()
+        console.log(`土地 ${index} 开始生长,种植时间: ${new Date(plot.plantedAt).toLocaleString()}`)
+        
+        setTimeout(() => {
+          if (plot.state === PLOT_STATES.GROWING) {
+            console.log(`土地 ${index} 生长完成,状态更新为可收获`)
+            this.updatePlotState(index, PLOT_STATES.HARVESTABLE)
+          } else {
+            console.log(`土地 ${index} 状态已改变,当前状态: ${plot.state},不更新为可收获状态`)
           }
-        })
-      }, 60000) // Check every minute
+        }, 5000)
+      } else if (this.farmState === FARM_STATES.HARVESTING) {
+        console.log(`当前为收获状态,尝试收获土地 ${index}`)
+        if (plot.state !== PLOT_STATES.HARVESTABLE) {
+          console.log(`土地 ${index} 没有可收获的作物,当前状态: ${plot.state}`)
+          uni.showToast({ title: '没有可以收获的花朵', icon: 'none' })
+          return
+        }
+        console.log(`收获土地 ${index} 成功,状态更新为空地`)
+        this.updatePlotState(index, PLOT_STATES.EMPTY)
+        plot.plantedAt = null
+        console.log(`发送收获完成事件`)
+        this.$emit('harvest-complete')
+      } else {
+        console.log(`当前为空闲状态,点击土地 ${index} 无效,请先选择操作模式`)
+      }
+    },
+    onContainerClick() {
+      if (this.farmState !== FARM_STATES.IDLE) {
+        console.log(`点击空白区域,从${this.farmState}状态切换为空闲状态`)
+        this.farmState = FARM_STATES.IDLE
+      }
     },
-    stopGrowthTimer() {
-      if (this.growthTimer) {
-        clearInterval(this.growthTimer)
-        this.growthTimer = null
+    getPlotStateText(state) {
+      const stateTexts = {
+        [PLOT_STATES.LOCKED]: '未解锁',
+        [PLOT_STATES.EMPTY]: '空地',
+        [PLOT_STATES.GROWING]: '生长中',
+        [PLOT_STATES.HARVESTABLE]: '可收获'
       }
+      return stateTexts[state]
+    },
+    getFarmStateText(state) {
+      const stateTexts = {
+        [FARM_STATES.IDLE]: '空闲',
+        [FARM_STATES.PLANTING]: '播种状态',
+        [FARM_STATES.HARVESTING]: '收获状态'
+      }
+      return stateTexts[state]
     }
-  },
-  beforeDestroy() {
-    this.stopGrowthTimer()
   }
 }
 </script>

+ 149 - 0
pages/isLand/TaskDialog.scss

@@ -0,0 +1,149 @@
+.task-board {
+  padding: 30rpx;
+  background: transparent;
+  width: 600rpx;
+  margin: 0 auto;
+  
+  .board-title {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    margin-bottom: 30rpx;
+    
+    .title-dot {
+      width: 12rpx;
+      height: 12rpx;
+      border-radius: 50%;
+      background-color: #A67C52;
+      margin: 0 20rpx;
+    }
+    
+    text {
+      color: #A67C52;
+      font-size: 32rpx;
+      font-weight: bold;
+    }
+  }
+
+  .task-card {
+    background-color: rgba(255, 246, 235, 0.95);
+    border-radius: 20rpx;
+    padding: 24rpx;
+    margin-bottom: 16rpx;
+    position: relative;
+    
+    .task-header,
+    .task-desc {
+      margin-bottom: 16rpx;
+      
+      .task-label {
+        color: #A67C52;
+        font-size: 26rpx;
+        font-weight: 500;
+      }
+      
+      .task-content {
+        color: #8B6244;
+        font-size: 26rpx;
+        margin-left: 8rpx;
+      }
+    }
+    
+    .task-rewards {
+      margin-bottom: 24rpx;
+      
+      .task-label {
+        color: #A67C52;
+        font-size: 26rpx;
+        font-weight: 500;
+        display: block;
+        margin-bottom: 16rpx;
+      }
+      
+      .reward-list {
+        display: flex;
+        gap: 30rpx;
+        
+        .reward-item {
+          display: flex;
+          flex-direction: column;
+          align-items: center;
+          
+          .reward-box {
+            width: 90rpx;
+            height: 90rpx;
+            background: linear-gradient(to bottom right, #B8E986, #7EC242);
+            border-radius: 14rpx;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            margin-bottom: 8rpx;
+            
+            &:nth-child(3) {
+              background: linear-gradient(to bottom right, #E5C1FF, #9D7EE0);
+            }
+            
+            .reward-icon {
+              width: 60rpx;
+              height: 60rpx;
+            }
+          }
+          
+          .reward-text {
+            color: #B3894A;
+            font-size: 22rpx;
+            text-align: center;
+          }
+        }
+      }
+    }
+    
+    .task-footer {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      
+      .task-date {
+        color: #CCAB8F;
+        font-size: 24rpx;
+      }
+      
+      .task-button {
+        min-width: 140rpx;
+        height: 60rpx;
+        line-height: 60rpx;
+        text-align: center;
+        border-radius: 30rpx;
+        font-size: 24rpx;
+        border: none;
+        padding: 0 24rpx;
+        
+        &.completed {
+          background: linear-gradient(to right, #FFD879, #FFA751);
+          color: #ffffff;
+          box-shadow: 0 4rpx 8rpx rgba(255, 167, 81, 0.3);
+        }
+        
+        &.available {
+          background: linear-gradient(to right, #ADE277, #7EC242);
+          color: #ffffff;
+          box-shadow: 0 4rpx 8rpx rgba(126, 194, 66, 0.3);
+        }
+      }
+    }
+    
+    .card-divider {
+      position: absolute;
+      left: 24rpx;
+      right: 24rpx;
+      bottom: 0;
+      height: 2rpx;
+      background: linear-gradient(to right, 
+        transparent, 
+        rgba(179, 137, 74, 0.3) 20%, 
+        rgba(179, 137, 74, 0.3) 80%, 
+        transparent
+      );
+    }
+  }
+} 

+ 131 - 0
pages/isLand/TaskDialog.vue

@@ -0,0 +1,131 @@
+<template>
+  <custom-dialog :visible="visible" title="" content-width="720rpx" @close="onClose">
+    <view class="task-board">
+      <view class="board-title">
+        <view class="title-dot"></view>
+        <text>任务看板</text>
+        <view class="title-dot"></view>
+      </view>
+      <view v-for="(task, index) in tasks" :key="index" class="task-card">
+        <view class="task-header">
+          <text class="task-label">任务目标:</text>
+          <text class="task-content">{{task.title}}</text>
+        </view>
+        <view class="task-desc">
+          <text class="task-label">任务描述:</text>
+          <text class="task-content">{{task.description}}</text>
+        </view>
+        <view class="task-rewards">
+          <text class="task-label">任务奖励:</text>
+          <view class="reward-list">
+            <view v-for="(reward, rIndex) in task.rewards" :key="rIndex" class="reward-item">
+              <view class="reward-box">
+                <image :src="reward.icon" class="reward-icon"></image>
+              </view>
+              <text class="reward-text">{{reward.text}}</text>
+            </view>
+          </view>
+        </view>
+        <view class="task-footer">
+          <text class="task-date">{{task.date}}</text>
+          <button 
+            :class="['task-button', task.status === 'completed' ? 'completed' : 'available']"
+            @click="handleTaskAction(task)"
+          >
+            {{task.status === 'completed' ? '完成任务' : '领取任务'}}
+          </button>
+        </view>
+        <view class="card-divider"></view>
+      </view>
+    </view>
+  </custom-dialog>
+</template>
+
+<script>
+import CustomDialog from '@/components/CustomDialog/CustomDialog.vue'
+
+export default {
+  name: 'TaskDialog',
+  components: {
+    CustomDialog
+  },
+  props: {
+    visible: {
+      type: Boolean,
+      default: false
+    }
+  },
+  data() {
+    return {
+      tasks: [
+        {
+          title: '负债累累的冒险家',
+          description: '终极目标就是偿还移民的债务,做一个无债一身轻的玩家',
+          rewards: [
+            {
+              icon: '/static/icons/coin.png',
+              text: '金币×3000'
+            },
+            {
+              icon: '/static/icons/exp.png',
+              text: '经验×3000'
+            },
+            {
+              icon: '/static/icons/blueprint.png',
+              text: '升级房屋图纸×1'
+            }
+          ],
+          date: '2024/04/08',
+          status: 'completed'
+        },
+        {
+          title: '负债累累的冒险家',
+          description: '终极目标就是偿还移民的债务,做一个无债一身轻的玩家',
+          rewards: [
+            {
+              icon: '/static/icons/coin.png',
+              text: '金币×3000'
+            },
+            {
+              icon: '/static/icons/exp.png',
+              text: '经验×3000'
+            },
+            {
+              icon: '/static/icons/blueprint.png',
+              text: '升级房屋图纸×1'
+            }
+          ],
+          date: '2024/04/08',
+          status: 'available'
+        }
+      ]
+    }
+  },
+  methods: {
+    onClose() {
+      this.$emit('close')
+    },
+    handleTaskAction(task) {
+      if (task.status === 'completed') {
+        // 处理完成任务的逻辑
+        uni.showToast({
+          title: '任务已完成!',
+          icon: 'success'
+        })
+      } else {
+        // 处理领取任务的逻辑
+        uni.showToast({
+          title: '已领取任务',
+          icon: 'success'
+        })
+      }
+    }
+  }
+}
+</script> 
+
+
+
+<style lang="scss">
+@import './TaskDialog.scss';
+</style> 

+ 85 - 1
pages/isLand/homeLand.vue

@@ -13,6 +13,12 @@
       <view style="position: absolute;width: 670rpx;left:180rpx; bottom:130rpx;align-items: center;">
         <image class="hall-image" src="/static/island/building/1.png" mode="widthFix" style="width:670rpx; position: static;" @click="onHallClick" :animation="hallAnimationData">	</image>
       </view>
+
+      <!-- 主岛箭头 -->
+      <view class="main-arrow" @click="goToMainLand" :animation="mainArrowAnimation" :style="{ opacity: mainArrowVisible ? 1 : 0 }">
+        <image src="/static/island/main_arrow.png" mode="widthFix" style="width: 100rpx;"></image>
+        <text class="main-text">主岛</text>
+      </view>
     </view>
   
     <!-- 第一层:UI -->
@@ -22,6 +28,7 @@
           <button class="ui-button" @click="showInventory">背包</button>
           <button class="ui-button" @click="showCharacter">角色</button>
           <button class="ui-button" @click="showShop">商店</button>
+          <button class="ui-button" @click="showTask">任务</button>
         </view>
       </view>
     </view>
@@ -30,6 +37,7 @@
     <inventory-dialog :visible.sync="inventoryVisible" @close="onInventoryClose"></inventory-dialog>
     <character-dialog :visible.sync="characterVisible" @close="onCharacterClose"></character-dialog>
     <shop-dialog :visible.sync="shopVisible" @close="onShopClose" @buy="onShopBuy"></shop-dialog>
+    <task-dialog :visible.sync="taskVisible" @close="onTaskClose"></task-dialog>
   </view> 
 </template>
 
@@ -38,12 +46,14 @@
 import InventoryDialog from '@/components/dialogs/InventoryDialog.vue'
 import CharacterDialog from '@/components/dialogs/CharacterDialog.vue'
 import ShopDialog from '@/components/dialogs/ShopDialog.vue'
+import TaskDialog from './TaskDialog.vue'
 
 export default {
 	components: {
 		InventoryDialog,
 		CharacterDialog,
-		ShopDialog
+		ShopDialog,
+		TaskDialog
 	},
 	data() {
 		return {
@@ -65,6 +75,10 @@ export default {
 			inventoryVisible: false,
 			characterVisible: false,
 			shopVisible: false,
+			mainArrowAnimation: {},
+			mainArrowAnimating: false,
+			mainArrowVisible: false,
+			taskVisible: false,
 		}
 	},
 	onLoad() {
@@ -84,6 +98,11 @@ export default {
 		// 在组件渲染完成后获取图片尺寸
 		setTimeout(() => {
 			this.getImageSize();
+			// 延迟1秒后显示箭头并开始动画
+			setTimeout(() => {
+				this.mainArrowVisible = true;
+				this.startMainArrowAnimation();
+			}, 1000);
 		}, 300);
 	},
 	methods: {
@@ -275,7 +294,52 @@ export default {
 		},
 		onShopBuy(item) {
 			console.log('Buying item:', item)
+		},
+		showTask() {
+			console.log('Opening task board...')
+			this.taskVisible = true
+		},
+		onTaskClose() {
+			console.log('Closing task board...')
+			this.taskVisible = false
+		},
+		startMainArrowAnimation() {
+			if (this.mainArrowAnimating) return;
+			this.mainArrowAnimating = true;
+
+			const animation = uni.createAnimation({
+				duration: 1000,
+				timingFunction: 'ease-in-out',
+			});
+
+			const animate = () => {
+				if (!this.mainArrowAnimating) return;
+
+				animation.translateY(-20).step({ duration: 1000 });
+				this.mainArrowAnimation = animation.export();
+
+				setTimeout(() => {
+					animation.translateY(0).step({ duration: 1000 });
+					this.mainArrowAnimation = animation.export();
+
+					setTimeout(() => {
+						if (this.mainArrowAnimating) {
+							animate();
+						}
+					}, 1000);
+				}, 1000);
+			};
+
+			animate();
+		},
+		goToMainLand() {
+			uni.navigateTo({
+				url: '/pages/isLand/mainLand'
+			});
 		}
+	},
+	beforeDestroy() {
+		this.mainArrowAnimating = false;
 	}
 }
 </script>
@@ -332,4 +396,24 @@ export default {
     cursor: grabbing;
   }
 }
+
+.main-arrow {
+  position: absolute;
+  right: 100rpx;  // 调整到右边位置
+  top: 50%;
+  transform: translateY(-50%);
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  cursor: pointer;
+  z-index: 10;
+  transition: opacity 0.5s ease-in-out;  // 添加淡入效果
+
+  .main-text {
+    color: #ffffff;
+    font-size: 28rpx;
+    text-shadow: 2rpx 2rpx 4rpx rgba(0, 0, 0, 0.5);
+    margin-top: 10rpx;
+  }
+}
 </style> 

+ 2 - 2
pages/isLand/mainLand.scss

@@ -20,7 +20,7 @@
       left: 0;
       width: 100%;
       height: 100%;
-      background: linear-gradient(to bottom, #87CEEB, #4682B4); /* 渐变背景 */
+      background: linear-gradient(to bottom, #265f9f, #4682B4); /* 渐变背景 */
       z-index: 1; /* 最底层 */
       user-select: none; /* 禁止选中 */
       pointer-events: none; /* 禁止交互 */
@@ -31,7 +31,7 @@
       position: fixed;
       bottom: 0;
       left: 0;
-      width: 1536rpx;  /* 地图比屏幕大 */
+      width: 2048rpx;  /* 地图比屏幕大 */
       height: 100%;
       z-index: 2;   /* 中间层 */
       cursor: grab; /* 拖动光标 */

+ 97 - 5
pages/isLand/mainLand.vue

@@ -14,14 +14,44 @@
     <view class="map-layer" id="mapLayer" :style="{ transform: `translateX(${translateX}px)` }" @touchstart="touchStart" @touchmove="touchMove" @touchend="touchEnd" @mousedown="onmousedown" @mousemove="onmousemove" @mouseup="onmouseup">
       <!-- 这里可以放置地图元素(示例:一个方块) -->
       <!-- <view style="position: absolute; top: 30%; left: 30%; width: 100px; height: 100px; background: green;"></view> -->
-	  <image class="island-image" src="/static/island/island.png" mode="widthFix" style="width:1536rpx; bottom: 0rpx;left: 0rpx; position: absolute;"></image>
-	 	<view style="position: absolute;width: 670rpx;left: 680rpx; bottom:430rpx;align-items: center;">
-			<image class="house-image" src="/static/island/building/4.png" mode="widthFix" style="width:670rpx; position: static;" @click="onHouseClick" :animation="houseAnimationData">	</image>
+	  <image class="island-image" src="/static/island/mainLand.png" mode="widthFix" style="width:2048rpx; bottom: 0rpx;left: 0rpx; position: absolute;"></image>
+	 	<!-- <view style="position: absolute;width: 300rpx;left: 280rpx; bottom:230rpx;align-items: center;">
+			<image class="house-image" src="/static/island/building/4.png" mode="widthFix" style="width:300rpx; position: static;" @click="onHouseClick" :animation="houseAnimationData">	</image>
+		</view> -->
+
+		<view style="position: absolute;width: 360rpx;left:782rpx; bottom:385rpx;align-items: center;">
+			<image class="farm-image" src="/static/island/building/3.png" mode="widthFix" style="width:360rpx; position: static;" @click="onFarmClick" :animation="farmAnimationData">	</image>
+		</view>
+
+		<view style="position: absolute;width: 360rpx;left: 335rpx; bottom:835rpx;align-items: center;">
+			<image class="mine-image" src="/static/island/building/4.png" mode="widthFix" style="width:360rpx; position: static;">	</image>
+		</view>
+
+		<view style="position: absolute;width: 360rpx;left: 782rpx; bottom:720rpx;align-items: center;">
+			<image class="hall-image" src="/static/island/building/5.png" mode="widthFix" style="width:360rpx; position: static;">	</image>
+		</view>
+
+		<view style="position: absolute;width: 360rpx;left: 248rpx; bottom:488rpx;align-items: center;">
+			<image class="wood-image" src="/static/island/building/6.png" mode="widthFix" style="width:360rpx; position: static;">	</image>
 		</view>
 
-		<view style="position: absolute;width: 670rpx;left:180rpx; bottom:130rpx;align-items: center;">
-			<image class="farm-image" src="/static/island/building/2.png" mode="widthFix" style="width:670rpx; position: static;" @click="onFarmClick" :animation="farmAnimationData">	</image>
+		<view style="position: absolute;width: 360rpx;left: 860rpx; bottom:128rpx;align-items: center;">
+			<image class="shop-image" src="/static/island/building/2.png" mode="widthFix" style="width:360rpx; position: static;">	</image>
 		</view>
+
+		<view style="position: absolute;width: 360rpx;left: 1308rpx; bottom:128rpx;align-items: center;">
+			<image class="airport-image" src="/static/island/building/7.png" mode="widthFix" style="width:360rpx; position: static;">	</image>
+		</view>
+
+		<view style="position: absolute;width: 360rpx;left: 1400rpx; bottom:380rpx;align-items: center;">
+			<image class="house-image" src="/static/island/building/1.png" mode="widthFix" style="width:360rpx; position: static;">	</image>
+		</view>
+
+    <!-- 回家箭头 -->
+    <view class="home-arrow" @click="goHome" :animation="homeArrowAnimation" :style="{ opacity: homeArrowVisible ? 1 : 0 }">
+      <image src="/static/island/home_arrow.png" mode="widthFix" style="width: 100rpx;"></image>
+      <text class="home-text">回家</text>
+    </view>
 	</view>
   
     <!-- 第一层:UI -->
@@ -79,6 +109,9 @@ export default {
 			characterVisible: false,
 			shopVisible: false,
 			farmVisible: false,
+			homeArrowAnimation: {},
+			homeArrowAnimating: false,
+			homeArrowVisible: false,
 		}
 	},
 	onLoad() {
@@ -98,6 +131,11 @@ export default {
 		// 在组件渲染完成后获取图片尺寸
 		setTimeout(() => {
 			this.getImageSize();
+			// 延迟1秒后显示箭头并开始动画
+			setTimeout(() => {
+				this.homeArrowVisible = true;
+				this.startHomeArrowAnimation();
+			}, 1000);
 		}, 300);
 	},
 	methods: {
@@ -303,6 +341,40 @@ export default {
 		onFarmClose() {
 			console.log('Closing farm dialog...')
 			this.farmVisible = false
+		},
+		startHomeArrowAnimation() {
+			if (this.homeArrowAnimating) return;
+			this.homeArrowAnimating = true;
+
+			const animation = uni.createAnimation({
+				duration: 1000,
+				timingFunction: 'ease-in-out',
+			});
+
+			const animate = () => {
+				if (!this.homeArrowAnimating) return;
+
+				animation.translateY(-20).step({ duration: 1000 });
+				this.homeArrowAnimation = animation.export();
+
+				setTimeout(() => {
+					animation.translateY(0).step({ duration: 1000 });
+					this.homeArrowAnimation = animation.export();
+
+					setTimeout(() => {
+						if (this.homeArrowAnimating) {
+							animate();
+						}
+					}, 1000);
+				}, 1000);
+			};
+
+			animate();
+		},
+		goHome() {
+			uni.navigateTo({
+				url: '/pages/isLand/homeLand'
+			});
 		}
 	}
 }
@@ -360,4 +432,24 @@ export default {
     cursor: grabbing;
   }
 }
+
+.home-arrow {
+  position: absolute;
+  left: 100rpx;  // 调整到左边位置
+  top: 50%;
+  transform: translateY(-50%);
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  cursor: pointer;
+  z-index: 10;
+  transition: opacity 0.5s ease-in-out;  // 添加淡入效果
+
+  .home-text {
+    color: #ffffff;
+    font-size: 28rpx;
+    text-shadow: 2rpx 2rpx 4rpx rgba(0, 0, 0, 0.5);
+    margin-top: 10rpx;
+  }
+}
 </style> 

BIN
static/island/building/1.png


BIN
static/island/building/2.png


BIN
static/island/building/3.png


BIN
static/island/building/4.png


BIN
static/island/building/5.png


BIN
static/island/building/6.png


BIN
static/island/building/7.png


BIN
static/island/mainLand.png