1
jhzh
2026-05-22 ef52095f5e9f0a9fe2da779bb1573947d77d75b6
src/components/Transform/perpetual-position-list/index.vue
@@ -1,130 +1,286 @@
<template>
  <!-- 持有仓位列表 -->
  <div id="cryptos">
    <!--        <div class="flex justify-between" v-if="listData.length">-->
    <!--            <div class="flex" @click.stop="changeIcon">-->
    <!--                <img v-show="!iconShow" src="../../assets/image/public/grey-rounded.png" alt="" class="w-38 h-38"/>-->
    <!--                <img v-show="iconShow" src="../../assets/image/public/blue-rounded.png" alt="" class="w-38 h-38"/>-->
    <!--                <div class="ml-37xw">{{ $t('隐藏其它合约') }}</div>-->
    <!--            </div>-->
    <!--            <button class="border-none all-cancel-btn pl-34 pr-34 pt-10 pb-10 font-25" @click="onSellAll">{{ $t('一键平仓') }}</button>-->
    <!--        </div>-->
    <div class="border-b-color" v-for="item in listData" :key="item.order_no">
      <div class="flex justify-between pt-44 pb-44">
        <div class="flex flex-col">
          <div class="flex items-center">
            <div class="pl-18 pr-18 pt-3 pb-3 text-white open-btn font-28"
              :class="item.direction == 'buy' ? ' bg-green' : 'bg-red'">
              {{ item.direction == 'buy' ? $t('开多') : $t('开空') }}
            </div>
            <div class="ml-22 font-31 font-600 ">
              <span class="textColor">{{ item.name }} {{ $t('永续') }}</span>
              <span class="text-grey font-28 font-400 ml-17 mr-17">
                {{ item.locationType == 1 ? $t('全仓') : $t('逐仓') }}
                {{ item.lever_rate }}x</span>
            </div>
            <img v-if="item.direction == 'buy'" src="@/assets/image/public/green-leverage.png" alt=""
              class="w-32 h-32" />
            <img v-else src="@/assets/image/public/red-leverage.png" alt="" class="w-32 h-32" />
  <!-- 持有仓位列表:参考图样式 border-b-color 细浅灰、右上分享、保证金加号弹框 -->
  <div >
    <div class="position-card border-b-color" v-for="item in listData" :key="item.order_no">
      <!-- 顶部:Buy/Sell 标签 + 交易对 Cross 100X + 刷新 + 分享 -->
      <div class="flex justify-between items-center pt-44 pb-24">
        <div class="flex items-center">
          <div class="position-tag font-28" :class="item.direction == 'buy' ? 'bg-green' : 'bg-red'">
            {{ item.direction == 'buy' ? $t('买入') : $t('卖出') }}
          </div>
          <div class="ml-22 font-31 font-600 textColor">
            <span>{{ item.name }}</span>
            <span class="text-grey font-28 font-400 ml-17">
              {{ item.locationType == 1 ? 'Cross' : 'Part' }} {{ item.lever_rate }}X</span>
          </div>
        </div>
        <div class="flex items-center" style="gap: 12px;">
          <van-icon name="replay" class="position-icon text-grey" size="20" />
          <van-icon name="share-o" class="position-icon text-grey" size="20" @click.stop="openShare(item)" />
        </div>
      </div>
      <!-- PnL -->
      <div class="flex justify-between font-28 pb-24">
        <div>
          <div class="text-grey">{{ $t('PnL(USDT)') || 'PnL(USDT)' }}</div>
          <div class="mt-12" :class="item.profit > 0 ? 'text-green' : 'text-red'">
            {{ item.profit > 0 ? '+' : '' }}{{ (item.profit != null ? item.profit : 0).toFixed(4) }}
            <span class="ml-8">[{{ item.change_ratio != null ? item.change_ratio : 0 }}%]</span>
          </div>
        </div>
      </div>
      <div class="flex justify-between font-28">
      <!-- Size / Margin(USDT)+加号 / Margin Ratio -->
      <div class="flex font-28 pb-20">
        <div class="flex-1">
          <div class="text-grey">{{ routeType == 'cryptos' ? $t('未实现盈亏(USDT)') : $t('未实现盈亏(USD)') }}</div>
          <div class="mt-20" :class="item.profit > 0 ? 'text-green' : 'text-red'">
            {{ item.profit > 0 ? '+' + item.profit.toFixed(4) : item.profit.toFixed(4) }}</div>
          <div class="text-grey">{{ $t('Size') || 'Size' }}</div>
          <div class="mt-12 textColor">{{ item.volume != null ? item.volume : (item.deposit != null ? item.deposit : '-') }}</div>
        </div>
        <div class="flex-1">
          <div class="text-grey text-center">ROE</div>
          <div class="mt-20 text-center" :class="item.change_ratio / 1 > 0 ? 'text-green' : 'text-red'">{{
            item.change_ratio
          }}%
        <div class="flex-1 flex items-center justify-center">
          <div>
            <div class="text-grey">{{ $t('margin') }}(USDT)</div>
            <div class="mt-12 flex items-center justify-center">
              <span class="textColor">{{ item.deposit }}</span>
              <span class="position-plus ml-8" @click.stop="openQuickAdd(item)" v-if="item.locationType == 0">+</span>
            </div>
          </div>
        </div>
        <div class="flex-1 flex justify-end">
          <button class="font-30 detail-btn border-light-blue greyBg colorMain w-125 h-60" @click="goDetail(item)">{{
            $t('详情') }}</button>
        <div class="flex-1 text-right">
          <div class="text-grey">{{ $t('Margin Ratio') || 'Margin Ratio' }}</div>
          <div class="mt-12 textColor">{{ item.margin_ratio != null ? item.margin_ratio + '%' : '-' }}</div>
        </div>
      </div>
      <div class="flex pt-44 pb-32 font-28">
      <!-- Entry Price / Mark Price / Liq. Price -->
      <div class="flex font-28 pb-24">
        <div class="flex-1">
          <div class="text-grey">{{ $t('持仓数量') }}</div>
          <div class="mt-20 textColor">{{ reserve(item.volume / (item.lever_rate ? item.lever_rate : 1),4) }}*{{
            item.lever_rate ? item.lever_rate : 1 }}x</div>
          <div class="text-grey">{{ $t('Entry Price') || 'Entry Price' }}</div>
          <div class="mt-12 textColor">{{ item.trade_avg_price }}</div>
        </div>
        <div class="flex-1 text-center  font-28">
          <div class="text-grey">{{ $t('交易金额') }} ( {{ routeType == 'cryptos' ? 'USDT' : 'USD' }})</div>
          <div class="mt-20 textColor">{{ item.deposit }}</div>
        <div class="flex-1 text-center">
          <div class="text-grey">{{ $t('Mark Price') || 'Mark Price' }}</div>
          <div class="mt-12 textColor">{{ item.mark_price }}</div>
        </div>
        <div class="flex-1 flex flex-col items-end  font-28">
          <div class="text-grey">{{ $t('开仓价格') }}</div>
          <div class="mt-20 textColor">{{ item.trade_avg_price }}</div>
        <div class="flex-1 text-right">
          <div class="text-grey">{{ $t('Liq. Price') || 'Liq. Price' }}</div>
          <div class="mt-12 textColor">{{ item.force_close_rice || '-' }}</div>
        </div>
      </div>
      <div class="flex pb-32  font-28">
        <div class="flex-1">
          <div class="text-grey">{{ $t('标记价格') }}</div>
          <div class="mt-20 textColor">{{ item.mark_price }}</div>
        </div>
        <div class="flex-1">
          <div class="text-grey text-center">{{ $t('强平价格') }}</div>
          <div class="mt-20 textColor text-center">{{ item.force_close_rice }}</div>
        </div>
        <div class="flex-1 flex flex-col items-end justify-end">
          <button class="greyBg textColor border-none pl-34 pr-34 pt-10 pb-10 rounded-ban"
            @click="onSell(item.order_no)">
            {{ $t('平仓') }}</button>
      <!-- 细浅灰分隔线 + 底部时间与 TP/SL、Close -->
      <div class="position-card-border"></div>
      <div class="flex justify-between items-center pt-24 pb-32 font-24">
        <span class="text-grey">{{ formatTime(item.create_time) }}</span>
        <div class="flex" style="gap: 12px;">
          <button class="position-bottom-btn" @click.stop="onSell(item.order_no)">{{ $t('平仓') }}</button>
          <button class="position-bottom-btn" @click.stop="goTpSl(item)">{{ $t('TP/SL') || 'TP/SL' }}</button>
        </div>
      </div>
      <!-- <div class="flex  pb-32  font-28">
        <div class="flex-1 flex flex-col items-center justify-end">
          <button class="font-30 detail-btn border-light-blue greyBg colorMain w-125 h-60" @click="goDetail(item)">{{
            $t('详情') }}</button>
        </div>
      </div> -->
    </div>
    <div class="text-grey text-center py-300 font-30" v-if="!listData.length">{{ $t('您目前没有持仓') }}</div>
    <!-- Quick addition 弹框 图三样式 -->
    <van-popup v-model:show="showQuickAdd" position="bottom" round class="quick-add-popup">
      <div class="quick-add-wrap">
        <div class="quick-add-header">
          <span class="quick-add-title">{{ $t('Quick addition') }}</span>
          <span class="quick-add-close" @click="showQuickAdd = false">×</span>
        </div>
        <div class="quick-add-row">
          <label class="quick-add-label">{{ $t('Quantity') || 'Quantity' }}:</label>
          <input v-model.number="quickAddAmount" type="number" class="quick-add-input" />
          <span class="quick-add-unit">USDT</span>
        </div>
        <div class="quick-add-slider-wrap">
          <van-slider v-model="quickAddPercent" :min="0" :max="100" bar-height="4px" active-color="#7c3aed" />
          <span class="quick-add-percent">{{ quickAddPercent }}%</span>
        </div>
        <div class="quick-add-balance">{{ $t('Futures asset balance') }}: {{ quickAddBalance }} USDT</div>
        <div class="quick-add-submit" @click="submitQuickAdd">{{ $t('Submit') }}</div>
      </div>
    </van-popup>
    <!-- TP/SL 止盈止损弹框:底部弹出,展示仓位信息 + 止盈/止损值 + Submit -->
    <van-popup v-model:show="showTpSl" position="bottom" round class="tpsl-popup">
      <div class="tpsl-wrap">
        <div class="tpsl-header">
          <h2 class="tpsl-title">{{ tpSlItem.name || 'BTC USDT' }} {{ tpSlItem.direction == 'buy' ? $t('Long') : $t('Short') }} {{ tpSlItem.lever_rate }}X {{ $t('Take Profit') }} {{ $t('Stop Loss') }}</h2>
          <span class="tpsl-close" @click="showTpSl = false">×</span>
        </div>
        <div class="tpsl-info-grid">
          <div class="tpsl-info-item">
            <div class="tpsl-info-label">{{ $t('Entry Price') }}</div>
            <div class="tpsl-info-value">{{ tpSlItem.trade_avg_price || '-' }}</div>
          </div>
          <div class="tpsl-info-item">
            <div class="tpsl-info-label">{{ $t('Mark Price') }}</div>
            <div class="tpsl-info-value">{{ tpSlItem.mark_price || '-' }}</div>
          </div>
          <div class="tpsl-info-item">
            <div class="tpsl-info-label">{{ $t('Margin(USDT)') }}</div>
            <div class="tpsl-info-value">{{ tpSlItem.deposit || '-' }}</div>
          </div>
          <div class="tpsl-info-item">
            <div class="tpsl-info-label">{{ $t('Liq. Price') }}</div>
            <div class="tpsl-info-value tpsl-liq">{{ tpSlItem.force_close_rice || '-' }}</div>
          </div>
        </div>
        <div class="tpsl-row">
          <label class="tpsl-row-label">{{ $t('Take Profit') }}</label>
          <input v-model="tpSlTakeProfit" type="number" class="tpsl-input" :placeholder="$t('Please enter') || 'Please enter'" />
        </div>
        <div class="tpsl-row">
          <label class="tpsl-row-label">{{ $t('Stop Loss') }}</label>
          <input v-model="tpSlStopLoss" type="number" class="tpsl-input" :placeholder="$t('Please enter') || 'Please enter'" />
        </div>
        <div class="tpsl-submit" @click="submitTpSl">{{ $t('Submit') }}</div>
      </div>
    </van-popup>
    <!-- 分享弹框:按参考图样式,背景 share-order.jpg,居中圆角卡片 -->
    <van-popup v-model:show="showShare" position="bottom" round class="share-popup-wrap" :style="{ height: '100%', backgroundColor: 'transparent' }" @click.self="showShare = false">
      <div class="share-mask" :style="{ backgroundColor: 'transparent' }" @click="showShare = false"></div>
      <div class="share-popup-body">
        <div ref="shareCardRef" class="share-card" :style="shareCardBgStyle" @click.stop>
          <div class="share-card-close" @click="showShare = false">×</div>
          <div class="share-card-top" :style="{ marginTop: '20%' }">
            <div class="share-card-pair">{{ shareItem.name || 'BTC USDT' }}</div>
            <div class="share-card-pos">{{ shareItem.direction == 'buy' ? $t('Long') : $t('Short') }} / {{ shareItem.lever_rate }}X</div>
          </div>
          <div class="share-card-pnl" :class="(shareItem.profit != null && shareItem.profit >= 0) ? 'share-pnl-green' : 'share-pnl-red'">{{ shareItem.change_ratio != null ? shareItem.change_ratio : 0 }}%</div>
          <div class="share-card-entry" :style="{ marginTop: '60px' }">{{ $t('Entry Price') }}: {{ shareItem.trade_avg_price || '-' }}</div>
          <div class="share-card-pnl-usdt">{{ $t('PnL(USDT)') }}: {{ shareItem.profit != null ? shareItem.profit.toFixed(4) : '-' }}</div>
          <div class="share-card-ref-row">
            <div class="share-card-ref-left">
              <div class="share-card-ref">{{ $t('Referral Code') }}</div>
              <div class="share-card-code">{{ userInfo.usercode || '91872511' }}</div>
            </div>
            <div class="share-card-qr"><canvas ref="shareQrRef" width="160" height="160"></canvas></div>
          </div>
          <button type="button" class="share-card-copy" @click="copyShareLink">{{ $t('复制链接') }}</button>
          <!-- <div class="share-card-save-link" @click="saveShareImage">{{ $t('保存图片') }}</div> -->
        </div>
      </div>
    </van-popup>
  </div>
</template>
<script>
import { _orderSellBatch, _contractOrderClose } from "@/service/trade.api";
import { _orderSellBatch, _contractOrderClose, _addDepositOpen, _setStopPrice } from "@/service/trade.api";
import { showConfirmDialog, showToast } from 'vant';
import { reserve } from "@/utils/utis";
//import { i18n } from "@/i18n";
import { mapGetters } from 'vuex';
import QRCode from 'qrcode';
import html2canvas from 'html2canvas';
import shareOrderBg from '@/assets/imgs/new/share-order.jpg';
export default {
  name: "perpetualPositionList",
  data() {
    return {
      iconShow: false,
      routeType: 'cryptos'
      routeType: 'cryptos',
      showQuickAdd: false,
      quickAddAmount: 0,
      quickAddPercent: 0,
      quickAddBalance: '0',
      _skipQuickAddSync: false,
      quickAddItem: null,
      showShare: false,
      shareItem: {},
      showTpSl: false,
      tpSlItem: {},
      tpSlTakeProfit: '',
      tpSlStopLoss: ''
    }
  },
  props: {
    type: {
      type: String,
      default: '2' // 2 永续合约历史持仓,3交割合约持仓
    type: { type: String, default: '2' },
    listData: { type: Array, default: () => [] },
    futuresBalance: { type: [String, Number], default: '0' }
  },
  computed: {
    ...mapGetters('user', ['userInfo']),
    quickAddBalanceNum() {
      return Math.max(0, parseFloat(this.quickAddBalance) || 0);
    },
    listData: {
      type: Array,
      default() {
        return []
      }
    shareCardBgStyle() {
      return {
        backgroundImage: `url(${shareOrderBg})`,
        backgroundSize: 'cover',
        backgroundRepeat: 'no-repeat',
        backgroundPosition: 'center'
      };
    }
  },
  mounted() {
    this.routeType = this.$route.query.type
    this.routeType = this.$route.query.type || 'cryptos';
  },
  watch: {
    showShare(val) {
      if (val && this.shareItem && this.$refs.shareQrRef) {
        this.$nextTick(() => this.drawShareQr());
      }
    },
    quickAddPercent(val) {
      if (this._skipQuickAddSync) return;
      this._skipQuickAddSync = true;
      const num = this.quickAddBalanceNum;
      const amount = num <= 0 ? 0 : Math.min(num, (num * val) / 100);
      this.quickAddAmount = Math.round(amount * 100) / 100;
      this.$nextTick(() => { this._skipQuickAddSync = false; });
    },
    quickAddAmount(val) {
      if (this._skipQuickAddSync) return;
      const num = this.quickAddBalanceNum;
      if (num <= 0) {
        this._skipQuickAddSync = true;
        this.quickAddPercent = 0;
        this.$nextTick(() => { this._skipQuickAddSync = false; });
        return;
      }
      this._skipQuickAddSync = true;
      const pct = Math.min(100, Math.max(0, (Number(val) || 0) / num * 100));
      this.quickAddPercent = Math.round(pct * 100) / 100;
      this.$nextTick(() => { this._skipQuickAddSync = false; });
    }
  },
  methods: {
    reserve,
    changeIcon() {
      this.iconShow = !this.iconShow;
    formatTime(t) {
      if (!t) return '-';
      const d = new Date(typeof t === 'number' ? t * 1000 : t);
      const pad = n => (n + '').padStart(2, '0');
      return `${pad(d.getDate())}-${pad(d.getMonth() + 1)}-${d.getFullYear()} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
    },
    goDetail(item) {
      this.$router.push({ path: "/cryptos/orderDetail", query: { order_no: item.order_no } });
    },
    onSell(order_no) { // 平仓单个
    goTpSl(item) {
      this.tpSlItem = { ...item };
      this.tpSlTakeProfit = item.take_profit_price != null ? String(item.take_profit_price) : (item.profit_price != null ? String(item.profit_price) : '');
      this.tpSlStopLoss = item.stop_loss_price != null ? String(item.stop_loss_price) : (item.loss_price != null ? String(item.loss_price) : '');
      this.showTpSl = true;
    },
    submitTpSl() {
      if (!this.tpSlItem || !this.tpSlItem.order_no) {
        showToast(this.$t('请选择仓位'));
        return;
      }
      const stopPriceProfit = String(this.tpSlTakeProfit || '').trim();
      const stopPriceLoss = String(this.tpSlStopLoss || '').trim();
      if (!stopPriceProfit && !stopPriceLoss) {
        showToast(this.$t('请输入止盈或止损价格'));
        return;
      }
      _setStopPrice({
        orderNo: this.tpSlItem.order_no,
        stopPriceProfit: stopPriceProfit || undefined,
        stopPriceLoss: stopPriceLoss || undefined
      }).then(() => {
        showToast(this.$t('提交成功'));
        this.showTpSl = false;
        this.$emit('sell');
      }).catch(() => {});
    },
    onSell(order_no) {
      showConfirmDialog({
        confirmButtonText: this.$t('确定'),
        cancelButtonText: this.$t('取消'),
@@ -132,16 +288,63 @@
        message: this.$t('是否平仓?'),
      }).then(() => {
        _contractOrderClose({ order_no }).then(() => {
          showToast(this.$t('平仓成功'))
          this.$emit('sell', order_no)
        })
      }).catch(() => { });
          showToast(this.$t('平仓成功'));
          this.$emit('sell', order_no);
        });
      }).catch(() => {});
    },
    onSellAll() { // 平仓全部
      _orderSellBatch().then(() => {
        this.showToast(this.$t('平仓成功'))
        this.$emit('sell')
      })
    openQuickAdd(item) {
      this.quickAddItem = item;
      this.quickAddBalance = (this.userInfo && this.userInfo.balance) || '0';
      this.quickAddAmount = 0;
      this.quickAddPercent = 0;
      this.showQuickAdd = true;
    },
    submitQuickAdd() {
      if (!this.quickAddItem || !this.quickAddItem.order_no) {
        showToast(this.$t('请选择仓位'));
        return;
      }
      const amount = Number(this.quickAddAmount);
      if (isNaN(amount) || amount <= 0) {
        showToast(this.$t('请输入有效追加金额'));
        return;
      }
      _addDepositOpen({
        orderNo: this.quickAddItem.order_no,
        addDepositOpen: amount
      }).then(() => {
        showToast(this.$t('提交成功'));
        this.showQuickAdd = false;
        this.$emit('sell'); // 刷新持仓列表
      }).catch(() => {});
    },
    openShare(item) {
      this.shareItem = { ...item };
      this.showShare = true;
    },
    drawShareQr() {
      const url = (this.userInfo && this.userInfo.url) || window.location.origin + '/#/register?code=' + (this.userInfo && this.userInfo.usercode);
      const canvas = this.$refs.shareQrRef;
      if (canvas) QRCode.toCanvas(canvas, url, { width: 160, margin: 2 });
    },
    copyShareLink() {
      const url = (this.userInfo && this.userInfo.url) || window.location.origin + '/#/register?code=' + (this.userInfo && this.userInfo.usercode);
      navigator.clipboard.writeText(url).then(() => showToast(this.$t('复制成功')));
    },
    async saveShareImage() {
      const el = this.$refs.shareCardRef;
      if (!el) return;
      try {
        const canvas = await html2canvas(el, { useCORS: true, backgroundColor: '#1e1e2e', scale: 2 });
        const link = document.createElement('a');
        link.download = 'share-position.png';
        link.href = canvas.toDataURL('image/png');
        link.click();
        showToast(this.$t('保存成功'));
      } catch (e) {
        showToast(this.$t('保存失败'));
      }
    }
  }
}
@@ -149,20 +352,231 @@
<style lang="scss" scoped>
@import "@/assets/init.scss";
/* 与参考图一致:细浅灰分隔线 */
.border-b-color {
  border-bottom: 1px solid #e8e8e8;
}
.position-card-border {
  border-bottom: 1px solid #e8e8e8;
  margin: 0 -20px;
}
.position-tag {
  padding: 4px 12px;
  border-radius: 6px;
  color: #fff;
  font-weight: 600;
}
.bg-green { background: #0ecb81; }
.bg-red { background: #f6465d; }
.position-icon { cursor: pointer; }
.position-plus {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: linear-gradient(135deg, #7c3aed, #5b21b6);
  color: #fff;
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
}
.position-bottom-btn {
  padding: 8px 20px;
  font-size: 24px;
  color: #333;
  background: #fff;
  border: 1px solid #e8e8e8;
  border-radius: 8px;
  cursor: pointer;
}
/* Quick addition 弹框 图三样式(字体放大1倍,上下左右边距加一倍) */
.quick-add-popup.van-popup { background: #fff; border-radius: 16px 16px 0 0; }
.quick-add-wrap { padding: 48px 40px 64px; }
.quick-add-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 48px; }
.quick-add-title { font-size: 36px; font-weight: 600; color: #1e1e1e; }
.quick-add-close { font-size: 56px; color: #999; cursor: pointer; }
.quick-add-row { display: flex; align-items: center; margin-bottom: 40px; }
.quick-add-label { font-size: 28px; color: #333; margin-right: 24px; flex-shrink: 0; }
.quick-add-input { flex: 1; height: 88px; border: 1px solid #e8e8e8; border-radius: 8px; padding: 0 24px; font-size: 28px; }
.quick-add-unit { margin-left: 24px; font-size: 28px; color: #666; }
.quick-add-slider-wrap { margin-bottom: 24px; position: relative; }
.quick-add-slider-wrap .van-slider { margin: 0;width: 85%; }
.quick-add-percent { position: absolute; right: 0; top: -8px; font-size: 24px; color: #999; }
.quick-add-balance { font-size: 24px; color: #999; margin-bottom: 48px; }
.quick-add-submit {
  height: 96px; line-height: 96px; text-align: center; font-size: 32px; font-weight: 600; color: #fff;
  background: linear-gradient(90deg, #7c3aed, #5b21b6); border-radius: 10px; cursor: pointer;
}
/* TP/SL 止盈止损弹框:白底、数据一排、输入框 F6F5FA */
.tpsl-popup.van-popup { background: #fff; border-radius: 16px 16px 0 0; }
.tpsl-wrap { padding: 48px 40px 64px; background: #fff; }
.tpsl-header { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 40px; }
.tpsl-title { font-size: 32px; font-weight: 600; color: #1e1e1e; line-height: 1.4; margin: 0; flex: 1; padding-right: 16px; }
.tpsl-close { font-size: 56px; color: #999; cursor: pointer; flex-shrink: 0; }
.tpsl-info-grid { display: flex; flex-wrap: nowrap; gap: 16px; margin-bottom: 40px; }
.tpsl-info-item { flex: 1; min-width: 0; }
.tpsl-info-label { font-size: 22px; color: #999; margin-bottom: 8px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tpsl-info-value { font-size: 24px; color: #333; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tpsl-info-value.tpsl-liq { color: #7c3aed; }
.tpsl-row { display: flex; align-items: center; margin-bottom: 32px; }
.tpsl-row-label { font-size: 28px; color: #333; margin-right: 24px; flex-shrink: 0; width: 140px; }
.tpsl-input { flex: 1; height: 88px; background: #F6F5FA; border: none; border-radius: 8px; padding: 0 24px; font-size: 28px; }
.tpsl-submit {
  height: 96px; line-height: 96px; text-align: center; font-size: 32px; font-weight: 600; color: #fff;
  background: linear-gradient(90deg, #7c3aed, #5b21b6); border-radius: 10px; cursor: pointer; margin-top: 48px;
}
/* 分享弹框:弹层与内容区透明,自管遮罩半透明,背后露出页面 */
.share-popup-wrap.van-popup { background: transparent !important; }
.share-popup-wrap :deep(.van-popup__content) {
  background: transparent !important;
  min-height: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
}
/* 自管遮罩:铺满、半透明黑,点击关闭 */
.share-mask {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.5);
  z-index: 0;
}
.share-popup-body {
  position: relative;
  z-index: 1;
  min-height: 100vh;
  height: 100%;
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 40px 20px 60px;
  box-sizing: border-box;
  background: transparent !important;
  pointer-events: none;
}
.share-popup-body .share-card { pointer-events: auto; }
.share-popup-body .share-card {
  max-width: 90vw;
  width: 85%;
  flex-shrink: 0;
}
/* 卡片:背景 share-order.jpg,宽高由外层控制,内部用相对单位响应 */
.share-card {
  position: relative;
  width: 100%;
  min-height: 40%;
  padding: 5% 6% 5%;
  border-radius: 16px;
  overflow: hidden;
  color: #fff;
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
  font-size: clamp(12px, 2.2vw, 20px);
}
.share-card-close {
  position: absolute;
  top: 3%;
  right: 3%;
  width: 2.2em;
  height: 2.2em;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 3em;
  line-height: 1;
  color: #fff;
  cursor: pointer;
  z-index: 2;
}
.share-card-top {
  margin-bottom: 0.3em;
}
.share-card-pair {
  font-size: 1.5em;
  font-weight: 700;
  color: #fff;
  line-height: 1.3;
}
.share-card-pos {
  font-size: 1.15em;
  color: rgba(255,255,255,0.9);
  margin-top: 0.2em;
}
.share-card-pnl {
  font-size: 2.2em;
  font-weight: 700;
  margin: 0.5em 0 0.6em;
  line-height: 1.2;
}
.share-pnl-green { color: #0ecb81; }
.share-pnl-red { color: #f6465d; }
.share-card-entry,
.share-card-pnl-usdt {
  font-size: 1.1em;
  color: #fff;
  margin-bottom: 0.25em;
}
.share-card-ref-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: 1em;
  margin-bottom: 0.8em;
  gap: 0.5em;
}
.share-card-ref-left { flex: 1; min-width: 0; }
.share-card-ref {
  font-size: 1em;
  color: rgba(255,255,255,0.9);
  margin-bottom: 0.2em;
}
.share-card-code {
  font-size: 1.35em;
  font-weight: 700;
  color: #fff;
}
.share-card-qr {
  width: 22%;
  max-width: 110px;
  aspect-ratio: 1;
  flex-shrink: 0;
  background: #fff;
  padding: 4%;
  border-radius: 8px;
}
.share-card-qr canvas { width: 100% !important; height: 100% !important; display: block; }
.share-card-copy {
  width: 100%;
  height: 2.8em;
  line-height: 2.8em;
  text-align: center;
  font-size: 1em;
  font-weight: 600;
  color: #fff;
  background: linear-gradient(90deg, #7c3aed, #5b21b6);
  border: none;
  border-radius: 10px;
  cursor: pointer;
  z-index: 2;
  margin-top: auto;
}
.share-card-save-link {
  text-align: center;
  font-size: 0.85em;
  color: rgba(255,255,255,0.8);
  margin-top: 0.6em;
  cursor: pointer;
  z-index: 2;
}
#cryptos {
  .open-btn {
    border-radius: 5px;
  }
  .detail-btn {
    border: 1px solid;
    border-radius: 2.5rem;
    background: $US_tabActice_background;
    color: $color_main;
  }
  .text-green {
    color: #06AD95;
  }
  .text-green { color: #06AD95; }
}
</style>