fix(user): keep the chosen alert product and split convert weight across both wires
Alerts store the selected product instead of falling back to gold, and the convert sliders turn the chosen asset amount into complementary 1.5 and 2.5 rings. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -57,4 +57,8 @@
|
|||||||
- 2026-11-01 | جای واحد و عدد در تبدیل | `CopperConvertCalculator.vue` + `useCopperConvertPage.ts` | ریال/کیلو/عدد با `bdi` داخل `measure` قبل از عدد؛ باکس مقدار `dir=ltr`
|
- 2026-11-01 | جای واحد و عدد در تبدیل | `CopperConvertCalculator.vue` + `useCopperConvertPage.ts` | ریال/کیلو/عدد با `bdi` داخل `measure` قبل از عدد؛ باکس مقدار `dir=ltr`
|
||||||
- 2026-11-01 | ماشینحساب اجرت/سود/مالیات | `components/GoldDayCalculatorModal.vue` | اجرت ساخت مفتول؛ سود مورد انتظار جدا؛ مالیات ارزش افزوده روی (هزینه+اجرت+سود)
|
- 2026-11-01 | ماشینحساب اجرت/سود/مالیات | `components/GoldDayCalculatorModal.vue` | اجرت ساخت مفتول؛ سود مورد انتظار جدا؛ مالیات ارزش افزوده روی (هزینه+اجرت+سود)
|
||||||
- 2026-11-01 | پلیسهولدر هشدار قیمت | user `priceTrigger/components/PriceTriggerForm.vue` + admin `priceTriggers/components/PriceTriggerEditorFormBody.vue` | «آبشده، سکه، نقره و ...» → «کاتد مس، مفتول مس و...»
|
- 2026-11-01 | پلیسهولدر هشدار قیمت | user `priceTrigger/components/PriceTriggerForm.vue` + admin `priceTriggers/components/PriceTriggerEditorFormBody.vue` | «آبشده، سکه، نقره و ...» → «کاتد مس، مفتول مس و...»
|
||||||
|
- 2026-11-02 | هشدار کاتد→طلا | user `priceTrigger/` + `BasicSelect.vue` + server `Customer/PriceTriggerController.php` | شناسه عددی محصول ارسال شود؛ fallback عنوان «طلا» نباشد. محصول پیشفرض const برابر id=1 است
|
||||||
|
- 2026-11-02 | زبانه تبدیل کشیدنی | `convert/components/CopperConvertCalculator.vue` | `dragHandleOnly` در capture؛ لمس ریل مقدار را جابهجا نمیکند، فقط کشیدن دستگیره
|
||||||
|
- 2026-11-02 | سه زبانه تبدیل از اول | همان `CopperConvertCalculator.vue` | زبانه ۱.۵ و ۲.۵ دیگر پشت `remainderAmount > 0` نیستند؛ زیر زبانه مقدار، قبل از نتیجه
|
||||||
|
- 2026-11-02 | زبانه سیم مکمل | `useCopperConvertPage.ts` | بودجه = ارزش مقدار انتخابشده؛ نصف به ۱.۵ و بقیه به ۲.۵؛ جابهجایی یکی دیگری را پر میکند
|
||||||
- 2026-11-01 | تاریخچه برداشت کیف پول | `wallet/mobile/MobileWalletView.vue` + `desktop/DesktopWalletView.vue` + `useWalletPageData.ts` | `walletWithdrawItems` از قبل لود میشد؛ زیر راهنمای برداشت و زیر حسابهای دسکتاپ نشان داده میشود
|
- 2026-11-01 | تاریخچه برداشت کیف پول | `wallet/mobile/MobileWalletView.vue` + `desktop/DesktopWalletView.vue` + `useWalletPageData.ts` | `walletWithdrawItems` از قبل لود میشد؛ زیر راهنمای برداشت و زیر حسابهای دسکتاپ نشان داده میشود
|
||||||
|
|||||||
@@ -23,8 +23,9 @@
|
|||||||
<div class="w-full text-center font-bold">{{ noData }}</div>
|
<div class="w-full text-center font-bold">{{ noData }}</div>
|
||||||
</template>
|
</template>
|
||||||
<a-select-option
|
<a-select-option
|
||||||
:label="val[props.valueName]"
|
|
||||||
v-for="val in props.items"
|
v-for="val in props.items"
|
||||||
|
:key="val[props.keyName]"
|
||||||
|
:label="val[props.valueName]"
|
||||||
:value="val[props.keyName]"
|
:value="val[props.keyName]"
|
||||||
>
|
>
|
||||||
<a-tooltip v-if="props.hasTooltip" placement="left" class="cursor-pointer">
|
<a-tooltip v-if="props.hasTooltip" placement="left" class="cursor-pointer">
|
||||||
|
|||||||
@@ -76,9 +76,8 @@
|
|||||||
</p>
|
</p>
|
||||||
<div
|
<div
|
||||||
class="app-touch-slider copper-convert__slider"
|
class="app-touch-slider copper-convert__slider"
|
||||||
@touchstart.stop
|
@mousedown.capture="dragHandleOnly"
|
||||||
@touchmove.stop
|
@touchstart.capture="dragHandleOnly"
|
||||||
@pointerdown.stop
|
|
||||||
>
|
>
|
||||||
<a-slider
|
<a-slider
|
||||||
v-model:value="sliderAmount"
|
v-model:value="sliderAmount"
|
||||||
@@ -94,46 +93,9 @@
|
|||||||
<span class="copper-convert__measure"><bdi>{{ assetUnit }}</bdi><bdi>{{ formatAssetAmount(assetAmount) }}</bdi></span>
|
<span class="copper-convert__measure"><bdi>{{ assetUnit }}</bdi><bdi>{{ formatAssetAmount(assetAmount) }}</bdi></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
|
||||||
|
|
||||||
<section v-if="result" class="copper-convert__result">
|
|
||||||
<div class="copper-convert__result-head">
|
|
||||||
<span class="copper-convert__result-kicker">خروجی</span>
|
|
||||||
<span class="copper-convert__result-title">نتیجه تبدیل</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="copper-convert__rings">
|
|
||||||
<article class="copper-convert__ring-card">
|
|
||||||
<span class="copper-convert__ring-badge" dir="ltr">۱.۵ mm²</span>
|
|
||||||
<p class="copper-convert__ring-count" dir="ltr">{{ formatRings(result.rings15ByValue) }}</p>
|
|
||||||
<p class="copper-convert__ring-caption">حلقه سیم</p>
|
|
||||||
</article>
|
|
||||||
<article class="copper-convert__ring-card">
|
|
||||||
<span class="copper-convert__ring-badge" dir="ltr">۲.۵ mm²</span>
|
|
||||||
<p class="copper-convert__ring-count" dir="ltr">{{ formatRings(result.rings25ByValue) }}</p>
|
|
||||||
<p class="copper-convert__ring-caption">حلقه سیم</p>
|
|
||||||
</article>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="copper-convert__summary">
|
|
||||||
<div class="copper-convert__summary-row">
|
|
||||||
<span>ارزش معادل</span>
|
|
||||||
<strong class="copper-convert__measure"><small>ریال</small><bdi>{{ formatRial(result.totalValueRial) }}</bdi></strong>
|
|
||||||
</div>
|
|
||||||
<div class="copper-convert__summary-row">
|
|
||||||
<span>معادل مفتول</span>
|
|
||||||
<strong class="copper-convert__measure"><small>کیلو</small><bdi>{{ formatKg(result.equivalentMaftoulKg) }}</bdi></strong>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section v-if="result && remainderAmount > 0" class="copper-convert__split">
|
|
||||||
<div class="copper-convert__result-head">
|
|
||||||
<span class="copper-convert__result-kicker">مانده دارایی</span>
|
|
||||||
<span class="copper-convert__result-title">تقسیم بین دو سیم</span>
|
|
||||||
</div>
|
|
||||||
<p class="copper-convert__asset-hint">
|
<p class="copper-convert__asset-hint">
|
||||||
مانده <span class="copper-convert__measure"><bdi>{{ assetUnit }}</bdi><bdi>{{ formatAssetAmount(remainderAmount) }}</bdi></span> است. هر زبانه فقط عدد صحیح حلقه است؛ اعشاری که به یک حلقه نرسد در دارایی میماند.
|
با این مقدار میتوانید <bdi>{{ wire15Rings }}</bdi> حلقه سیم ۱.۵ و <bdi>{{ wire25Rings }}</bdi> حلقه سیم ۲.۵ ببرید. این دو زبانه مکملاند و با هم روی همین مقدار تنظیم میشوند.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div class="copper-convert__weight">
|
<div class="copper-convert__weight">
|
||||||
@@ -144,9 +106,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="app-touch-slider copper-convert__slider"
|
class="app-touch-slider copper-convert__slider"
|
||||||
@touchstart.stop
|
@mousedown.capture="dragHandleOnly"
|
||||||
@touchmove.stop
|
@touchstart.capture="dragHandleOnly"
|
||||||
@pointerdown.stop
|
|
||||||
>
|
>
|
||||||
<a-slider
|
<a-slider
|
||||||
v-model:value="wire15Rings"
|
v-model:value="wire15Rings"
|
||||||
@@ -171,9 +132,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="app-touch-slider copper-convert__slider"
|
class="app-touch-slider copper-convert__slider"
|
||||||
@touchstart.stop
|
@mousedown.capture="dragHandleOnly"
|
||||||
@touchmove.stop
|
@touchstart.capture="dragHandleOnly"
|
||||||
@pointerdown.stop
|
|
||||||
>
|
>
|
||||||
<a-slider
|
<a-slider
|
||||||
v-model:value="wire25Rings"
|
v-model:value="wire25Rings"
|
||||||
@@ -189,9 +149,42 @@
|
|||||||
<span class="copper-convert__measure"><bdi>عدد حلقه</bdi><bdi>{{ maxRings25 }}</bdi></span>
|
<span class="copper-convert__measure"><bdi>عدد حلقه</bdi><bdi>{{ maxRings25 }}</bdi></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section v-if="result" class="copper-convert__result">
|
||||||
|
<div class="copper-convert__result-head">
|
||||||
|
<span class="copper-convert__result-kicker">خروجی</span>
|
||||||
|
<span class="copper-convert__result-title">نتیجه تبدیل</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="copper-convert__rings">
|
||||||
|
<article class="copper-convert__ring-card">
|
||||||
|
<span class="copper-convert__ring-badge" dir="ltr">۱.۵ mm²</span>
|
||||||
|
<p class="copper-convert__ring-count" dir="ltr">{{ wire15Rings }}</p>
|
||||||
|
<p class="copper-convert__ring-caption">حلقه میتوانید ببرید</p>
|
||||||
|
</article>
|
||||||
|
<article class="copper-convert__ring-card">
|
||||||
|
<span class="copper-convert__ring-badge" dir="ltr">۲.۵ mm²</span>
|
||||||
|
<p class="copper-convert__ring-count" dir="ltr">{{ wire25Rings }}</p>
|
||||||
|
<p class="copper-convert__ring-caption">حلقه میتوانید ببرید</p>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="copper-convert__summary">
|
||||||
|
<div class="copper-convert__summary-row">
|
||||||
|
<span>ارزش معادل</span>
|
||||||
|
<strong class="copper-convert__measure"><small>ریال</small><bdi>{{ formatRial(result.totalValueRial) }}</bdi></strong>
|
||||||
|
</div>
|
||||||
|
<div class="copper-convert__summary-row">
|
||||||
|
<span>معادل مفتول</span>
|
||||||
|
<strong class="copper-convert__measure"><small>کیلو</small><bdi>{{ formatKg(result.equivalentMaftoulKg) }}</bdi></strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="copper-convert__split">
|
||||||
<p class="copper-convert__asset-hint">
|
<p class="copper-convert__asset-hint">
|
||||||
اعشار مانده: <span class="copper-convert__measure"><bdi>{{ assetUnit }}</bdi><bdi>{{ formatAssetAmount(leftoverAmount) }}</bdi></span> نگه داشته میشود.
|
از مقدار انتخابشده، <span class="copper-convert__measure"><bdi>{{ assetUnit }}</bdi><bdi>{{ formatAssetAmount(leftoverAmount) }}</bdi></span> به حلقهٔ کامل نمیرسد و در دارایی میماند. باقی دارایی که روی زبانه انتخاب نشده هم دستنخورده میماند.
|
||||||
</p>
|
</p>
|
||||||
<BasicButton
|
<BasicButton
|
||||||
type="primary"
|
type="primary"
|
||||||
@@ -227,7 +220,6 @@ const {
|
|||||||
assetUnit,
|
assetUnit,
|
||||||
sliderAmount,
|
sliderAmount,
|
||||||
sliderStep,
|
sliderStep,
|
||||||
remainderAmount,
|
|
||||||
maxRings15,
|
maxRings15,
|
||||||
maxRings25,
|
maxRings25,
|
||||||
wire15Rings,
|
wire15Rings,
|
||||||
@@ -241,6 +233,14 @@ const {
|
|||||||
|
|
||||||
const amountAttempted = computed(() => sliderAmount.value > 0)
|
const amountAttempted = computed(() => sliderAmount.value > 0)
|
||||||
|
|
||||||
|
function dragHandleOnly(event: Event) {
|
||||||
|
const target = event.target
|
||||||
|
if (target instanceof Element && target.closest('.ant-slider-handle')) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
event.stopPropagation()
|
||||||
|
}
|
||||||
|
|
||||||
const sourceOptions = computed(() => [
|
const sourceOptions = computed(() => [
|
||||||
{ label: 'کاتد', value: 'katod' as CopperConvertSource },
|
{ label: 'کاتد', value: 'katod' as CopperConvertSource },
|
||||||
{ label: 'مفتول', value: 'maftoul' as CopperConvertSource },
|
{ label: 'مفتول', value: 'maftoul' as CopperConvertSource },
|
||||||
@@ -274,12 +274,6 @@ function formatKg(value: number) {
|
|||||||
if (value >= 10) return value.toFixed(2)
|
if (value >= 10) return value.toFixed(2)
|
||||||
return value.toFixed(3)
|
return value.toFixed(3)
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatRings(value: number) {
|
|
||||||
if (value >= 100) return value.toFixed(1)
|
|
||||||
if (value >= 10) return value.toFixed(2)
|
|
||||||
return value.toFixed(3)
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@@ -182,8 +182,7 @@ export function useCopperConvertPage() {
|
|||||||
|
|
||||||
watch(source, () => {
|
watch(source, () => {
|
||||||
sliderAmount.value = assetAmount.value
|
sliderAmount.value = assetAmount.value
|
||||||
wire15Rings.value = 0
|
applyDefaultSplit()
|
||||||
wire25Rings.value = 0
|
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
@@ -235,37 +234,81 @@ export function useCopperConvertPage() {
|
|||||||
return prices.value.shamshPerKg
|
return prices.value.shamshPerKg
|
||||||
})
|
})
|
||||||
|
|
||||||
const remainderValue = computed(() =>
|
const selectedValue = computed(() => result.value?.totalValueRial ?? 0)
|
||||||
quantityToKg(remainderAmount.value, assetUnit.value, sourceProduct.value) * sourceUnitPrice.value,
|
|
||||||
)
|
|
||||||
|
|
||||||
const maxRings15 = computed(() => {
|
const maxRings15 = computed(() => {
|
||||||
const price = prices.value.wireRing15
|
const price = prices.value.wireRing15
|
||||||
if (price <= 0 || remainderValue.value <= 0) return 0
|
if (price <= 0 || selectedValue.value <= 0) return 0
|
||||||
return Math.floor(remainderValue.value / price)
|
return Math.floor(selectedValue.value / price)
|
||||||
})
|
})
|
||||||
|
|
||||||
const maxRings25 = computed(() => {
|
const maxRings25 = computed(() => {
|
||||||
const price = prices.value.wireRing25
|
const price = prices.value.wireRing25
|
||||||
const left = remainderValue.value - wire15Rings.value * prices.value.wireRing15
|
if (price <= 0 || selectedValue.value <= 0) return 0
|
||||||
if (price <= 0 || left <= 0) return 0
|
return Math.floor(selectedValue.value / price)
|
||||||
return Math.floor(left / price)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
function rings25For(rings15: number) {
|
||||||
|
const price = prices.value.wireRing25
|
||||||
|
const left = selectedValue.value - rings15 * prices.value.wireRing15
|
||||||
|
if (price <= 0 || left <= 0) return 0
|
||||||
|
return Math.min(maxRings25.value, Math.floor(left / price))
|
||||||
|
}
|
||||||
|
|
||||||
|
function rings15For(rings25: number) {
|
||||||
|
const price = prices.value.wireRing15
|
||||||
|
const left = selectedValue.value - rings25 * prices.value.wireRing25
|
||||||
|
if (price <= 0 || left <= 0) return 0
|
||||||
|
return Math.min(maxRings15.value, Math.floor(left / price))
|
||||||
|
}
|
||||||
|
|
||||||
|
function defaultSplit() {
|
||||||
|
const value = selectedValue.value
|
||||||
|
const price15 = prices.value.wireRing15
|
||||||
|
if (value <= 0 || price15 <= 0) return { rings15: 0, rings25: 0 }
|
||||||
|
const rings15 = Math.min(maxRings15.value, Math.floor(value / 2 / price15))
|
||||||
|
return { rings15, rings25: rings25For(rings15) }
|
||||||
|
}
|
||||||
|
|
||||||
|
let splitLock = false
|
||||||
|
|
||||||
|
function writeRings(rings15: number, rings25: number) {
|
||||||
|
splitLock = true
|
||||||
|
wire15Rings.value = rings15
|
||||||
|
wire25Rings.value = rings25
|
||||||
|
splitLock = false
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyDefaultSplit() {
|
||||||
|
const split = defaultSplit()
|
||||||
|
writeRings(split.rings15, split.rings25)
|
||||||
|
}
|
||||||
|
|
||||||
const leftoverAmount = computed(() => {
|
const leftoverAmount = computed(() => {
|
||||||
const spent = wire15Rings.value * prices.value.wireRing15 + wire25Rings.value * prices.value.wireRing25
|
const spent = wire15Rings.value * prices.value.wireRing15 + wire25Rings.value * prices.value.wireRing25
|
||||||
const leftValue = Math.max(0, remainderValue.value - spent)
|
const leftValue = Math.max(0, selectedValue.value - spent)
|
||||||
const perUnit = sourceUnitPrice.value
|
const perUnit = sourceUnitPrice.value
|
||||||
if (perUnit <= 0) return remainderAmount.value
|
if (perUnit <= 0) return 0
|
||||||
return roundAmount(kgToNative(leftValue / perUnit, assetUnit.value, sourceProduct.value))
|
return roundAmount(kgToNative(leftValue / perUnit, assetUnit.value, sourceProduct.value))
|
||||||
})
|
})
|
||||||
|
|
||||||
watch([wire15Rings, maxRings15, maxRings25], () => {
|
watch(sliderAmount, () => applyDefaultSplit(), { flush: 'sync' })
|
||||||
const next15 = Math.max(0, Math.min(maxRings15.value, Math.floor(wire15Rings.value)))
|
|
||||||
if (next15 !== wire15Rings.value) wire15Rings.value = next15
|
watch(selectedValue, (value, prev) => {
|
||||||
const next25 = Math.max(0, Math.min(maxRings25.value, Math.floor(wire25Rings.value)))
|
if ((prev ?? 0) <= 0 && value > 0) applyDefaultSplit()
|
||||||
if (next25 !== wire25Rings.value) wire25Rings.value = next25
|
}, { flush: 'sync' })
|
||||||
})
|
|
||||||
|
watch(wire15Rings, (value) => {
|
||||||
|
if (splitLock) return
|
||||||
|
const next15 = Math.max(0, Math.min(maxRings15.value, Math.floor(value)))
|
||||||
|
writeRings(next15, rings25For(next15))
|
||||||
|
}, { flush: 'sync' })
|
||||||
|
|
||||||
|
watch(wire25Rings, (value) => {
|
||||||
|
if (splitLock) return
|
||||||
|
const next25 = Math.max(0, Math.min(maxRings25.value, Math.floor(value)))
|
||||||
|
writeRings(rings15For(next25), next25)
|
||||||
|
}, { flush: 'sync' })
|
||||||
|
|
||||||
async function submitRequest() {
|
async function submitRequest() {
|
||||||
if (submitting.value) return
|
if (submitting.value) return
|
||||||
|
|||||||
@@ -116,6 +116,7 @@ import BasicInfoItem from '@/components/BasicPacks/BasicInfoItem.vue'
|
|||||||
import DialogVant from '@/components/DialogVant.vue'
|
import DialogVant from '@/components/DialogVant.vue'
|
||||||
import { deletePriceTrigger, updatePriceTrigger } from '@/api/triggerPrice'
|
import { deletePriceTrigger, updatePriceTrigger } from '@/api/triggerPrice'
|
||||||
import { loading } from '@/utils'
|
import { loading } from '@/utils'
|
||||||
|
import { usePriceTriggerPageContext } from '../composables/usePriceTriggerPage'
|
||||||
|
|
||||||
const infoVisible = ref(false)
|
const infoVisible = ref(false)
|
||||||
const isActive = ref(false)
|
const isActive = ref(false)
|
||||||
@@ -132,7 +133,17 @@ const props = withDefaults(
|
|||||||
|
|
||||||
const emits = defineEmits(['edit', 'refresh'])
|
const emits = defineEmits(['edit', 'refresh'])
|
||||||
|
|
||||||
const productTitle = computed(() => props.item.product?.title || 'طلا')
|
const { products } = usePriceTriggerPageContext()
|
||||||
|
|
||||||
|
const productTitle = computed(() => {
|
||||||
|
const nested = props.item.product?.title?.trim()
|
||||||
|
if (nested) {
|
||||||
|
return nested
|
||||||
|
}
|
||||||
|
const id = Number(props.item.product_id ?? props.item.product?.id)
|
||||||
|
const match = products.value.find((product) => Number(product.id) === id)
|
||||||
|
return match?.title?.trim() || '—'
|
||||||
|
})
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
isActive.value = props.item.is_active
|
isActive.value = props.item.is_active
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
<a-form-item :rules="[required()]" label="محصول" name="product_id">
|
<a-form-item :rules="[required()]" label="محصول" name="product_id">
|
||||||
<BasicSelect
|
<BasicSelect
|
||||||
v-model:value="form.product_id"
|
v-model:value="form.product_id"
|
||||||
:items="products"
|
:items="productOptions"
|
||||||
key-name="id"
|
key-name="id"
|
||||||
placeholder="کاتد مس، مفتول مس و..."
|
placeholder="کاتد مس، مفتول مس و..."
|
||||||
size="large"
|
size="large"
|
||||||
@@ -62,7 +62,7 @@ const {
|
|||||||
editId,
|
editId,
|
||||||
loading,
|
loading,
|
||||||
typeItems,
|
typeItems,
|
||||||
products,
|
productOptions,
|
||||||
ok,
|
ok,
|
||||||
} = usePriceTriggerPageContext()
|
} = usePriceTriggerPageContext()
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { inject, onMounted, provide, reactive, ref, type InjectionKey, type Ref } from 'vue'
|
import { computed, inject, onMounted, provide, reactive, ref, type ComputedRef, type InjectionKey, type Ref } from 'vue'
|
||||||
import { cloneDeep } from 'lodash'
|
import { cloneDeep } from 'lodash'
|
||||||
import { type FormInstance, message } from 'ant-design-vue'
|
import { type FormInstance, message } from 'ant-design-vue'
|
||||||
import { createPriceTrigger, getPriceTriggers, updatePriceTrigger } from '@/api/triggerPrice'
|
import { createPriceTrigger, getPriceTriggers, updatePriceTrigger } from '@/api/triggerPrice'
|
||||||
@@ -18,9 +18,24 @@ function emptyForm(): PriceTrigger {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function resolveProductId(value: unknown): number | undefined {
|
||||||
|
if (typeof value === 'number' && Number.isFinite(value) && value > 0) {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
if (typeof value === 'string' && value.trim()) {
|
||||||
|
const id = Number(value)
|
||||||
|
return Number.isFinite(id) && id > 0 ? id : undefined
|
||||||
|
}
|
||||||
|
if (value && typeof value === 'object' && 'id' in value) {
|
||||||
|
return resolveProductId((value as { id?: unknown }).id)
|
||||||
|
}
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
export interface PriceTriggerPageContext {
|
export interface PriceTriggerPageContext {
|
||||||
items: Ref<PriceTrigger[]>
|
items: Ref<PriceTrigger[]>
|
||||||
products: Ref<Product[]>
|
products: Ref<Product[]>
|
||||||
|
productOptions: ComputedRef<Array<{ id: number; title: string }>>
|
||||||
addTriggerVisible: Ref<boolean>
|
addTriggerVisible: Ref<boolean>
|
||||||
editId: Ref<number | undefined>
|
editId: Ref<number | undefined>
|
||||||
form: Ref<PriceTrigger>
|
form: Ref<PriceTrigger>
|
||||||
@@ -41,6 +56,14 @@ export const PRICE_TRIGGER_PAGE_CONTEXT_KEY: InjectionKey<PriceTriggerPageContex
|
|||||||
export function usePriceTriggerPage() {
|
export function usePriceTriggerPage() {
|
||||||
const items = ref<PriceTrigger[]>([])
|
const items = ref<PriceTrigger[]>([])
|
||||||
const products = ref<Product[]>([])
|
const products = ref<Product[]>([])
|
||||||
|
const productOptions = computed(() =>
|
||||||
|
products.value
|
||||||
|
.map((product) => ({
|
||||||
|
id: Number(product.id),
|
||||||
|
title: String(product.title ?? '').trim(),
|
||||||
|
}))
|
||||||
|
.filter((product) => Number.isFinite(product.id) && product.id > 0 && product.title),
|
||||||
|
)
|
||||||
const addTriggerVisible = ref(false)
|
const addTriggerVisible = ref(false)
|
||||||
const editId = ref<number | undefined>(undefined)
|
const editId = ref<number | undefined>(undefined)
|
||||||
const formRef = ref<FormInstance>()
|
const formRef = ref<FormInstance>()
|
||||||
@@ -72,7 +95,7 @@ export function usePriceTriggerPage() {
|
|||||||
function openEdit(val: PriceTrigger) {
|
function openEdit(val: PriceTrigger) {
|
||||||
form.value = cloneDeep({
|
form.value = cloneDeep({
|
||||||
...val,
|
...val,
|
||||||
product_id: val.product_id ?? val.product?.id,
|
product_id: resolveProductId(val.product_id ?? val.product?.id),
|
||||||
})
|
})
|
||||||
editId.value = form.value.id
|
editId.value = form.value.id
|
||||||
addTriggerVisible.value = true
|
addTriggerVisible.value = true
|
||||||
@@ -91,21 +114,30 @@ export function usePriceTriggerPage() {
|
|||||||
|
|
||||||
async function loadProducts() {
|
async function loadProducts() {
|
||||||
try {
|
try {
|
||||||
products.value = ((await getProducts()) as Product[]) ?? []
|
const list = await getProducts()
|
||||||
|
products.value = Array.isArray(list) ? list : []
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e)
|
console.log(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function ok() {
|
async function ok() {
|
||||||
|
const productId = resolveProductId(form.value.product_id)
|
||||||
|
form.value.product_id = productId
|
||||||
await formRef.value?.validateFields().then(async () => {
|
await formRef.value?.validateFields().then(async () => {
|
||||||
try {
|
try {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
form.value.action = 'sms'
|
const payload: PriceTrigger = {
|
||||||
|
product_id: productId,
|
||||||
|
price_condition: form.value.price_condition,
|
||||||
|
is_active: form.value.is_active,
|
||||||
|
action: 'sms',
|
||||||
|
target_price: form.value.target_price,
|
||||||
|
}
|
||||||
if (editId.value) {
|
if (editId.value) {
|
||||||
await updatePriceTrigger(editId.value, form.value)
|
await updatePriceTrigger(editId.value, payload)
|
||||||
} else {
|
} else {
|
||||||
await createPriceTrigger(form.value)
|
await createPriceTrigger(payload)
|
||||||
}
|
}
|
||||||
message.success('هشدار با موفقیت ایجاد شد')
|
message.success('هشدار با موفقیت ایجاد شد')
|
||||||
await initItems()
|
await initItems()
|
||||||
@@ -126,6 +158,7 @@ export function usePriceTriggerPage() {
|
|||||||
const context: PriceTriggerPageContext = {
|
const context: PriceTriggerPageContext = {
|
||||||
items,
|
items,
|
||||||
products,
|
products,
|
||||||
|
productOptions,
|
||||||
addTriggerVisible,
|
addTriggerVisible,
|
||||||
editId,
|
editId,
|
||||||
form,
|
form,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="price-trigger-page-root">
|
<div class="price-trigger-page-root">
|
||||||
<DesktopPriceTriggerView v-show="isDesktop" />
|
<DesktopPriceTriggerView v-if="isDesktop" />
|
||||||
<MobilePriceTriggerView v-show="!isDesktop" />
|
<MobilePriceTriggerView v-else />
|
||||||
|
|
||||||
<BasicActionSheet v-if="!isDesktop" v-model:show="addTriggerVisible" teleport="body">
|
<BasicActionSheet v-if="!isDesktop" v-model:show="addTriggerVisible" teleport="body">
|
||||||
<div class="font-bold text-lg mb-2">هشدار قیمت</div>
|
<div class="font-bold text-lg mb-2">هشدار قیمت</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user