From 9205423626163e1ace324867545cb947f2d4d4b0 Mon Sep 17 00:00:00 2001 From: leiyun Date: Tue, 4 Aug 2026 15:27:39 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=8F=91=E5=B8=83=E8=84=9A?= =?UTF-8?q?=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/components/LoginModal.vue | 21 ++- app/composables/useApi.js | 11 +- app/composables/useClientDevice.js | 57 ++++++++ app/composables/useIdPhoto.js | 5 +- app/composables/useOldPhoto.js | 5 +- app/composables/usePortrait.js | 5 +- app/composables/usePvcv.js | 44 ++++++ app/composables/useUpload.js | 7 +- app/pages/portrait-photo.vue | 63 +++++++-- app/pages/recharge.vue | 207 ++++++++++++++++++++++++----- app/plugins/pvcv.client.js | 29 ++++ ecosystem.config.cjs | 32 +++++ restart.sh | 89 +++++++++++++ 13 files changed, 519 insertions(+), 56 deletions(-) create mode 100644 app/composables/useClientDevice.js create mode 100644 app/composables/usePvcv.js create mode 100644 app/plugins/pvcv.client.js create mode 100644 ecosystem.config.cjs create mode 100644 restart.sh diff --git a/app/components/LoginModal.vue b/app/components/LoginModal.vue index 892a578..aef1e8f 100644 --- a/app/components/LoginModal.vue +++ b/app/components/LoginModal.vue @@ -33,6 +33,7 @@
-
+
+ +
生成张数
+ +
+
+ 生成张数 + 固定 1 张 +
+

后台已关闭多张生成,本次只生成 1 张。

+
@@ -288,7 +298,7 @@
- {{ job.success_count || 0 }} / {{ job.count || quantity }} 张 · 已等待 {{ waitedSeconds }}s + {{ job.success_count || 0 }} / {{ job.count || effectiveQuantity }} 张 · 已等待 {{ waitedSeconds }}s
下次刷新 {{ pollCountdown }}s
@@ -484,6 +494,7 @@ const activeRatio = ref('') const options = reactive({}) const quantity = ref(1) const maxCount = ref(4) +const quantityEnabled = ref(true) const unitPoints = ref(0) const configLoading = ref(true) const submitting = ref(false) @@ -508,7 +519,12 @@ const currentStyle = computed(() => styles.value.find(item => item.key === activ const currentGroups = computed(() => currentStyle.value.groups || []) const resultUrls = computed(() => job.value.result_urls || []) const activeResultUrl = computed(() => resultUrls.value[activeIndex.value] || resultUrls.value[0] || '') -const totalPoints = computed(() => unitPoints.value * quantity.value) +const effectiveQuantity = computed(() => (quantityEnabled.value ? quantity.value : 1)) +const totalPoints = computed(() => unitPoints.value * effectiveQuantity.value) +const quantityPriceText = computed(() => { + if (!unitPoints.value) return quantityEnabled.value ? `共${totalPoints.value}点` : '固定1张' + return quantityEnabled.value ? `${unitPoints.value}点/张 · 共${totalPoints.value}点` : `${unitPoints.value}点/张 · 固定1张` +}) const currentPoints = computed(() => Number(balance.points || 0)) const isBalanceInsufficient = computed( () => user.isLogin.value && totalPoints.value > 0 && currentPoints.value < totalPoints.value, @@ -530,20 +546,51 @@ function showToast(message, color = 'error') { toast.add({ title: message, color }) } +function pickFirstDefined(values) { + return values.find(value => value !== undefined && value !== null && value !== '') +} + +function normalizeQuantityEnabled(data = {}) { + const raw = pickFirstDefined([ + data.quantity_enabled, + data.quantity_supported, + data.product?.quantity_enabled, + data.billing?.product?.quantity_enabled, + data.billing?.quote?.product?.quantity_enabled, + ]) + if (raw === undefined) return Number(data.max_count || 1) > 1 + if (typeof raw === 'boolean') return raw + return !['0', 'false', 'no', 'off', 'unsupported', 'not_support', '不支持'].includes(String(raw).toLowerCase()) +} + +function applyQuantityConfig(data = {}) { + const enabled = normalizeQuantityEnabled(data) + const rawMax = pickFirstDefined([ + data.max_count, + data.product?.max_quantity, + data.billing?.product?.max_quantity, + data.billing?.quote?.product?.max_quantity, + ]) + const nextMax = enabled ? Math.max(1, Number(rawMax || maxCount.value || 1)) : 1 + quantityEnabled.value = enabled + maxCount.value = nextMax + quantity.value = enabled ? Math.min(Math.max(1, quantity.value), nextMax) : 1 +} + // ==================== 配置 / 示例 / 余额 ==================== async function loadConfig(silent = false) { try { const res = await post('/portrait/config', { style: activeStyle.value || undefined, model: activeModel.value || undefined, - count: quantity.value, + count: effectiveQuantity.value, }) const data = res.data || {} models.value = data.models || [] styles.value = data.styles || [] ratios.value = data.ratios || [] tips.value = data.tips || [] - maxCount.value = Number(data.max_count || 4) + applyQuantityConfig(data) unitPoints.value = Number(data.unit_points || 0) if (!activeModel.value) activeModel.value = data.default_model || models.value[0]?.key || '' if (!activeRatio.value) { @@ -682,7 +729,7 @@ async function handleSubmit() { style: activeStyle.value, model: activeModel.value, ratio: activeRatio.value, - count: quantity.value, + count: effectiveQuantity.value, extra_prompt: extraPrompt.value, request_id: createRequestId(), options: { ...options }, diff --git a/app/pages/recharge.vue b/app/pages/recharge.vue index e3c5a1c..cd4b4a7 100644 --- a/app/pages/recharge.vue +++ b/app/pages/recharge.vue @@ -108,16 +108,16 @@