index.vue 10.9 KB
<!--
 * @Date: 2025-08-27 17:44:53
 * @LastEditors: hookehuyr hookehuyr@gmail.com
 * @LastEditTime: 2025-08-28 21:18:07
 * @FilePath: /lls_program/src/pages/EditFamily/index.vue
 * @Description: 文件描述
-->
<template>
  <view class="min-h-screen flex flex-col bg-white">
    <!-- <AppHeader title="编辑家庭" /> -->
    <view class="flex-1 px-4 py-6 overflow-auto">
      <view class="mb-6">
        <!-- <view class="text-gray-600 mb-6">
          请填写家庭信息,创建您的专属家庭空间
        </view> -->
        <!-- Family Name -->
        <view class="mb-6">
          <view class="bg-white rounded-lg border border-gray-200 p-4">
            <view class="block text-lg font-medium mb-2">家庭名称</view>
            <input
              type="text"
              v-model="familyName"
              class="w-full text-gray-600 focus:outline-none"
              placeholder="请输入家庭名称"
            />
          </view>
        </view>
        <!-- Family Introduction -->
        <view class="mb-6">
          <view class="bg-white rounded-lg border border-gray-200 p-4">
            <view class="block text-lg font-medium mb-2">家庭介绍</view>
            <textarea
              v-model="familyIntro"
              class="w-full text-gray-600 focus:outline-none resize-none"
              placeholder="请输入您家庭的特色、成员特点等家庭标签"
              :rows="2"
            />
          </view>
        </view>
        <!-- Family Size -->
        <view class="mb-6">
          <view class="bg-white rounded-lg border border-gray-200 p-4">
            <view class="block text-lg font-medium mb-4">家庭规模</view>
            <view class="flex gap-2">
              <view
                v-for="size in familySizes"
                :key="size"
                @click="familySize = size"
                :class="[
                  'flex-1 py-3 rounded-lg border text-center',
                  familySize === size
                    ? 'border-blue-500 bg-blue-50 text-blue-500'
                    : 'border-gray-200 text-gray-700'
                ]"
              >
                {{ size }}
              </view>
            </view>
          </view>
        </view>
        <!-- Family Motto -->
        <view class="mb-6">
          <view class="bg-white rounded-lg border border-gray-200 p-4">
            <view class="flex justify-between items-center mb-4">
              <view class="block text-lg font-medium">家训口令</view>
              <!-- <view
                @click="generateRandomMotto"
                class="px-3 py-1 bg-blue-100 text-blue-600 rounded-full text-sm"
              >
                随机生成
              </view> -->
            </view>
            <view class="flex gap-2 mb-4">
              <view v-for="(char, index) in familyMotto" :key="index" class="flex-1">
                <view class="w-full aspect-square flex items-center justify-center bg-gray-100 rounded-lg">
                  <input
                    type="text"
                    v-model="familyMotto[index]"
                    :placeholder="familyMottoPlaceholder[index]"
                    @input="(e) => handleInputChange(index, e.target.value)"
                    @focus="focusedIndex = index"
                    @blur="handleBlur(index)"
                    class="w-full h-full bg-transparent text-center"
                    style="font-size: 38rpx;"
                    :cursorSpacing="100"
                  />
                </view>
              </view>
              <!-- <view class="flex-1 flex items-center justify-center">
                <view class="w-full aspect-square flex items-center justify-center bg-gray-100 rounded-lg text-blue-500">
                  <Edit size="20" />
                </view>
              </view> -->
            </view>
            <view class="flex items-center text-sm text-gray-600">
              <Tips size="16" class="text-yellow-500 mr-2" />
              <view>设置有意义的家训口令,便于家人记忆和加入</view>
            </view>
          </view>
        </view>
        <!-- Family Avatar -->
        <view class="mb-10">
          <view class="bg-white rounded-lg border border-gray-200 p-4">
            <view class="block text-lg font-medium mb-2">
              家庭头像(选填)
            </view>
            <!-- 已上传头像显示 -->
            <view v-if="familyAvatar" class="mb-4">
              <view class="relative inline-block">
                <image
                  :src="familyAvatar"
                  class="w-24 h-24 rounded-lg object-cover"
                  mode="aspectFill"
                  @tap="previewAvatar"
                />
                <view
                  @click="deleteAvatar"
                  class="absolute -top-2 -right-2 w-4 h-4 bg-red-500 rounded-full flex items-center justify-center"
                >
                  <view class="text-white text-xs">×</view>
                </view>
              </view>
            </view>
            <!-- 上传区域 -->
            <view
              v-if="!familyAvatar"
              class="border border-dashed border-gray-300 rounded-lg p-6 flex flex-col items-center justify-center"
              @click="chooseImage"
            >
              <view class="text-gray-400 mb-2">
                <Photograph size="24" />
              </view>
              <view class="text-center text-gray-400">点击上传图片</view>
              <view class="text-center text-gray-400 text-xs mt-1">
                支持jpg、png格式,大小不超过10MB
              </view>
            </view>
          </view>
        </view>
      </view>
      <!-- Submit Button -->
      <view
        @click="handleSaveFamily"
        class="w-full py-3 bg-blue-500 text-white text-lg font-medium rounded-lg flex items-center justify-center"
      >
        保存
      </view>
    </view>



    <!-- 图片预览 -->
    <nut-image-preview
      v-model:show="previewVisible"
      :images="previewImages"
      :init-no="previewIndex"
      @close="closePreview"
    />
  </view>
</template>

<script setup>
import { ref, onMounted } from 'vue';
import Taro from '@tarojs/taro';
import { Edit, Tips, Photograph } from '@nutui/icons-vue-taro';
// import AppHeader from '../../components/AppHeader.vue';
import BASE_URL from '@/utils/config';

const familyName = ref('');
const familyIntro = ref('');
const familySize = ref('3-5人');
const familyMotto = ref(['', '', '', '']);
const familyMottoPlaceholder = ref(['孝', '敬', '和', '睦']);
const familySizes = ['2人', '3-5人', '6人+'];
const familyAvatar = ref('');
const focusedIndex = ref(-1);

// 图片预览相关
const previewVisible = ref(false);
const previewImages = ref([]);
const previewIndex = ref(0);

onMounted(() => {
  Taro.setNavigationBarTitle({ title: '编辑家庭' });
  // Mock data for current family information
  familyName.value = '幸福一家';
  familyIntro.value = '我们是相亲相爱的一家人';
  familySize.value = '3-5人';
  familyMotto.value = ['家', '和', '万', '事'];
  familyAvatar.value = 'https://img.yzcdn.cn/vant/cat.jpeg';
});

/**
 * 处理输入变化
 */
const handleInputChange = (index, value) => {
  // 只保留第一个有效字符(汉字、数字、大小写字母)
  const validChar = value.match(/[\u4e00-\u9fa5a-zA-Z0-9]/)?.[0] || '';
  familyMotto.value[index] = validChar;
};

/**
 * 处理失焦事件
 */
const handleBlur = (index) => {
  focusedIndex.value = -1;
  // 确保只保留有效字符(汉字、数字、大小写字母)
  const value = familyMotto.value[index];
  const validChar = value.match(/[\u4e00-\u9fa5a-zA-Z0-9]/)?.[0] || '';
  familyMotto.value[index] = validChar;
};

/**
 * 显示提示信息
 */
const showToast = (message, type = 'success') => {
  const icon = type === 'error' ? 'error' : 'success';
  Taro.showToast({
    title: message,
    icon: icon,
    duration: 2000
  });
};

/**
 * 选择图片
 */
const chooseImage = () => {
  Taro.chooseImage({
    count: 1,
    sizeType: ['compressed'],
    sourceType: ['album', 'camera'],
    success: function (res) {
      const tempFilePath = res.tempFilePaths[0];

      // 检查文件大小(10MB = 10 * 1024 * 1024 bytes)
      Taro.getFileInfo({
        filePath: tempFilePath,
        success: function (fileInfo) {
          if (fileInfo.size > 10 * 1024 * 1024) {
            showToast('图片大小不能超过10MB', 'error');
            return;
          }
          uploadImage(tempFilePath);
        },
        fail: function () {
          // 如果获取文件信息失败,直接上传
          uploadImage(tempFilePath);
        }
      });
    },
    fail: function () {
      showToast('选择图片失败', 'error');
    }
  });
};

/**
 * 上传图片到服务器
 */
const uploadImage = (filePath) => {
  // 显示上传中提示
  Taro.showLoading({
    title: '上传中',
    mask: true
  });

  wx.uploadFile({
    url: BASE_URL + '/admin/?m=srv&a=upload',
    filePath,
    name: 'file',
    header: {
      'content-type': 'multipart/form-data',
    },
    success: function (res) {
      let upload_data = JSON.parse(res.data);
      Taro.hideLoading({
        success: () => {
          if (res.statusCode === 200) {
            familyAvatar.value = upload_data.data.src;
            showToast('上传成功', 'success');
          } else {
            showToast('服务器错误,稍后重试!', 'error');
          }
        },
      });
    },
    fail: function (res) {
      Taro.hideLoading({
        success: () => {
          showToast('上传失败,稍后重试!', 'error');
        }
      });
    }
  });
};

/**
 * 预览头像
 */
const previewAvatar = () => {
  if (!familyAvatar.value) {
    showToast('暂无图片可预览', 'error');
    return;
  }
  previewImages.value = [{ src: familyAvatar.value }];
  previewIndex.value = 0;
  previewVisible.value = true;
};

/**
 * 删除头像
 */
const deleteAvatar = () => {
  familyAvatar.value = '';
  showToast('头像已删除', 'success');
};

/**
 * 关闭预览
 */
const closePreview = () => {
  previewVisible.value = false;
};

/**
 * 表单验证
 */
const validateForm = () => {
  if (!familyName.value.trim()) {
    showToast('请输入家庭名称', 'error');
    return false;
  }

  if (!familyIntro.value.trim()) {
    showToast('请输入家庭介绍', 'error');
    return false;
  }

  if (!familySize.value) {
    showToast('请选择家庭规模', 'error');
    return false;
  }

  // 检查家训口令是否完整填写
  const mottoComplete = familyMotto.value.every(char => char.trim() !== '');
  if (!mottoComplete) {
    showToast('请完整填写家训口令', 'error');
    return false;
  }

  return true;
};

/**
 * 保存家庭信息
 */
const handleSaveFamily = () => {
  if (!validateForm()) {
    return;
  }

  // 在实际应用中,这里会调用API保存家庭信息
  showToast('家庭信息保存成功', 'success');

  setTimeout(() => {
    Taro.navigateBack();
  }, 1500);
};
</script>