/* 资产编辑模态框 */
const AssetModal = {
  template: `
  <div v-if="store.assetModal.show" class="fixed inset-0 bg-black/40 z-50 flex items-end md:items-center justify-center p-0 md:p-4">
    <div class="bg-white dark:bg-slate-900 w-full md:max-w-lg md:rounded-xl rounded-t-xl max-h-[92vh] overflow-y-auto">
      <div class="px-5 py-3 border-b border-slate-100 dark:border-slate-800 flex justify-between items-center sticky top-0 bg-white dark:bg-slate-900">
        <h3 class="font-semibold text-sm">{{ store.assetModal.editing ? '编辑资产' : '新增资产' }}</h3>
        <button @click="store.assetModal.show=false" class="text-slate-400 hover:text-slate-600">✕</button>
      </div>
      <form @submit.prevent="save" class="px-5 py-4 space-y-3" v-if="f">
        <div class="grid grid-cols-2 gap-3">
          <label class="col-span-2 block"><span class="text-xs text-slate-500">名称 *</span>
            <input v-model="f.name" required class="mt-1 w-full px-3 py-2 rounded-lg border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm"></label>
          <label class="block"><span class="text-xs text-slate-500">类型 *</span>
            <select v-model="f.asset_type" class="mt-1 w-full px-3 py-2 rounded-lg border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm">
              <option v-for="(l,k) in Fmt.TYPE_LABELS" :key="k" :value="k">{{ l }}</option>
            </select></label>
          <label class="block"><span class="text-xs text-slate-500">所属平台</span>
            <select v-model="f.provider_id" @change="onProviderChange" class="mt-1 w-full px-3 py-2 rounded-lg border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm">
              <option :value="null">未指定</option>
              <option v-for="p in providersForType" :key="p.id" :value="p.id">{{ p.name }}</option>
            </select></label>
          <label class="block"><span class="text-xs text-slate-500">到期时间</span>
            <input type="date" v-model="f.expiry_date" class="mt-1 w-full px-3 py-2 rounded-lg border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm"></label>
          <label class="block"><span class="text-xs text-slate-500">续费周期</span>
            <select v-model="f.renewal_cycle" class="mt-1 w-full px-3 py-2 rounded-lg border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm">
              <option value="">未设定</option><option v-for="(l,k) in Fmt.CYCLE_LABELS" :key="k" :value="k">{{ l }}</option>
            </select></label>
          <label class="block"><span class="text-xs text-slate-500">费用</span>
            <input type="number" step="0.01" v-model.number="f.cost" class="mt-1 w-full px-3 py-2 rounded-lg border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm"></label>
          <label class="block"><span class="text-xs text-slate-500">币种</span>
            <select v-model="f.currency" class="mt-1 w-full px-3 py-2 rounded-lg border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm">
              <option>USD</option><option>CNY</option><option>JPY</option><option>EUR</option>
            </select></label>
          <label class="col-span-2 block"><span class="text-xs text-slate-500">续费网址</span>
            <input v-model="f.renew_url" placeholder="https://…（去哪续费）" class="mt-1 w-full px-3 py-2 rounded-lg border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm"></label>
          <label class="col-span-2 block"><span class="text-xs text-slate-500">取消订阅网址</span>
            <input v-model="f.cancel_url" placeholder="https://…（去哪取消）" class="mt-1 w-full px-3 py-2 rounded-lg border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm"></label>
          <label class="block"><span class="text-xs text-slate-500">状态</span>
            <select v-model="f.status" class="mt-1 w-full px-3 py-2 rounded-lg border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm">
              <option v-for="(l,k) in Fmt.STATUS_LABELS" :key="k" :value="k">{{ l }}</option>
            </select></label>
          <label class="block"><span class="text-xs text-slate-500">所属账号</span>
            <select v-model.number="f.account_id" class="mt-1 w-full px-3 py-2 rounded-lg border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm">
              <option :value="null">无（在「平台」页管理账号）</option>
              <option v-for="a in store.accounts" :key="a.id" :value="a.id">{{ accountLabel(a) }}</option>
            </select></label>
          <div class="col-span-2 flex gap-5 text-sm text-slate-600 dark:text-slate-300">
            <label class="flex items-center gap-2"><input type="checkbox" v-model="f.auto_renew"> 自动续费</label>
            <label class="flex items-center gap-2"><input type="checkbox" v-model="f.is_archived"> 已归档</label>
          </div>
        </div>

        <fieldset v-if="f.asset_type==='vps'" class="border border-slate-200 dark:border-slate-700 rounded-lg p-3">
          <legend class="text-xs font-medium text-slate-600 dark:text-slate-300 px-1">VPS 详情</legend>
          <div class="grid grid-cols-2 gap-2">
            <label class="block"><span class="text-xs text-slate-400">公网 IP *</span><input v-model="f.vps_detail.ip_address" required class="mt-1 w-full px-2 py-1.5 rounded border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm"></label>
            <label class="block"><span class="text-xs text-slate-400">Tailscale IP</span><input v-model="f.vps_detail.tailscale_ip" class="mt-1 w-full px-2 py-1.5 rounded border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm"></label>
            <label class="block"><span class="text-xs text-slate-400">区域</span><input v-model="f.vps_detail.region" class="mt-1 w-full px-2 py-1.5 rounded border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm"></label>
            <label class="block"><span class="text-xs text-slate-400">系统</span><input v-model="f.vps_detail.os" class="mt-1 w-full px-2 py-1.5 rounded border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm"></label>
            <label class="block"><span class="text-xs text-slate-400">CPU核</span><input type="number" v-model.number="f.vps_detail.cpu_cores" class="mt-1 w-full px-2 py-1.5 rounded border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm"></label>
            <label class="block"><span class="text-xs text-slate-400">内存GB</span><input type="number" step="0.5" v-model.number="f.vps_detail.memory_gb" class="mt-1 w-full px-2 py-1.5 rounded border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm"></label>
            <label class="block"><span class="text-xs text-slate-400">磁盘GB</span><input type="number" v-model.number="f.vps_detail.disk_gb" class="mt-1 w-full px-2 py-1.5 rounded border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm"></label>
            <label class="block"><span class="text-xs text-slate-400">SSH端口</span><input type="number" v-model.number="f.vps_detail.ssh_port" class="mt-1 w-full px-2 py-1.5 rounded border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm"></label>
            <label class="block"><span class="text-xs text-slate-400">SSH用户</span><input v-model="f.vps_detail.ssh_user" placeholder="root" class="mt-1 w-full px-2 py-1.5 rounded border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm"></label>
            <label class="block"><span class="text-xs text-slate-400">登录方式</span><select v-model="f.vps_detail.login_method" class="mt-1 w-full px-2 py-1.5 rounded border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm"><option value="key">密钥</option><option value="password">密码</option><option value="other">其他</option></select></label>
            <label class="block"><span class="text-xs text-slate-400">SSH密钥</span><input v-model="f.vps_detail.ssh_key" type="password" :placeholder="store.assetModal.editing ? '留空不修改' : ''" class="mt-1 w-full px-2 py-1.5 rounded border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm"></label>
            <label class="block"><span class="text-xs text-slate-400">密码</span><input v-model="f.vps_detail.password" type="password" :placeholder="store.assetModal.editing ? '留空不修改' : ''" class="mt-1 w-full px-2 py-1.5 rounded border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm"></label>
            <label class="col-span-2 block"><span class="text-xs text-slate-400">用途/项目</span><input v-model="f.vps_detail.purpose" class="mt-1 w-full px-2 py-1.5 rounded border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm"></label>
          </div>
        </fieldset>

        <fieldset v-if="f.asset_type==='domain'" class="border border-slate-200 dark:border-slate-700 rounded-lg p-3">
          <legend class="text-xs font-medium text-slate-600 dark:text-slate-300 px-1">域名详情</legend>
          <div class="grid grid-cols-2 gap-2">
            <label class="col-span-2 block"><span class="text-xs text-slate-400">域名 *</span><input v-model="f.domain_detail.domain_name" required class="mt-1 w-full px-2 py-1.5 rounded border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm"></label>
            <label class="block"><span class="text-xs text-slate-400">注册商</span><input v-model="f.domain_detail.registrar" class="mt-1 w-full px-2 py-1.5 rounded border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm"></label>
            <label class="block"><span class="text-xs text-slate-400">DNS托管</span><input v-model="f.domain_detail.dns_provider" class="mt-1 w-full px-2 py-1.5 rounded border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm"></label>
            <label class="block"><span class="text-xs text-slate-400">CF账号</span><input v-model="f.domain_detail.cloudflare_account" class="mt-1 w-full px-2 py-1.5 rounded border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm"></label>
            <label class="block"><span class="text-xs text-slate-400">绑定资产ID</span><input type="number" v-model.number="f.domain_detail.bind_asset_id" placeholder="关联VPS的ID" class="mt-1 w-full px-2 py-1.5 rounded border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm"></label>
            <label class="col-span-2 flex items-center gap-2 text-sm text-slate-600 dark:text-slate-300"><input type="checkbox" v-model="f.domain_detail.is_using"> 仍在实际使用</label>
          </div>
        </fieldset>

        <fieldset v-if="f.asset_type==='ai_agent'" class="border border-slate-200 dark:border-slate-700 rounded-lg p-3">
          <legend class="text-xs font-medium text-slate-600 dark:text-slate-300 px-1">AI 账号详情</legend>
          <div class="grid grid-cols-2 gap-2">
            <label class="block"><span class="text-xs text-slate-400">服务商 *</span><input v-model="f.ai_detail.provider" required class="mt-1 w-full px-2 py-1.5 rounded border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm"></label>
            <label class="block"><span class="text-xs text-slate-400">计划</span><input v-model="f.ai_detail.plan" class="mt-1 w-full px-2 py-1.5 rounded border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm"></label>
            <label class="col-span-2 block"><span class="text-xs text-slate-400">API Key</span><input v-model="f.ai_detail.api_key" type="password" class="mt-1 w-full px-2 py-1.5 rounded border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm"></label>
            <label class="block"><span class="text-xs text-slate-400">余额</span><input type="number" step="0.01" v-model.number="f.ai_detail.balance" class="mt-1 w-full px-2 py-1.5 rounded border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm"></label>
            <label class="block"><span class="text-xs text-slate-400">月限额</span><input type="number" step="0.01" v-model.number="f.ai_detail.monthly_limit" class="mt-1 w-full px-2 py-1.5 rounded border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm"></label>
          </div>
        </fieldset>

        <fieldset v-if="f.asset_type==='cloudflare'" class="border border-slate-200 dark:border-slate-700 rounded-lg p-3">
          <legend class="text-xs font-medium text-slate-600 dark:text-slate-300 px-1">Cloudflare 子资产详情</legend>
          <div class="grid grid-cols-2 gap-2">
            <label class="block"><span class="text-xs text-slate-400">账号邮箱</span><input v-model="f.cloudflare_detail.account_email" placeholder="账号邮箱（区分多账号）" class="mt-1 w-full px-2 py-1.5 rounded border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm"></label>
            <label class="block"><span class="text-xs text-slate-400">子资产类型</span><select v-model="f.cloudflare_detail.sub_type" class="mt-1 w-full px-2 py-1.5 rounded border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm"><option value="zone">zone(解析)</option><option value="worker">Worker</option><option value="r2">R2</option><option value="tunnel">Tunnel</option><option value="mail">Mail</option><option value="dns_record">DNS记录</option><option value="other">其他</option></select></label>
            <label class="block"><span class="text-xs text-slate-400">子资产名称</span><input v-model="f.cloudflare_detail.sub_name" placeholder="如 worker 名 / bucket 名" class="mt-1 w-full px-2 py-1.5 rounded border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm"></label>
            <label class="block"><span class="text-xs text-slate-400">关联 zone/域名</span><input v-model="f.cloudflare_detail.zone_name" placeholder="example.com" class="mt-1 w-full px-2 py-1.5 rounded border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm"></label>
            <label class="col-span-2 block"><span class="text-xs text-slate-400">状态</span><input v-model="f.cloudflare_detail.status" placeholder="active / paused 等" class="mt-1 w-full px-2 py-1.5 rounded border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm"></label>
          </div>
        </fieldset>

        <label class="block"><span class="text-xs text-slate-500">备注</span>
          <textarea v-model="f.remark" rows="2" class="mt-1 w-full px-3 py-2 rounded-lg border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm"></textarea></label>

        <div class="flex justify-end gap-2 pt-1">
          <button type="button" @click="store.assetModal.show=false" class="px-4 py-2 rounded-lg border border-slate-300 dark:border-slate-700 text-sm text-slate-600 dark:text-slate-300">取消</button>
          <button type="submit" class="px-4 py-2 rounded-lg bg-blue-600 text-white text-sm hover:bg-blue-700">{{ store.assetModal.editing ? '保存' : '创建' }}</button>
        </div>
      </form>
    </div>
  </div>`,
  setup() {
    const f = Vue.computed(() => store.assetModal.form);
    const providersForType = Vue.computed(() => {
      const t = f.value ? f.value.asset_type : null;
      const map = { vps: 'vps', domain: 'domain', ai_agent: 'ai_agent', cloudflare: 'cloudflare' };
      const cat = map[t];
      if (!cat) return store.providers;
      // 综合平台按 services 过滤（如阿里云可同时选 vps/domain）；services 为空时回退分类匹配
      return store.providers.filter(p => {
        if (p.services) return p.services.split(',').map(s => s.trim()).includes(cat);
        return p.category === cat || p.category === 'other';
      });
    });
    function onProviderChange() {
      const p = store.providers.find(x => x.id === f.value.provider_id);
      if (p) f.value.provider = p.slug;
    }
    // 账号候选显示：平台名 + 账号标识（同名账号靠平台前缀区分），说明优先
    function accountLabel(a) {
      const p = store.providers.find(x => x.slug === a.platform);
      const pname = p ? p.name : (a.platform || '未指定平台');
      if (a.remark) return pname + ' · ' + a.remark + '（' + a.name + '）';
      return pname + ' · ' + a.name;
    }
    return { store, Fmt, f, providersForType, onProviderChange, accountLabel, save: saveAsset };
  },
};

/* 平台编辑模态框 */
const ProviderModal = {
  template: `
  <div v-if="store.providerModal.show" class="fixed inset-0 bg-black/40 z-50 flex items-end md:items-center justify-center p-0 md:p-4">
    <div class="bg-white dark:bg-slate-900 w-full md:max-w-md md:rounded-xl rounded-t-xl max-h-[92vh] overflow-y-auto">
      <div class="px-5 py-3 border-b border-slate-100 dark:border-slate-800 flex justify-between items-center">
        <h3 class="font-semibold text-sm">{{ store.providerModal.editing ? '编辑平台' : '新增平台' }}</h3>
        <button @click="store.providerModal.show=false" class="text-slate-400 hover:text-slate-600">✕</button>
      </div>
      <form @submit.prevent="save" class="px-5 py-4 space-y-3" v-if="f">
        <div class="grid grid-cols-2 gap-3">
          <label class="block"><span class="text-xs text-slate-500">标识 slug *</span><input v-model="f.slug" required placeholder="aliyun" class="mt-1 w-full px-3 py-2 rounded-lg border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm"></label>
          <label class="block"><span class="text-xs text-slate-500">名称 *</span><input v-model="f.name" required class="mt-1 w-full px-3 py-2 rounded-lg border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm"></label>
          <label class="col-span-2 block"><span class="text-xs text-slate-500">分类</span><select v-model="f.category" class="mt-1 w-full px-3 py-2 rounded-lg border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm"><option v-for="(l,k) in Fmt.CATEGORY_LABELS" :key="k" :value="k">{{ l }}</option></select></label>
          <div class="col-span-2 block"><span class="text-xs text-slate-500">提供服务（可多选，综合平台全选）</span>
            <div class="flex flex-wrap gap-x-3 gap-y-1 mt-1">
              <label v-for="(l,k) in Fmt.SERVICES_LABELS" :key="k" class="flex items-center gap-1 text-xs text-slate-600 dark:text-slate-300"><input type="checkbox" :checked="servicesSet.has(k)" @change="toggleService(k)"> {{ l }}</label>
            </div>
          </div>
          <label class="block"><span class="text-xs text-slate-500">SDK类型</span><select v-model="f.sdk_type" class="mt-1 w-full px-3 py-2 rounded-lg border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm"><option value="">未指定</option><option v-for="o in sdkOptions" :key="o" :value="o">{{ o }}</option></select></label>
          <label class="col-span-2 block"><span class="text-xs text-slate-500">官网</span><input v-model="f.website" class="mt-1 w-full px-3 py-2 rounded-lg border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm"></label>
          <label class="col-span-2 block"><span class="text-xs text-slate-500">管理面板URL</span><input v-model="f.console_url" class="mt-1 w-full px-3 py-2 rounded-lg border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm"></label>
          <div v-if="f.sdk_type" class="col-span-2 text-xs text-slate-400 bg-slate-50 dark:bg-slate-800/50 rounded-lg p-2">API 凭证请在平台账号中配置：保存后在平台卡片点「账号」→ 新增账号并填写 API 配置，即可测试/同步。</div>
          <label class="col-span-2 flex items-center gap-2 text-sm text-slate-600 dark:text-slate-300"><input type="checkbox" v-model="f.enabled"> 启用</label>
        </div>
        <div class="flex justify-end gap-2">
          <button type="button" @click="store.providerModal.show=false" class="px-4 py-2 rounded-lg border border-slate-300 dark:border-slate-700 text-sm text-slate-600 dark:text-slate-300">取消</button>
          <button type="submit" class="px-4 py-2 rounded-lg bg-blue-600 text-white text-sm hover:bg-blue-700">保存</button>
        </div>
      </form>
    </div>
  </div>`,
  setup() {
    const f = Vue.computed(() => store.providerModal.form);
    const sdkOptions = Vue.ref([]);
    Vue.onMounted(async () => {
      try {
        const r = await Api.get('/providers/adapters');
        const opts = [];
        for (const m of (r.adapters || [])) for (const t of (m.sdk_types || [])) opts.push(t);
        sdkOptions.value = opts;
      } catch (e) { /* ignore */ }
    });
    // 提供服务多选：services 逗号分隔字符串 <-> checkbox 集合
    const servicesSet = Vue.computed(() => new Set((f.value && f.value.services || '').split(',').map(s => s.trim()).filter(Boolean)));
    function toggleService(k) {
      const arr = (f.value.services || '').split(',').map(s => s.trim()).filter(Boolean);
      const idx = arr.indexOf(k);
      if (idx >= 0) arr.splice(idx, 1); else arr.push(k);
      f.value.services = arr.join(',');
    }
    // 凭证已下沉到账号层，平台表单不再收集 API 凭证
    return { store, Fmt, f, sdkOptions, servicesSet, toggleService, save: saveProvider };
  },
};

/* 账号编辑模态框（z-[60]：可能叠加在账号查看弹窗 z-50 之上打开，需更高层级） */
const AccountModal = {
  template: `
  <div v-if="store.accountModal.show" class="fixed inset-0 bg-black/40 z-[60] flex items-end md:items-center justify-center p-0 md:p-4">
    <div class="bg-white dark:bg-slate-900 w-full md:max-w-md md:rounded-xl rounded-t-xl max-h-[92vh] overflow-y-auto">
      <div class="px-5 py-3 border-b border-slate-100 dark:border-slate-800 flex justify-between items-center">
        <h3 class="font-semibold text-sm">{{ store.accountModal.editing ? '编辑账号' : '新增账号' }}</h3>
        <button @click="store.accountModal.show=false" class="text-slate-400 hover:text-slate-600">✕</button>
      </div>
      <form @submit.prevent="save" class="px-5 py-4 space-y-3" v-if="f">
        <label class="block"><span class="text-xs text-slate-500">账号说明（这个账号是干嘛的）</span>
          <input v-model="f.remark" placeholder="如：XX公司代管账号" class="mt-1 w-full px-3 py-2 rounded-lg border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm"></label>
        <label class="block"><span class="text-xs text-slate-500">账号标识 *（邮箱/用户名）</span>
          <input v-model="f.name" required placeholder="如 you@example.com" class="mt-1 w-full px-3 py-2 rounded-lg border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm"></label>
        <label class="block"><span class="text-xs text-slate-500">所属平台</span>
          <select v-model="f.platform" class="mt-1 w-full px-3 py-2 rounded-lg border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm">
            <option value="">未指定</option>
            <option v-for="p in store.providers" :key="p.id" :value="p.slug">{{ p.name }}</option>
          </select></label>
        <div class="grid grid-cols-2 gap-3">
          <label class="block"><span class="text-xs text-slate-500">登录用户名/邮箱</span>
            <input v-model="f.login_user" placeholder="网站登录用" class="mt-1 w-full px-3 py-2 rounded-lg border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm"></label>
          <label class="block"><span class="text-xs text-slate-500">登录密码（加密存储）</span>
            <input v-model="f.login_password" type="password" :placeholder="store.accountModal.editing ? '留空不修改' : '可选'" class="mt-1 w-full px-3 py-2 rounded-lg border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm"></label>
        </div>
        <div v-if="currentFields.length" class="border border-slate-200 dark:border-slate-700 rounded-lg p-3 space-y-2">
          <div class="text-xs font-medium text-slate-600 dark:text-slate-300">API 配置（供 SDK 同步，加密存储）</div>
          <label v-for="field in currentFields" :key="field" class="block"><span class="text-xs text-slate-400">{{ field }}</span>
            <input v-model="apiFields[field]" type="password" :placeholder="store.accountModal.editing ? '留空不修改' : '填写 ' + field" class="mt-1 w-full px-2 py-1.5 rounded border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm"></label>
        </div>
        <p v-if="store.accountModal.editing" class="text-xs text-slate-400">修改账号标识后，引用该账号的资产会自动同步更新。</p>
        <div class="flex justify-end gap-2">
          <button type="button" @click="store.accountModal.show=false" class="px-4 py-2 rounded-lg border border-slate-300 dark:border-slate-700 text-sm text-slate-600 dark:text-slate-300">取消</button>
          <button type="submit" :disabled="store.accountSaving" class="px-4 py-2 rounded-lg bg-blue-600 text-white text-sm hover:bg-blue-700 disabled:opacity-60">{{ store.accountSaving ? '保存中…' : '保存' }}</button>
        </div>
      </form>
    </div>
  </div>`,
  setup() {
    const f = Vue.computed(() => store.accountModal.form);
    const adapterMeta = Vue.ref([]);
    const apiFields = Vue.reactive({});
    Vue.onMounted(async () => {
      try { const r = await Api.get('/providers/adapters'); adapterMeta.value = r.adapters || []; } catch (e) { /* ignore */ }
    });
    // 按账号所属平台的 sdk_type 动态渲染所需凭证字段
    const currentFields = Vue.computed(() => {
      const p = store.providers.find(x => x.slug === (f.value && f.value.platform));
      if (!p || !p.sdk_type) return [];
      const m = adapterMeta.value.find(x => (x.sdk_types || []).includes(p.sdk_type));
      return m ? (m.required_config || []) : [];
    });
    // 每次打开清空残留输入，避免串号
    Vue.watch(() => store.accountModal.show, (show) => {
      if (show) { for (const k of Object.keys(apiFields)) delete apiFields[k]; }
    });
    function save() {
      const cfg = {};
      for (const field of currentFields.value) if (apiFields[field]) cfg[field] = apiFields[field];
      f.value.api_config = Object.keys(cfg).length ? JSON.stringify(cfg) : '';
      saveAccount();
    }
    return { store, f, currentFields, apiFields, save };
  },
};

/* 账号查看弹窗：平台 → 账号 → 资产 三层结构，展开账号看名下资产 */
const AccountsViewModal = {
  template: `
  <div v-if="store.accountsModal.show" class="fixed inset-0 bg-black/40 z-50 flex items-end md:items-center justify-center p-0 md:p-4">
    <div class="bg-white dark:bg-slate-900 w-full md:max-w-lg md:rounded-xl rounded-t-xl max-h-[92vh] overflow-y-auto">
      <div class="px-5 py-3 border-b border-slate-100 dark:border-slate-800 flex justify-between items-center sticky top-0 bg-white dark:bg-slate-900">
        <h3 class="font-semibold text-sm">👤 {{ title }}（{{ accountList.length }}）</h3>
        <div class="flex items-center gap-3">
          <button @click="add" class="text-xs text-blue-600 dark:text-blue-400">+ 新增账号</button>
          <button @click="store.accountsModal.show=false" class="text-slate-400 hover:text-slate-600">✕</button>
        </div>
      </div>
      <div class="px-5 py-4">
        <p v-if="!accountList.length" class="text-xs text-slate-400">暂无账号。一个平台可有多个账号（如代管/公司账号），添加后可在资产表单「所属账号」中选择。</p>
        <div v-else class="space-y-4">
          <div v-for="g in groups" :key="g.platform">
            <div v-if="!singleProvider" class="text-xs font-medium text-slate-400 mb-1">{{ g.platform ? platformName(g.platform) : '未指定平台' }}</div>
            <div class="divide-y divide-slate-100 dark:divide-slate-800 border border-slate-100 dark:border-slate-800 rounded-lg">
              <div v-for="a in g.accounts" :key="a.id">
                <div class="px-3 py-2 flex items-center gap-2">
                  <button @click="toggle(a.id)" class="text-slate-400 text-xs w-4">{{ expanded[a.id] ? '▾' : '▸' }}</button>
                  <div class="flex-1 min-w-0">
                    <div class="text-sm truncate">{{ a.remark || a.name }}
                      <span v-if="a.has_login_password" class="text-[10px] px-1 py-0.5 ml-1 rounded bg-slate-100 dark:bg-slate-800 text-slate-400" title="已配置登录凭证">🔑登录</span>
                      <span v-if="a.has_api_config" class="text-[10px] px-1 py-0.5 ml-1 rounded bg-blue-500/10 text-blue-500" title="已配置 API 凭证">⚙️API</span>
                    </div>
                    <div class="text-xs text-slate-400 truncate"><span v-if="a.remark">{{ a.name }} · </span>关联 {{ assetsOf(a).length }} 个资产</div>
                  </div>
                  <button v-if="a.has_login_password" @click="viewPwd(a)" class="text-xs text-amber-600 dark:text-amber-400" title="查看登录密码">🔑</button>
                  <button v-if="canSync(a)" @click="testAcct(a)" class="text-xs text-emerald-600 dark:text-emerald-400">测试</button>
                  <button v-if="canSync(a)" @click="syncAcct(a)" class="text-xs text-violet-600 dark:text-violet-400">同步</button>
                  <button @click="edit(a)" class="text-xs text-blue-600 dark:text-blue-400">编辑</button>
                  <button @click="del(a)" class="text-xs text-red-600 dark:text-red-400">删除</button>
                </div>
                <div v-if="expanded[a.id]" class="px-3 pb-2 pl-9 space-y-1">
                  <p v-if="!assetsOf(a).length" class="text-xs text-slate-400">该账号下暂无资产（编辑资产时在「所属账号」选择 {{ a.name }} 即可关联）</p>
                  <div v-for="asset in assetsOf(a)" :key="asset.id" class="text-xs flex items-center gap-2">
                    <span class="px-1.5 py-0.5 rounded-full shrink-0" :class="Fmt.typeBadge(asset.asset_type)">{{ Fmt.TYPE_LABELS[asset.asset_type] }}</span>
                    <span class="truncate">{{ asset.name }}</span>
                    <span v-if="asset.expiry_date" class="text-slate-400 shrink-0">{{ asset.expiry_date }} 到期</span>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
    <!-- 密码查看弹窗：点击账号的 🔑 后展示明文，几秒后自动隐藏 -->
    <div v-if="pwd.show" class="absolute inset-0 bg-black/50 flex items-center justify-center p-4" @click.self="pwd.show=false">
      <div class="bg-white dark:bg-slate-800 rounded-xl w-full max-w-xs p-5 shadow-xl">
        <h4 class="font-semibold text-sm mb-3">🔑 {{ pwd.name }} 的登录凭证</h4>
        <div class="space-y-2 text-sm">
          <div v-if="pwd.login_user" class="flex justify-between items-center gap-2">
            <span class="text-slate-400 text-xs shrink-0">用户名</span>
            <span class="font-mono truncate">{{ pwd.login_user }}</span>
          </div>
          <div class="flex justify-between items-center gap-2">
            <span class="text-slate-400 text-xs shrink-0">密码</span>
            <span class="font-mono break-all">{{ pwd.password }}</span>
          </div>
        </div>
        <div class="flex gap-2 mt-4">
          <button @click="copyPwd" class="flex-1 py-1.5 rounded-lg bg-blue-600 text-white text-xs">{{ pwd.copied ? '✓ 已复制' : '复制密码' }}</button>
          <button @click="pwd.show=false" class="px-3 py-1.5 rounded-lg border border-slate-200 dark:border-slate-700 text-xs">关闭</button>
        </div>
        <p class="text-[10px] text-slate-400 mt-2 text-center">{{ pwd.countdown }}s 后自动关闭</p>
      </div>
    </div>
  </div>`,
  setup() {
    const expanded = Vue.reactive({});
    const pwd = Vue.reactive({ show: false, name: '', login_user: '', password: '', copied: false, countdown: 0, _timer: null });
    const singleProvider = Vue.computed(() => !!store.accountsModal.providerSlug);
    const title = Vue.computed(() => {
      const slug = store.accountsModal.providerSlug;
      if (!slug) return '全部账号';
      const p = store.providers.find(x => x.slug === slug);
      return (p ? p.name : slug) + ' 的账号';
    });
    const accountList = Vue.computed(() => {
      const slug = store.accountsModal.providerSlug;
      return slug ? store.accounts.filter(a => a.platform === slug) : store.accounts;
    });
    // 全部模式下按平台分组，保持 平台→账号→资产 层级
    const groups = Vue.computed(() => {
      const m = new Map();
      for (const a of accountList.value) {
        const key = a.platform || '';
        if (!m.has(key)) m.set(key, []);
        m.get(key).push(a);
      }
      return [...m.entries()].map(([platform, accounts]) => ({ platform, accounts }));
    });
    function platformName(slug) {
      const p = store.providers.find(x => x.slug === slug);
      return p ? p.name : slug;
    }
    function assetsOf(a) {
      return store.assets.filter(x => x.account_id === a.id);
    }
    function toggle(id) { expanded[id] = !expanded[id]; }
    // 账号所属平台配了 sdk_type 且账号有 API 配置时，提供测试/同步
    function providerOf(a) {
      return store.providers.find(p => p.slug === a.platform) || store.providers.find(p => p.name === a.platform);
    }
    function canSync(a) {
      const p = providerOf(a);
      return !!(a.has_api_config && p && p.sdk_type);
    }
    async function testAcct(a) {
      try {
        const r = await Api.post('/accounts/' + a.id + '/test', {});
        alert(r.ok ? '✅ ' + r.message : '❌ ' + r.message);
      } catch (e) { alert('测试失败：' + e.message); }
    }
    async function syncAcct(a) {
      try {
        const r = await Api.post('/accounts/' + a.id + '/sync', {});
        const lines = ['同步完成'];
        if (r.vps) lines.push('VPS：新增 ' + r.vps.created + ' / 更新 ' + r.vps.updated);
        if (r.domains) lines.push('域名：新增 ' + r.domains.created + ' / 更新 ' + r.domains.updated);
        if (r.vps_error) lines.push('VPS 错误：' + r.vps_error);
        if (r.domains_error) lines.push('域名错误：' + r.domains_error);
        alert(lines.join('\n'));
        await Promise.all([loadAccounts(), loadAssets()]);
      } catch (e) { alert('同步失败：' + e.message); }
    }
    // 每次打开重置展开状态
    Vue.watch(() => store.accountsModal.show, (show) => {
      if (show) for (const k of Object.keys(expanded)) delete expanded[k];
    });
    // 查看密码：拉取明文并展示，8 秒后自动关闭；关闭时清除定时器
    async function viewPwd(a) {
      try {
        const r = await Api.get('/accounts/' + a.id + '/password');
        pwd.name = a.remark || a.name;
        pwd.login_user = r.login_user || '';
        pwd.password = r.password;
        pwd.copied = false;
        pwd.show = true;
        if (pwd._timer) clearInterval(pwd._timer);
        pwd.countdown = 8;
        pwd._timer = setInterval(() => {
          pwd.countdown--;
          if (pwd.countdown <= 0) { pwd.show = false; clearInterval(pwd._timer); pwd._timer = null; }
        }, 1000);
      } catch (e) { alert('获取密码失败：' + e.message); }
    }
    function copyPwd() {
      const done = () => { pwd.copied = true; setTimeout(() => { pwd.copied = false; }, 1500); };
      if (navigator.clipboard && navigator.clipboard.writeText) {
        navigator.clipboard.writeText(pwd.password).then(done).catch(() => fallbackCopy());
      } else { fallbackCopy(); }
      function fallbackCopy() {
        const ta = document.createElement('textarea');
        ta.value = pwd.password; document.body.appendChild(ta); ta.select();
        try { document.execCommand('copy'); done(); } catch (e) {}
        document.body.removeChild(ta);
      }
    }
    // 关闭弹窗时清定时器，避免泄漏
    Vue.watch(() => pwd.show, (v) => { if (!v && pwd._timer) { clearInterval(pwd._timer); pwd._timer = null; } });
    return {
      store, Fmt, expanded, pwd, singleProvider, title, accountList, groups,
      platformName, assetsOf, toggle, canSync, testAcct, syncAcct, viewPwd, copyPwd,
      add: () => openAccountCreate(store.accountsModal.providerSlug || ''),
      edit: openAccountEdit, del: deleteAccount,
    };
  },
};
