<template>
  <view class="make-page" :style="{ paddingTop: getBavBarHeight() + 'px' }">
    <!-- 预览卡片 -->
    <view class="card-preview">
      <image
        class="card-bg"
        :src="currentTemplate?.imageUrl"
        mode="aspectFill"
      />
      <view class="card-overlay">
        <view class="watermark">年禧集.马年春节祝福</view>
        <view class="title">
          <text class="main">新春快乐</text>
          <text class="sub">2026 YEAR OF THE HORSE</text>
        </view>
        <view
          class="bubble"
          @tap="activeTool = 'text'"
          :style="{ marginTop: 80 + bubbleOffsetY + 'rpx' }"
        >
          <text
            class="bubble-text"
            :style="{
              color: selectedColor,
              fontFamily: selectedFont.family,
              fontSize: fontSize + 'rpx',
              lineHeight: fontSize * 1.5 + 'rpx',
            }"
            >{{ targetName + "\n  " + blessingText.content }}</text
          >
        </view>
        <view class="user" :style="{ left: 160 + userOffsetX + 'rpx', bottom: 40 - userOffsetY + 'rpx' }">
          <image class="avatar" :src="userAvatar" mode="aspectFill" />
          <view class="user-info">
            <text class="user-name" :style="{ color: signatureColor }">{{
              signatureName
            }}</text>
            <text class="user-desc" :style="{ color: signatureColor }"
              >送上祝福</text
            >
          </view>
        </view>
      </view>
    </view>

    <view class="tip-line">
      <text>分享或保存即可去除水印</text>
    </view>

    <!-- 编辑工具区 -->
    <view class="editor-panel">
      <view class="drag-handle"></view>

      <!-- 底部操作 -->
      <view class="bottom-actions">
        <button class="btn secondary" @tap="preview">
          <uni-icons type="cloud-download" size="20" color="#888"></uni-icons>
          <view>保存</view>
        </button>
        <button open-type="share" class="btn primary">
          <uni-icons
            type="paperplane-filled"
            size="20"
            color="#fff"
          ></uni-icons>
          <view>分享给好友</view>
        </button>
      </view>

      <!-- 功能入口 -->
      <view class="tools">
        <view
          v-for="(tool, idx) in tools"
          :key="idx"
          class="tool-item"
          :class="{ active: activeTool === tool.type }"
          @tap="activeTool = tool.type"
        >
          <view class="tool-icon">{{ tool.icon }}</view>
          <text class="tool-text">{{ tool.text }}</text>
        </view>
      </view>

      <!-- 模板区 -->
      <view v-if="activeTool === 'template'" class="section">
        <view class="section-title">
          <text>热门模板</text>
        </view>
        <view class="tpl-scroll">
          <view class="tpl-grid">
            <view
              v-for="(tpl, i) in templates"
              :key="i"
              class="tpl-card"
              :class="{ selected: tpl?.id === currentTemplate?.id }"
              @tap="applyTemplate(tpl)"
            >
              <image :src="tpl.imageUrl" class="tpl-cover" mode="aspectFill" />
              <view class="tpl-name">{{ tpl.name }}</view>
              <view v-if="tpl?.id === currentTemplate?.id" class="tpl-check"
                >✔</view
              >
            </view>
          </view>
          <view v-if="loadingTemplates" class="loading-more">加载中...</view>
          <view
            v-else-if="!hasMoreTemplates && templates.length > 0"
            class="no-more"
            >没有更多了</view
          >
        </view>
      </view>

      <!-- 文字编辑 -->
      <view v-if="activeTool === 'text'" class="section text-edit-section">
        <!-- 祝贺对象 -->
        <view class="form-item">
          <text class="label">祝贺对象</text>
          <input
            class="input-box"
            v-model="targetName"
            placeholder="请输入称呼"
            placeholder-style="color:#ccc"
            maxlength="5"
          />
        </view>

        <!-- 祝福语库 -->
        <view class="form-item">
          <view class="label-row">
            <text class="label">祝福语库</text>
            <view class="refresh-btn" @tap="refreshGreetings">
              <text class="refresh-icon">↻</text> 换一批
            </view>
          </view>
          <scroll-view scroll-x class="greeting-scroll" show-scrollbar="false">
            <view class="greeting-list">
              <view
                v-for="(text, index) in displayedGreetings"
                :key="index"
                class="greeting-card"
                :class="{ active: blessingText === text.content }"
                @tap="selectGreeting(text)"
              >
                <text class="greeting-text">{{ text.content }}</text>
                <view
                  v-if="blessingText.content === text.content"
                  class="check-mark"
                  >✔</view
                >
              </view>
            </view>
          </scroll-view>
        </view>

        <!-- 署名 -->
        <view class="form-item">
          <text class="label">署名</text>
          <view class="input-wrapper">
            <input
              class="input-box"
              v-model="signatureName"
              placeholder="请输入署名"
              placeholder-style="color:#ccc"
              maxlength="5"
            />
            <text class="edit-icon">✎</text>
          </view>
        </view>

        <!-- 字体选择 -->
        <view class="form-item">
          <text class="label">字体样式</text>
          <scroll-view scroll-x class="font-scroll" show-scrollbar="false">
            <view class="font-list">
              <view
                v-for="(font, index) in fontList"
                :key="index"
                class="font-item"
                :class="{ active: selectedFont.family === font.family }"
                @tap="changeFont(font)"
              >
                <text :style="{ fontFamily: font.family }">{{
                  font.name
                }}</text>
              </view>
            </view>
          </scroll-view>
        </view>

        <!-- 字体大小 -->
        <view class="form-item">
          <text class="label">字体大小</text>
          <slider
            :value="fontSize"
            min="24"
            max="64"
            show-value
            @change="(e) => (fontSize = e.detail.value)"
            activeColor="#ff3b30"
          />
        </view>

        <!-- 文字颜色 -->
        <view class="form-item">
          <text class="label">祝福语颜色</text>
          <view class="color-list">
            <view
              v-for="(color, index) in textColors"
              :key="index"
              class="color-item"
              :style="{ background: color }"
              @tap="selectedColor = color"
            >
              <view v-if="selectedColor === color" class="color-check">✔</view>
            </view>
          </view>
        </view>

        <!-- 署名颜色 -->
        <view class="form-item">
          <text class="label">署名颜色</text>
          <view class="color-list">
            <view
              v-for="(color, index) in textColors"
              :key="index"
              class="color-item"
              :style="{ background: color }"
              @tap="signatureColor = color"
            >
              <view v-if="signatureColor === color" class="color-check">✔</view>
            </view>
          </view>
        </view>
      </view>

      <!-- 位置调整 -->
      <view v-if="activeTool === 'position'" class="section">
        <view class="form-item">
          <text class="label">祝福语位置 (上下)</text>
          <slider
            :value="bubbleOffsetY"
            min="-200"
            max="200"
            show-value
            @change="(e) => (bubbleOffsetY = e.detail.value)"
            activeColor="#ff3b30"
          />
        </view>
        <view class="form-item">
          <text class="label">署名位置 (左右)</text>
          <slider
            :value="userOffsetX"
            min="-200"
            max="200"
            show-value
            @change="(e) => (userOffsetX = e.detail.value)"
            activeColor="#ff3b30"
          />
        </view>
        <view class="form-item">
          <text class="label">署名位置 (上下)</text>
          <slider
            :value="userOffsetY"
            min="-200"
            max="200"
            show-value
            @change="(e) => (userOffsetY = e.detail.value)"
            activeColor="#ff3b30"
          />
        </view>
      </view>

      <!-- 头像挂饰 -->
      <view v-if="activeTool === 'avatar'" class="section">
        <view class="section-title"><text>头像挂饰</text></view>
        <view class="row">
          <button class="btn" @tap="toggleAvatarDecor">切换挂饰</button>
        </view>
      </view>
    </view>

    <canvas
      type="2d"
      id="cardCanvas"
      class="hidden-canvas"
      style="width: 540px; height: 960px"
    />

    <LoginPopup ref="loginPopupRef" @logind="handleLogind" />
  </view>
</template>

<script setup>
import { ref, computed } from "vue";
import { getBavBarHeight, getDeviceInfo } from "@/utils/system";
import { generateObjectId } from "@/utils/common";

import {
  createCardTmp,
  updateCard,
  getCardTemplateList,
  getCardTemplateContentList,
} from "@/api/make";
import { createShareToken, abilityCheck, getShareReward } from "@/api/system";
import {
  onShareAppMessage,
  onLoad,
  onReachBottom,
  onShow,
} from "@dcloudio/uni-app";
import { useUserStore } from "@/stores/user";
import LoginPopup from "@/components/LoginPopup/LoginPopup.vue";
import { saveRecordRequest, uploadImage } from "@/utils/common.js";

const userStore = useUserStore();
const loginPopupRef = ref(null);
const isLoggedIn = computed(() => !!userStore.userInfo.nickName);

const templatePage = ref(1);
const loadingTemplates = ref(false);
const hasMoreTemplates = ref(true);
const cardId = ref("");

const targetName = ref("祝您");
const signatureName = ref(userStore?.userInfo?.nickName || "xxx");
const userAvatar = ref(
  userStore?.userInfo?.avatarUrl ||
    "https://file.lihailezzc.com/resource/b48c41054c2633c478463ac1b1f1ca23.png",
);

const blessingText = ref({});
const fontSize = ref(32);

const textColors = [
  "#ffffff",
  "#000000",
  "#ff3b30",
  "#F5A623",
  "#8B572A",
  "#D0021B",
  "#F8E71C",
  "#7ED321",
  "#4A90E2",
  "#9013FE",
  "#FFC0CB",
];
const selectedColor = ref("#ffffff");
const signatureColor = ref("#ffffff");

const fontList = [
  { name: "默认", family: "PingFang SC", url: "" },
  {
    name: "毛笔",
    family: "MaoBi",
    url: "https://file.lihailezzc.com/MaShanZheng-Regular.ttf", // 示例地址
  },
  {
    name: "手写",
    family: "ShouXie",
    url: "https://file.lihailezzc.com/ZhiMangXing-Regular.ttf", // 示例地址
  },
  {
    name: "可爱",
    family: "KeAi",
    url: "https://file.lihailezzc.com/ZCOOLKuaiLe-Regular.ttf", // 示例地址
  },
  {
    name: "草书",
    family: "LiuJianMaoCao",
    url: "https://file.lihailezzc.com/LiuJianMaoCao-Regular.ttf", // 示例地址
  },
];
const selectedFont = ref(fontList[0]);
const loadedFonts = ref(new Set()); // 记录已加载的字体

const changeFont = (font) => {
  // 1. 如果是默认字体或已加载过的字体，直接应用
  if (!font.url || loadedFonts.value.has(font.family)) {
    selectedFont.value = font;
    return;
  }

  // 2. 否则加载字体
  uni.showLoading({ title: "加载字体中", mask: true });
  uni.loadFontFace({
    global: true,
    family: font.family,
    source: `url("${font.url}")`,
    scopes: ["webview", "native"],
    success: () => {
      selectedFont.value = font;
      loadedFonts.value.add(font.family); // 标记为已加载
      uni.hideLoading();
    },
    fail: (err) => {
      console.error(err);
      uni.hideLoading();
      // 如果加载失败，可以尝试直接设置（有些情况可能已经缓存或本地支持）
      // 或者提示用户
      uni.showToast({ title: "字体加载失败", icon: "none" });
    },
  });
};

const greetingLib = ref([]);
const greetingIndex = ref(0);

const bubbleOffsetY = ref(0);
const userOffsetX = ref(0);
const userOffsetY = ref(0);

onLoad((options) => {
  getTemplateList();
  getTemplateContentList();
});

onShow(() => {
  const recommendData = uni.getStorageSync("RECOMMEND_CARD_DATA");
  if (recommendData) {
    uni.removeStorageSync("RECOMMEND_CARD_DATA");
    if (recommendData.imageUrl) {
      const tpl = {
        id: recommendData.recommendId,
        imageUrl: recommendData.imageUrl,
        name: "推荐模板", // 暂时使用通用名称，如果需要可以从接口获取更多信息
      };

      // 切换到模板 Tab
      activeTool.value = "template";

      // 应用模板
      currentTemplate.value = tpl;

      // 如果模板列表中存在，更新引用
      const found = templates.value.find((t) => t.id === tpl.id);
      if (found) currentTemplate.value = found;
    }
  }

  const tempBlessing = uni.getStorageSync("TEMP_BLESSING_TEXT");
  if (tempBlessing) {
    blessingText.value = { content: tempBlessing, id: "" };
    uni.removeStorageSync("TEMP_BLESSING_TEXT");
    activeTool.value = "text";
  }
});

onReachBottom(() => {
  if (activeTool.value === "template") {
    loadMoreTemplates();
  }
});

const handleLogind = async () => {};

const createCard = () => {
  const id = generateObjectId();
  createCardTmp({ id });
  cardId.value = id;
  return id;
};

const getTemplateList = async (isLoadMore = false) => {
  if (loadingTemplates.value || (!hasMoreTemplates.value && isLoadMore)) return;

  loadingTemplates.value = true;
  try {
    const res = await getCardTemplateList(templatePage.value);

    // 兼容数组或对象列表格式
    const list = Array.isArray(res) ? res : res.list || [];

    if (list.length > 0) {
      if (isLoadMore) {
        templates.value = [...templates.value, ...list];
      } else {
        templates.value = list;
        // 初始加载时设置第一个为当前选中
        if (list.length > 0 && !currentTemplate.value) {
          currentTemplate.value = list[0];
        }
      }

      // 判断是否还有更多
      if (typeof res.hasNext !== "undefined") {
        hasMoreTemplates.value = res.hasNext;
      } else {
        // 如果没有 hasNext 字段，根据返回数量简单判断
        hasMoreTemplates.value = list.length >= 8; // 假设每页 10 条
      }

      if (hasMoreTemplates.value) {
        templatePage.value++;
      }
    } else {
      if (!isLoadMore) templates.value = [];
      hasMoreTemplates.value = false;
    }
  } catch (error) {
    console.error("加载模板失败:", error);
  } finally {
    loadingTemplates.value = false;
  }
};

const getTemplateContentList = async () => {
  const res = await getCardTemplateContentList();
  if (res.length) {
    greetingLib.value = res;
    displayedGreetings.value = greetingLib.value.slice(0, 2);
    if (!blessingText.value.content) {
      blessingText.value = greetingLib.value[0] || {};
    }
  }
};

const loadMoreTemplates = () => {
  getTemplateList(true);
};

onShareAppMessage(async () => {
  getShareReward({ scene: "card_generate" });
  if (!isLoggedIn.value) {
    return {
      title: "新春祝福",
      path: "/pages/index/index",
    };
  }
  // 1. 确保有 cardId (如果内容有变动，最好是新建)
  const id = createCard();
  if (!id) {
    return {
      title: "新春祝福",
      path: "/pages/index/index",
    };
  }

  const deviceInfo = getDeviceInfo();
  const shareTokenRes = await createShareToken({
    scene: "card_generate",
    targetId: id,
    ...deviceInfo,
  });
  shareOrSave(id);
  return {
    title: "新春祝福",
    path: "/pages/detail/index?shareToken=" + shareTokenRes.shareToken,
    imageUrl: currentTemplate.value?.imageUrl || "/static/images/bg.jpg",
  };
});

const displayedGreetings = ref([]);

const refreshGreetings = () => {
  if (!greetingLib.value.length) return;

  const nextIndex = (greetingIndex.value + 2) % greetingLib.value.length;
  greetingIndex.value = nextIndex;

  let next = greetingLib.value.slice(nextIndex, nextIndex + 2);
  if (next.length < 2) {
    next = [...next, ...greetingLib.value.slice(0, 2 - next.length)];
  }
  displayedGreetings.value = next;
};

const selectGreeting = (text) => {
  blessingText.value = text;
};

const tools = [
  { type: "template", text: "模板", icon: "▦" },
  { type: "text", text: "文字", icon: "文" },
  { type: "position", text: "位置", icon: "图" },
  // { type: "avatar", text: "头像挂饰", icon: "饰" },
];
const activeTool = ref("template");

const templates = ref([]);

const currentTemplate = ref(templates.value[0]);

const applyTemplate = (tpl) => {
  currentTemplate.value = tpl;
};

const pickImage = () => {
  uni.chooseImage({
    count: 1,
    success: (res) => {
      const path = res.tempFilePaths?.[0];
      if (path)
        currentTemplate.value = { ...currentTemplate.value, cover: path };
    },
  });
};

const resetBackground = () => {
  currentTemplate.value = templates.value[0];
};

const toggleAvatarDecor = () => {
  uni.showToast({ title: "挂饰功能即将上线～", icon: "none" });
};

const preview = async () => {
  if (!isLoggedIn.value) {
    loginPopupRef.value.open();
    return;
  }
  const abilityRes = await abilityCheck("card_generate");
  if (!abilityRes.canUse) {
    if (
      abilityRes?.blockType === "need_share" &&
      abilityRes?.message === "分享可继续"
    ) {
      uni.showToast({
        title: "分享给好友可继续使用",
        icon: "none",
      });
      return;
    }
    uni.showToast({
      title: "您今日祝福卡下载次数已用完，直接分享给好友或者明日再试",
      icon: "none",
    });
    return;
  }
  const tempPath = await saveByCanvas(true);
  id = createCard();
  shareOrSave(id);
  saveRecordRequest(tempPath, id, "card_generate");

  // uni.showToast({ title: '已保存到相册', icon: 'checkmarkempty' })
};

const shareOrSave = async (id) => {
  if (!id) id = createCard();

  const tempPath = await saveByCanvas(false);
  const imageUrl = await uploadImage(tempPath);

  updateCard({
    id,
    imageUrl,
    status: 1,
    blessingId: blessingText.value?.id || "",
    blessingTo: targetName.value,
    blessingFrom: signatureName.value,
    templateId: currentTemplate.value?.id || "",
  });
};

const showMore = () => {
  uni.showToast({ title: "更多模板即将上线～", icon: "none" });
};

const saveByCanvas = async (save = true) => {
  return new Promise((resolve, reject) => {
    const query = uni.createSelectorQuery();
    query
      .select("#cardCanvas")
      .fields({ node: true, size: true })
      .exec(async (res) => {
        if (!res[0] || !res[0].node) {
          reject("Canvas not found");
          return;
        }

        const canvas = res[0].node;
        const ctx = canvas.getContext("2d");

        // 初始化画布尺寸
        const dpr = uni.getSystemInfoSync().pixelRatio;
        // 保持 540x960 的逻辑尺寸，为了清晰度可以考虑 * dpr，
        // 但为了保持和原来一致的输出尺寸，这里先固定物理尺寸
        // 如果要高清，可以 set width = 540 * dpr，然后 scale(dpr, dpr)
        // 这里为了简单兼容原逻辑，我们让物理尺寸等于逻辑尺寸
        canvas.width = 540;
        canvas.height = 960;

        // 画布尺寸（rpx 转 px）
        const W = 540;
        const H = 960;

        // 辅助函数：加载图片为 Image 对象
        const loadCanvasImage = (url) => {
          return new Promise((resolve, reject) => {
            const img = canvas.createImage();
            img.onload = () => resolve(img);
            img.onerror = (e) => reject(e);
            img.src = url;
          });
        };

        try {
          // 1️⃣ 画背景
          // ⭐ 先加载背景图
          const [bgImg, avatarImg] = await Promise.all([
            loadCanvasImage(currentTemplate?.value?.imageUrl),
            loadCanvasImage(userAvatar.value),
          ]);

          ctx.drawImage(bgImg, 0, 0, W, H);

          // 2️⃣ 半透明遮罩（和你 UI 一致）
          ctx.fillStyle = "rgba(0,0,0,0.08)";
          ctx.fillRect(0, 0, W, H);

          // 3️⃣ 标题
          ctx.fillStyle = "#ffffff";
          ctx.font = "42px sans-serif"; // 默认字体
          ctx.textAlign = "center";
          ctx.textBaseline = "alphabetic"; // Canvas 2D 默认是 alphabetic
          ctx.fillText("新春快乐", W / 2, 120);

          ctx.font = "22px sans-serif";
          ctx.globalAlpha = 0.9;
          ctx.fillText("2026 YEAR OF THE HORSE", W / 2, 165);
          ctx.globalAlpha = 1;

          // 4️⃣ 祝福语气泡
          drawBubbleText(ctx, {
            text: targetName.value + "\n  " + blessingText.value.content,
            x: 70,
            y: 260 + bubbleOffsetY.value,
            maxWidth: 400,
            fontSize: fontSize.value,
            lineHeight: fontSize.value * 1.5,
            backgroundColor: "rgba(255,255,255,0.85)",
            textColor: selectedColor.value,
            fontFamily: selectedFont.value.family,
          });

          drawUserBubble(ctx, {
            x: 160 + userOffsetX.value,
            y: H - 136 + userOffsetY.value,
            avatarImg: avatarImg, // 传入 Image 对象
            username: signatureName.value,
            desc: "送上祝福",
            textColor: signatureColor.value,
          });

          // 6️⃣ 输出
          uni.canvasToTempFilePath({
            canvas: canvas, // Canvas 2D 必须传 canvas 实例
            width: W,
            height: H,
            destWidth: W,
            destHeight: H,
            success: (res) => {
              if (save) saveImage(res.tempFilePath);
              resolve(res.tempFilePath);
            },
            fail: (err) => reject(err),
          });
        } catch (error) {
          console.error("Canvas draw error:", error);
          reject(error);
        }
      });
  });
};

const loadImage = (url) => {
  // 此函数保留给其他可能用到的地方，但 saveByCanvas 内部使用 loadCanvasImage
  return new Promise((resolve, reject) => {
    uni.getImageInfo({
      src: url,
      success: (res) => {
        resolve(res.path); // 本地路径
      },
      fail: (err) => {
        reject(err);
      },
    });
  });
};

const saveImage = (path) => {
  uni.saveImageToPhotosAlbum({
    filePath: path,
    success() {
      uni.showToast({ title: "已保存到相册" });
    },
    fail() {
      uni.showModal({
        title: "提示",
        content: "请授权保存到相册",
      });
    },
  });
};

function drawBubbleText(ctx, options) {
  const {
    text,
    x,
    y,
    maxWidth = 400,
    padding = 24,
    lineHeight = 42,
    radius = 24,
    backgroundColor = "rgba(255,255,255,0.9)",
    textColor = "#fff",
    fontSize = 32,
    fontFamily = "PingFang SC",
  } = options;
  if (!text) return;

  ctx.fillStyle = textColor;
  ctx.font = `${fontSize}px '${fontFamily}'`;
  ctx.textAlign = "left";
  ctx.textBaseline = "top";

  // 1️⃣ 文本自动换行
  const paragraphs = text.split("\n");

  const lines = [];

  /** ② 每一段再自动换行 */
  paragraphs.forEach((p) => {
    let line = "";
    for (let char of p) {
      const testLine = line + char;
      const { width } = ctx.measureText(testLine);
      if (width > maxWidth) {
        lines.push(line);
        line = char;
      } else {
        line = testLine;
      }
    }
    if (line) lines.push(line);
  });

  // 2️⃣ 计算气泡尺寸
  // const bubbleWidth = maxWidth

  // const bubbleHeight = lines.length * lineHeight + padding * 2

  // 3️⃣ 绘制气泡（圆角矩形）
  // drawRoundRect(
  //   ctx,
  //   x,
  //   y,
  //   bubbleWidth,
  //   bubbleHeight,
  //   radius,
  //   backgroundColor
  // )

  // 4️⃣ 绘制文字
  ctx.fillStyle = textColor;

  lines.forEach((line, index) => {
    ctx.fillText(line, x + padding, y + padding + index * lineHeight);
  });
}

function drawUserBubble(ctx, options) {
  const {
    x = 40, // 气泡起点 x
    y = 860, // 气泡起点 y
    avatarImg, // CanvasImage 对象
    username = "zzc",
    desc = "送上祝福",
    avatarSize = 64, // 头像直径
    padding = 16, // 气泡内边距
    fontSizeName = 24,
    fontSizeDesc = 20,
    bubbleColor = "rgba(255,255,255,0.18)",
    textColor = "#ffffff",
  } = options;

  // 设置字体
  ctx.textBaseline = "top";
  ctx.font = `${fontSizeName}px 'PingFang SC'`;

  // 测量文字宽度
  const nameWidth = ctx.measureText(username).width;
  ctx.font = `${fontSizeDesc}px 'PingFang SC'`;
  const descWidth = ctx.measureText(desc).width;

  // 计算气泡宽度和高度
  const textWidth = Math.max(nameWidth, descWidth);
  const bubbleHeight =
    Math.max(avatarSize, fontSizeName + fontSizeDesc + 4) + padding * 2;
  const bubbleWidth = avatarSize + padding + textWidth + padding * 2;

  // 1️⃣ 绘制气泡（左右半圆）
  drawRoundRect(
    ctx,
    x,
    y,
    bubbleWidth,
    bubbleHeight,
    bubbleHeight / 2,
    bubbleColor,
  );

  // 2️⃣ 绘制头像
  const avatarX = x + padding;
  const avatarY = y + (bubbleHeight - avatarSize) / 2;

  ctx.save();
  ctx.beginPath();
  ctx.arc(
    avatarX + avatarSize / 2,
    avatarY + avatarSize / 2,
    avatarSize / 2,
    0,
    Math.PI * 2,
  );
  ctx.clip();
  if (avatarImg) {
    ctx.drawImage(avatarImg, avatarX, avatarY, avatarSize, avatarSize);
  }
  ctx.restore();

  // 3️⃣ 绘制文字
  const textX = avatarX + avatarSize + padding;
  const textY = y + padding;
  ctx.fillStyle = textColor;
  ctx.font = `${fontSizeName}px 'PingFang SC'`;
  ctx.fillText(username, textX, textY);
  ctx.font = `${fontSizeDesc}px 'PingFang SC'`;
  ctx.globalAlpha = 0.6;
  ctx.fillText(desc, textX, textY + fontSizeName + 4);
  ctx.globalAlpha = 1;
}

function drawRoundRect(ctx, x, y, w, h, r, color) {
  ctx.beginPath();
  //   ctx.setFillStyle(color)
  ctx.fillStyle = "rgba(255,255,255,0.18)";
  ctx.fill();

  // 描边（非常关键）
  ctx.strokeStyle = "rgba(255,255,255,0.35)";
  ctx.lineWidth = 1;
  ctx.stroke();

  ctx.moveTo(x + r, y);
  ctx.lineTo(x + w - r, y);
  ctx.arcTo(x + w, y, x + w, y + r, r);
  ctx.lineTo(x + w, y + h - r);
  ctx.arcTo(x + w, y + h, x + w - r, y + h, r);
  ctx.lineTo(x + r, y + h);
  ctx.arcTo(x, y + h, x, y + h - r, r);
  ctx.lineTo(x, y + r);
  ctx.arcTo(x, y, x + r, y, r);
  ctx.closePath();
  ctx.fill();
}
</script>

<style lang="scss" scoped>
.make-page {
  min-height: 100vh;
  background: #fff;
  box-sizing: border-box;
}

/* 卡片预览 */
.card-preview {
  margin: 24rpx auto;
  height: 960rpx;
  width: 540rpx;
  border-radius: 30rpx;
  overflow: hidden;
  position: relative;
  box-shadow: 0 16rpx 40rpx rgba(0, 0, 0, 0.12);
}
.card-bg {
  width: 100%;
  height: 100%;
}
.card-overlay {
  position: absolute;
  inset: 0;
  padding: 30rpx;
  color: #fff;
  display: flex;
  flex-direction: column;
  align-items: center;
}
.watermark {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%) rotate(-45deg);
  font-size: 36rpx;
  color: rgba(255, 255, 255, 0.2);
  font-weight: bold;
  pointer-events: none;
  white-space: nowrap;
  z-index: 1;
}
.card-overlay .title {
  display: flex;
  flex-direction: column;
  align-items: center;
  margin-top: 60rpx;
}
.title .main {
  font-size: 42rpx;
  font-weight: 700;
}
.title .sub {
  margin-top: 8rpx;
  font-size: 22rpx;
  opacity: 0.9;
}
.bubble {
  margin-top: 80rpx;
  padding: 40rpx;
  max-width: 500rpx;
}
.bubble-text {
  font-size: 26rpx;
  line-height: 1.6;
  white-space: pre-wrap;
  text-align: left;
}
.user {
  position: absolute;
  bottom: 40rpx;
  display: flex;
  align-items: center;
  background: rgba(255, 255, 255, 0.18);
  border: 1rpx solid rgba(255, 255, 255, 0.35);
  border-radius: 9999rpx;
  padding: 15rpx;
  padding-left: 20rpx;
  padding-right: 20rpx;
}
.avatar {
  width: 64rpx;
  height: 64rpx;
  border-radius: 50%;
  margin-right: 14rpx;
  border: 2rpx solid rgba(255, 255, 255, 0.6);
}
.user .user-info {
  display: flex;
  flex-direction: column;
}
.user-name {
  font-size: 24rpx;
  font-weight: 600;
}
.user-desc {
  font-size: 20rpx;
  opacity: 0.6;
}

/* 顶部提示 */
.tip-line {
  text-align: center;
  color: #999;
  font-size: 22rpx;
}

/* 编辑工具区 */
.editor-panel {
  margin: 20rpx 24rpx 40rpx;
  border-radius: 30rpx 30rpx 0 0;
  background: #fff;
  box-shadow: 0 -10rpx 30rpx rgba(0, 0, 0, 0.06);
  padding-bottom: env(safe-area-inset-bottom);
}
.drag-handle {
  width: 120rpx;
  height: 8rpx;
  border-radius: 999rpx;
  background: #eee;
  margin: 12rpx auto;
}

/* 工具入口 */
.tools {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  padding: 16rpx 24rpx 8rpx;
}
.tool-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 14rpx 0;
  border-radius: 18rpx;
  color: #666;
}
.tool-item.active {
  background: #fff6f5;
  color: #ff3b30;
}
.tool-icon {
  width: 64rpx;
  height: 64rpx;
  border-radius: 16rpx;
  background: #fafafa;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 8rpx;
}
.tool-text {
  font-size: 22rpx;
}

/* 模板区 */
.section {
  padding: 12rpx 24rpx 0;
}
.section-title {
  display: flex;
  align-items: center;
}
.section-title .more {
  margin-left: auto;
  color: #ff3b30;
  font-size: 24rpx;
}
.tpl-scroll {
  margin-top: 12rpx;
}
.tpl-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16rpx;
  padding-bottom: 20rpx;
}
.tpl-card {
  width: 100%; /* 自适应 grid 宽度 */
  border-radius: 12rpx;
  overflow: hidden;
  background: #fff;
  position: relative;
  box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.06);
}
.tpl-card.selected {
  outline: 4rpx solid #ff3b30;
}
.tpl-cover {
  width: 100%;
  height: 368rpx;
}
.tpl-name {
  font-size: 20rpx;
  color: #333;
  padding: 6rpx 8rpx;
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.tpl-check {
  position: absolute;
  right: 6rpx;
  top: 6rpx;
  width: 32rpx;
  height: 32rpx;
  border-radius: 50%;
  background: #ff3b30;
  color: #fff;
  font-size: 20rpx;
  display: flex;
  align-items: center;
  justify-content: center;
}
.loading-more,
.no-more {
  text-align: center;
  font-size: 22rpx;
  color: #999;
  padding: 10rpx 0;
}

/* 文字编辑区 */
.text-edit-section {
  padding: 10rpx 24rpx 0;
}
.form-item {
  margin-bottom: 32rpx;
}
.label {
  font-size: 24rpx;
  color: #333;
  font-weight: 600;
  margin-bottom: 16rpx;
  display: block;
}
.input-box {
  background: #f9f9f9;
  border-radius: 12rpx;
  padding: 20rpx 24rpx;
  font-size: 28rpx;
  color: #333;
}
.label-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 16rpx;
}
.refresh-btn {
  font-size: 22rpx;
  color: #ff3b30;
  display: flex;
  align-items: center;
}
.refresh-icon {
  margin-right: 6rpx;
  font-size: 24rpx;
}
.greeting-scroll {
  width: 100%;
}
.greeting-list {
  display: flex;
  padding-bottom: 10rpx;
}
.greeting-card {
  flex-shrink: 0;
  width: 320rpx;
  height: 160rpx;
  background: #fff;
  border: 2rpx solid #eee;
  border-radius: 16rpx;
  padding: 20rpx;
  margin-right: 20rpx;
  position: relative;
  box-sizing: border-box;
}
.greeting-card.active {
  background: #fff5f5;
  border-color: #ff3b30;
}
.greeting-text {
  font-size: 24rpx;
  color: #666;
  line-height: 1.5;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 4;
  overflow: hidden;
}
.greeting-card.active .greeting-text {
  color: #ff3b30;
}
.check-mark {
  position: absolute;
  top: 10rpx;
  right: 10rpx;
  width: 32rpx;
  height: 32rpx;
  background: #ff3b30;
  border-radius: 50%;
  color: #fff;
  font-size: 20rpx;
  display: flex;
  align-items: center;
  justify-content: center;
}
.input-wrapper {
  position: relative;
}
.edit-icon {
  position: absolute;
  right: 24rpx;
  top: 50%;
  transform: translateY(-50%);
  color: #999;
}
.font-scroll {
  white-space: nowrap;
  width: 100%;
}

.font-list {
  display: flex;
  padding: 4rpx;
}

.font-item {
  padding: 12rpx 24rpx;
  background: #f5f5f5;
  border-radius: 8rpx;
  margin-right: 16rpx;
  border: 2rpx solid transparent;
  transition: all 0.3s;
}

.font-item.active {
  background: #fff5f5;
  border-color: #ff3b30;
  color: #ff3b30;
}

.color-list {
  display: flex;
  gap: 20rpx;
  flex-wrap: wrap;
}
.color-item {
  width: 64rpx;
  height: 64rpx;
  border-radius: 50%;
  position: relative;
  border: 2rpx solid rgba(0, 0, 0, 0.05);
}
.color-check {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  font-size: 32rpx;
  text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.2);
}

/* 按钮区 */
.bottom-actions {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20rpx 24rpx 30rpx;
}
.btn {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 88rpx;
  border-radius: 999rpx;
  padding: 0 40rpx;
  font-size: 28rpx;
}
.btn view {
  margin-left: 10rpx;
}
.btn.secondary {
  background: #fff;
  color: #000;
  border: 1px solid #ccc;
}
.btn.primary {
  background: #ff3b30;
  color: #fff;
  box-shadow: 0 12rpx 24rpx rgba(255, 59, 48, 0.35);
}
.hidden-canvas {
  position: fixed;
  left: -9999px;
  top: -9999px;
}
</style>
