<template>
  <div v-if="show" class="pl-mask" @click.stop>
    <div class="pl-card" @click.stop>
      <div class="pl-body">
        <div class="pl-ring-wrap" :style="{ width: ringPx + 'px', height: ringPx + 'px' }">
          <svg class="pl-ring-svg" :viewBox="viewBox" aria-hidden="true">
            <defs>
              <linearGradient :id="progressGradientId" x1="12" y1="18" x2="88" y2="82" gradientUnits="userSpaceOnUse">
                <stop offset="0%" class="pl-grad-stop pl-grad-stop--start" />
                <stop offset="52%" class="pl-grad-stop pl-grad-stop--mid" />
                <stop offset="100%" class="pl-grad-stop pl-grad-stop--end" />
              </linearGradient>
            </defs>
            <circle class="pl-ring-track" :cx="cx" :cy="cy" :r="radius" fill="none" :stroke-width="strokeW" />
            <circle
              class="pl-ring-progress"
              :cx="cx"
              :cy="cy"
              :r="radius"
              fill="none"
              :stroke="`url(#${progressGradientId})`"
              :stroke-width="strokeW"
              stroke-linecap="round"
              :stroke-dasharray="dashArray"
              :stroke-dashoffset="dashOffset"
              :transform="`rotate(-90 ${cx} ${cy})`"
            />
          </svg>
          <div class="pl-percent-wrap">
            <span class="pl-percent">{{ displayPercent }}%</span>
          </div>
        </div>
        <p class="pl-title">
          {{ title }}
        </p>
      </div>
      <div class="pl-divider" />
      <button type="button" class="pl-cancel" @click="onCancel">
        <span class="pl-cancel__text">{{ cancelText }}</span>
      </button>
    </div>
  </div>
</template>

<script setup>
import { computed } from "vue";

/** 避免同页多个实例时 defs id 冲突 */
const progressGradientId = `pl-ring-grad-${Math.random().toString(36).slice(2, 10)}`;

const props = defineProps({
  show: {
    type: Boolean,
    default: false
  },
  percent: {
    type: Number,
    default: 0
  },
  title: {
    type: String,
    default: "上传中"
  },
  cancelText: {
    type: String,
    default: "取消上传"
  }
});

const emit = defineEmits(["cancel"]);

const displayPercent = computed(() => {
  const n = Number(props.percent);
  if (Number.isNaN(n)) return 0;
  return Math.round(Math.min(100, Math.max(0, n)));
});

/** SVG 环形：与 Element 主题色对齐 */
const vb = 100;
const cx = vb / 2;
const cy = vb / 2;
const radius = 38;
const strokeW = 7;
const viewBox = `0 0 ${vb} ${vb}`;
const ringPx = 118;

const circumference = 2 * Math.PI * radius;

const dashArray = computed(() => `${circumference} ${circumference}`);

const dashOffset = computed(() => {
  const p = displayPercent.value / 100;
  return circumference * (1 - p);
});

function onCancel() {
  emit("cancel");
}
</script>

<style lang="scss" scoped>
.pl-mask {
  position: fixed;
  inset: 0;
  z-index: 10000;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: rgb(0 0 0 / 50%);
}
.pl-card {
  box-sizing: border-box;
  width: 100%;
  max-width: 320px;
  overflow: hidden;
  background: var(--el-bg-color, #ffffff);
  border-radius: 12px;
  box-shadow: var(--el-box-shadow, 0 12px 32px rgb(0 0 0 / 12%));
}
.pl-body {
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 32px 24px 24px;
}
.pl-ring-wrap {
  position: relative;
  flex-shrink: 0;
}
.pl-ring-svg {
  display: block;
  width: 100%;
  height: 100%;
}
.pl-ring-track {
  stroke: var(--el-color-primary-light-8, #e8ecfc);
}
.pl-ring-progress {
  transition: stroke-dashoffset 0.35s ease;
}
.pl-grad-stop--start {
  stop-color: var(--el-color-primary-light-5, #a3b9fc);
}
.pl-grad-stop--mid {
  stop-color: var(--el-color-primary, #6c8ff8);
}
.pl-grad-stop--end {
  stop-color: var(--el-color-primary-dark-2, #4a6fd6);
}
.pl-percent-wrap {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}
.pl-percent {
  font-size: 22px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  line-height: 1.2;
  color: var(--el-color-primary, #6c8ff8);
}
.pl-title {
  margin: 20px 0 0;
  font-size: 15px;
  font-weight: 500;
  line-height: 1.4;
  color: var(--el-text-color-primary, #303133);
  text-align: center;
}
.pl-divider {
  height: 1px;
  margin: 0;
  background: var(--el-border-color-lighter, #ebeef5);
}
.pl-cancel {
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  padding: 14px 20px 16px;
  margin: 0;
  font: inherit;
  cursor: pointer;
  background: transparent;
  border: none;
  &:hover .pl-cancel__text {
    color: var(--el-color-primary-light-3, #8aa6fa);
  }
  &:active .pl-cancel__text {
    color: var(--el-color-primary-dark-2, #4a6fd6);
  }
}
.pl-cancel__text {
  font-size: 15px;
  font-weight: 500;
  line-height: 1.4;
  color: var(--el-color-primary, #6c8ff8);
  transition: color 0.15s ease;
}
</style>
