优化前端,使用组件Element
This commit is contained in:
@@ -0,0 +1,266 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import { useInventory } from '../composables/useInventory'
|
||||
import { handleApiError } from '@/shared/notification'
|
||||
|
||||
const {
|
||||
state,
|
||||
formatCurrency,
|
||||
formatDateTime,
|
||||
formatDate,
|
||||
loadFinance,
|
||||
refreshFinanceByPeriod
|
||||
} = useInventory()
|
||||
|
||||
const periodYear = ref(state.financePeriod.year)
|
||||
const periodQuarter = ref(state.financePeriod.quarter)
|
||||
|
||||
const quarterOptions = [
|
||||
{ label: '全年', value: '' },
|
||||
{ label: 'Q1', value: '1' },
|
||||
{ label: 'Q2', value: '2' },
|
||||
{ label: 'Q3', value: '3' },
|
||||
{ label: 'Q4', value: '4' }
|
||||
]
|
||||
|
||||
const periodLabel = computed(() => {
|
||||
if (!periodQuarter.value) return `${periodYear.value}年 全年`
|
||||
return `${periodYear.value}年 Q${periodQuarter.value}`
|
||||
})
|
||||
|
||||
function refreshFinance() {
|
||||
state.financePeriod.year = periodYear.value
|
||||
state.financePeriod.quarter = periodQuarter.value
|
||||
loadFinance()
|
||||
}
|
||||
|
||||
const totalReceivable = computed(() => {
|
||||
return state.financeSummary?.total_receivable ?? 0
|
||||
})
|
||||
|
||||
const totalPayable = computed(() => {
|
||||
return state.financeSummary?.total_payable ?? 0
|
||||
})
|
||||
|
||||
const periodReceived = computed(() => {
|
||||
return state.financeSummary?.period_received ?? 0
|
||||
})
|
||||
|
||||
const periodPaid = computed(() => {
|
||||
return state.financeSummary?.period_paid ?? 0
|
||||
})
|
||||
|
||||
function getTransactionTypeLabel(type: string): string {
|
||||
return type === 'receipt' ? '收款' : '付款'
|
||||
}
|
||||
|
||||
function getTransactionTypeTag(type: string): string {
|
||||
return type === 'receipt' ? 'success' : 'warning'
|
||||
}
|
||||
|
||||
function getTransactionStatusLabel(status: string): string {
|
||||
const map: Record<string, string> = {
|
||||
confirmed: '已确认',
|
||||
pending: '待确认'
|
||||
}
|
||||
return map[status] || status
|
||||
}
|
||||
|
||||
refreshFinance()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div style="margin-bottom: 16px; display: flex; gap: 12px; align-items: center; flex-wrap: wrap;">
|
||||
<span style="font-weight: 600;">统计周期:</span>
|
||||
<el-input-number
|
||||
v-model="periodYear"
|
||||
:min="2020"
|
||||
:max="2099"
|
||||
style="width: 120px;"
|
||||
/>
|
||||
<span>年</span>
|
||||
<el-select v-model="periodQuarter" placeholder="选择季度" style="width: 120px;">
|
||||
<el-option
|
||||
v-for="q in quarterOptions"
|
||||
:key="q.value"
|
||||
:label="q.label"
|
||||
:value="q.value"
|
||||
/>
|
||||
</el-select>
|
||||
<el-button type="primary" @click="refreshFinance">刷新统计</el-button>
|
||||
<el-tag type="info" size="large">{{ periodLabel }}</el-tag>
|
||||
</div>
|
||||
|
||||
<el-row :gutter="16" style="margin-bottom: 16px;">
|
||||
<el-col :span="6">
|
||||
<el-card shadow="hover">
|
||||
<div style="text-align: center;">
|
||||
<div style="color: #909399; font-size: 14px; margin-bottom: 8px;">应收总额</div>
|
||||
<div style="font-size: 24px; font-weight: 700; color: #409eff;">
|
||||
{{ formatCurrency(totalReceivable) }}
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card shadow="hover">
|
||||
<div style="text-align: center;">
|
||||
<div style="color: #909399; font-size: 14px; margin-bottom: 8px;">应付总额</div>
|
||||
<div style="font-size: 24px; font-weight: 700; color: #e6a23c;">
|
||||
{{ formatCurrency(totalPayable) }}
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card shadow="hover">
|
||||
<div style="text-align: center;">
|
||||
<div style="color: #909399; font-size: 14px; margin-bottom: 8px;">周期收款</div>
|
||||
<div style="font-size: 24px; font-weight: 700; color: #67c23a;">
|
||||
{{ formatCurrency(periodReceived) }}
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card shadow="hover">
|
||||
<div style="text-align: center;">
|
||||
<div style="color: #909399; font-size: 14px; margin-bottom: 8px;">周期付款</div>
|
||||
<div style="font-size: 24px; font-weight: 700; color: #f56c6c;">
|
||||
{{ formatCurrency(periodPaid) }}
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-card style="margin-bottom: 16px;">
|
||||
<template #header>
|
||||
<span style="font-weight: 600;">客户账款(周期)</span>
|
||||
</template>
|
||||
<el-table :data="state.customerFinanceStatement" v-loading="state.loading" stripe size="small">
|
||||
<el-table-column prop="partner_name" label="客户" />
|
||||
<el-table-column prop="order_count" label="订单数" />
|
||||
<el-table-column prop="transaction_count" label="流水数" />
|
||||
<el-table-column label="订单金额">
|
||||
<template #default="{ row }">{{ formatCurrency(row.order_amount) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="订单已收">
|
||||
<template #default="{ row }">{{ formatCurrency(row.order_received) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="实收流水">
|
||||
<template #default="{ row }">{{ formatCurrency(row.transaction_received) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="应收余额">
|
||||
<template #default="{ row }">{{ formatCurrency(row.receivable_balance) }}</template>
|
||||
</el-table-column>
|
||||
<el-empty v-if="!state.loading && state.customerFinanceStatement.length === 0" description="暂无数据" />
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
<el-card style="margin-bottom: 16px;">
|
||||
<template #header>
|
||||
<span style="font-weight: 600;">供应商账款(周期)</span>
|
||||
</template>
|
||||
<el-table :data="state.supplierFinanceStatement" v-loading="state.loading" stripe size="small">
|
||||
<el-table-column prop="partner_name" label="供应商" />
|
||||
<el-table-column prop="order_count" label="订单数" />
|
||||
<el-table-column prop="transaction_count" label="流水数" />
|
||||
<el-table-column label="订单金额">
|
||||
<template #default="{ row }">{{ formatCurrency(row.order_amount) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="订单已付">
|
||||
<template #default="{ row }">{{ formatCurrency(row.order_paid) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="实付流水">
|
||||
<template #default="{ row }">{{ formatCurrency(row.transaction_paid) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="应付余额">
|
||||
<template #default="{ row }">{{ formatCurrency(row.payable_balance) }}</template>
|
||||
</el-table-column>
|
||||
<el-empty v-if="!state.loading && state.supplierFinanceStatement.length === 0" description="暂无数据" />
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
<el-card style="margin-bottom: 16px;">
|
||||
<template #header>
|
||||
<span style="font-weight: 600;">客户-商品追溯(周期)</span>
|
||||
</template>
|
||||
<el-table :data="state.customerProductStatement" v-loading="state.loading" stripe size="small">
|
||||
<el-table-column prop="partner_name" label="客户" />
|
||||
<el-table-column prop="product_sku" label="SKU" />
|
||||
<el-table-column prop="product_name" label="商品" />
|
||||
<el-table-column prop="order_count" label="订单数" />
|
||||
<el-table-column prop="quantity" label="数量" />
|
||||
<el-table-column label="订单金额">
|
||||
<template #default="{ row }">{{ formatCurrency(row.order_amount) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="已结款">
|
||||
<template #default="{ row }">{{ formatCurrency(row.settled_amount) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="未结款">
|
||||
<template #default="{ row }">{{ formatCurrency(row.unsettled_amount) }}</template>
|
||||
</el-table-column>
|
||||
<el-empty v-if="!state.loading && state.customerProductStatement.length === 0" description="暂无数据" />
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
<el-card style="margin-bottom: 16px;">
|
||||
<template #header>
|
||||
<span style="font-weight: 600;">供应商-商品追溯(周期)</span>
|
||||
</template>
|
||||
<el-table :data="state.supplierProductStatement" v-loading="state.loading" stripe size="small">
|
||||
<el-table-column prop="partner_name" label="供应商" />
|
||||
<el-table-column prop="product_sku" label="SKU" />
|
||||
<el-table-column prop="product_name" label="商品" />
|
||||
<el-table-column prop="order_count" label="订单数" />
|
||||
<el-table-column prop="quantity" label="数量" />
|
||||
<el-table-column label="订单金额">
|
||||
<template #default="{ row }">{{ formatCurrency(row.order_amount) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="已结款">
|
||||
<template #default="{ row }">{{ formatCurrency(row.settled_amount) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="未结款">
|
||||
<template #default="{ row }">{{ formatCurrency(row.unsettled_amount) }}</template>
|
||||
</el-table-column>
|
||||
<el-empty v-if="!state.loading && state.supplierProductStatement.length === 0" description="暂无数据" />
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
<el-card>
|
||||
<template #header>
|
||||
<span style="font-weight: 600;">最近财务流水</span>
|
||||
</template>
|
||||
<el-table :data="state.financeTransactions" v-loading="state.loading" stripe size="small">
|
||||
<el-table-column prop="order_no" label="单号" />
|
||||
<el-table-column label="类型">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="getTransactionTypeTag(row.type)" size="small">
|
||||
{{ getTransactionTypeLabel(row.type) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="partner_name" label="往来方" />
|
||||
<el-table-column label="金额">
|
||||
<template #default="{ row }">{{ formatCurrency(row.amount) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.status === 'confirmed' ? 'success' : 'warning'" size="small">
|
||||
{{ getTransactionStatusLabel(row.status) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="日期">
|
||||
<template #default="{ row }">{{ formatDate(row.transaction_date) }}</template>
|
||||
</el-table-column>
|
||||
<el-empty v-if="!state.loading && state.financeTransactions.length === 0" description="暂无数据" />
|
||||
</el-table>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
@@ -0,0 +1,538 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import { useInventory } from '../composables/useInventory'
|
||||
import { apiRequest } from '@/shared/api'
|
||||
import { addNotification, handleApiError } from '@/shared/notification'
|
||||
|
||||
const {
|
||||
state,
|
||||
formatCurrency,
|
||||
formatDateTime,
|
||||
formatDate,
|
||||
getSalesOrderStatusLabel,
|
||||
loadProductionOrders,
|
||||
loadFinishedProducts,
|
||||
loadCustomers,
|
||||
loadMaterials,
|
||||
loadInventory,
|
||||
loadMovements
|
||||
} = useInventory()
|
||||
|
||||
const showModal = ref(false)
|
||||
const editingItem = ref<any>(null)
|
||||
const form = ref<any>({})
|
||||
const moldItems = ref<any[]>([])
|
||||
const productionWarehouseId = ref<number | null>(null)
|
||||
|
||||
const showConsumptionModal = ref(false)
|
||||
const consumptionItems = ref<any[]>([])
|
||||
const consumedMaterials = ref<any[]>([])
|
||||
|
||||
function openCreateOrder() {
|
||||
editingItem.value = null
|
||||
form.value = {
|
||||
customer_id: null,
|
||||
delivery_date: '',
|
||||
remark: ''
|
||||
}
|
||||
moldItems.value = []
|
||||
consumedMaterials.value = []
|
||||
showModal.value = true
|
||||
}
|
||||
|
||||
function addMoldItem() {
|
||||
moldItems.value.push({
|
||||
mold_mode: 'new',
|
||||
mold_sku: '',
|
||||
mold_name: '',
|
||||
mold_id: null,
|
||||
quantity: 1,
|
||||
unit_price: 0,
|
||||
remark: ''
|
||||
})
|
||||
}
|
||||
|
||||
function removeMoldItem(index: number) {
|
||||
moldItems.value.splice(index, 1)
|
||||
}
|
||||
|
||||
const orderTotalAmount = computed(() => {
|
||||
return moldItems.value.reduce((sum: number, item: any) => {
|
||||
return sum + (item.unit_price || 0) * (item.quantity || 0)
|
||||
}, 0)
|
||||
})
|
||||
|
||||
async function editOrder(order: any) {
|
||||
await loadCustomers()
|
||||
await loadFinishedProducts()
|
||||
await loadMaterials()
|
||||
try {
|
||||
const detail = await apiRequest(`/api/sales-orders/${order.id}`)
|
||||
editingItem.value = order
|
||||
form.value = {
|
||||
customer_id: detail.customer_id || order.customer_id || null,
|
||||
delivery_date: detail.delivery_date || order.delivery_date || '',
|
||||
remark: detail.remark || order.remark || ''
|
||||
}
|
||||
moldItems.value = (detail.items || []).map((item: any) => ({
|
||||
mold_mode: item.mold_id ? 'existing' : 'new',
|
||||
mold_sku: item.sku || '',
|
||||
mold_name: item.name || '',
|
||||
mold_id: item.mold_id || null,
|
||||
quantity: item.quantity || 1,
|
||||
unit_price: item.unit_price || 0,
|
||||
remark: item.remark || ''
|
||||
}))
|
||||
try {
|
||||
const consumptionData = await apiRequest(`/api/sales-orders/${order.id}/consume-materials`)
|
||||
consumedMaterials.value = (consumptionData.items || []).map((item: any) => ({
|
||||
material_id: item.material_id,
|
||||
material_name: item.material_name || item.material_sku || '',
|
||||
quantity: item.quantity,
|
||||
unit_price: item.unit_price || 0,
|
||||
remark: item.remark || ''
|
||||
}))
|
||||
} catch {
|
||||
consumedMaterials.value = []
|
||||
}
|
||||
} catch (e) {
|
||||
handleApiError(e, '加载销售订单详情')
|
||||
}
|
||||
showModal.value = true
|
||||
}
|
||||
|
||||
async function saveSalesOrder() {
|
||||
if (!form.value.customer_id) {
|
||||
addNotification('请选择客户', 'warning')
|
||||
return
|
||||
}
|
||||
if (!moldItems.value || moldItems.value.length === 0) {
|
||||
addNotification('请至少添加一个模具', 'warning')
|
||||
return
|
||||
}
|
||||
try {
|
||||
const payload = {
|
||||
customer_id: form.value.customer_id,
|
||||
delivery_date: form.value.delivery_date || null,
|
||||
remark: form.value.remark || '',
|
||||
items: moldItems.value.map((item: any) => ({
|
||||
mold_mode: item.mold_mode,
|
||||
mold_sku: item.mold_sku || '',
|
||||
mold_name: item.mold_name || '',
|
||||
mold_id: item.mold_id || null,
|
||||
quantity: item.quantity,
|
||||
unit_price: item.unit_price || 0,
|
||||
remark: item.remark || ''
|
||||
}))
|
||||
}
|
||||
if (editingItem.value) {
|
||||
await apiRequest(`/api/sales-orders/${editingItem.value.id}`, {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify(payload)
|
||||
})
|
||||
addNotification('销售订单更新成功', 'success')
|
||||
} else {
|
||||
await apiRequest('/api/sales-orders', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(payload)
|
||||
})
|
||||
addNotification('销售订单创建成功', 'success')
|
||||
}
|
||||
showModal.value = false
|
||||
editingItem.value = null
|
||||
form.value = {}
|
||||
moldItems.value = []
|
||||
consumedMaterials.value = []
|
||||
loadProductionOrders()
|
||||
loadFinishedProducts()
|
||||
loadInventory()
|
||||
loadMovements()
|
||||
} catch (e) {
|
||||
handleApiError(e, '保存销售订单')
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteOrder(id: number) {
|
||||
if (!confirm('确定要删除这个销售订单吗?')) return
|
||||
try {
|
||||
await apiRequest(`/api/sales-orders/${id}`, { method: 'DELETE' })
|
||||
addNotification('销售订单已删除', 'success')
|
||||
loadProductionOrders()
|
||||
loadInventory()
|
||||
loadMovements()
|
||||
} catch (e) {
|
||||
handleApiError(e, '删除销售订单')
|
||||
}
|
||||
}
|
||||
|
||||
async function updateStatus(orderId: number, status: string) {
|
||||
const label = status === 'delivered' ? '已交付' : '已收款'
|
||||
if (!confirm(`确认标记为${label}?`)) return
|
||||
try {
|
||||
await apiRequest(`/api/sales-orders/${orderId}/status`, {
|
||||
method: 'PATCH',
|
||||
body: JSON.stringify({ status })
|
||||
})
|
||||
addNotification(`已标记为${label}`, 'success')
|
||||
loadProductionOrders()
|
||||
loadMovements()
|
||||
} catch (e) {
|
||||
handleApiError(e, '更新状态')
|
||||
}
|
||||
}
|
||||
|
||||
function openConsumptionModal() {
|
||||
consumptionItems.value = []
|
||||
showConsumptionModal.value = true
|
||||
}
|
||||
|
||||
function addConsumptionItem() {
|
||||
consumptionItems.value.push({
|
||||
material_id: state.materials[0]?.id || null,
|
||||
quantity: 1,
|
||||
remark: ''
|
||||
})
|
||||
}
|
||||
|
||||
function removeConsumptionItem(index: number) {
|
||||
consumptionItems.value.splice(index, 1)
|
||||
}
|
||||
|
||||
const consumptionTotalAmount = computed(() => {
|
||||
return consumptionItems.value.reduce((sum: number, item: any) => {
|
||||
const material = state.materials.find((m: any) => m.id === item.material_id)
|
||||
const unitPrice = material?.cost_price || 0
|
||||
return sum + unitPrice * (item.quantity || 0)
|
||||
}, 0)
|
||||
})
|
||||
|
||||
async function saveConsumption() {
|
||||
if (!editingItem.value) return
|
||||
if (!consumptionItems.value || consumptionItems.value.length === 0) {
|
||||
addNotification('请至少添加一个消耗物料', 'warning')
|
||||
return
|
||||
}
|
||||
try {
|
||||
const payload = {
|
||||
items: consumptionItems.value.map((item: any) => ({
|
||||
material_id: item.material_id,
|
||||
quantity: item.quantity,
|
||||
unit_price: state.materials.find((m: any) => m.id === item.material_id)?.cost_price || 0,
|
||||
remark: item.remark || ''
|
||||
}))
|
||||
}
|
||||
await apiRequest(`/api/sales-orders/${editingItem.value.id}/consume-materials`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(payload)
|
||||
})
|
||||
addNotification('物料消耗记录保存成功', 'success')
|
||||
showConsumptionModal.value = false
|
||||
try {
|
||||
const consumptionData = await apiRequest(`/api/sales-orders/${editingItem.value.id}/consume-materials`)
|
||||
consumedMaterials.value = (consumptionData.items || []).map((item: any) => ({
|
||||
material_id: item.material_id,
|
||||
material_name: item.material_name || item.material_sku || '',
|
||||
quantity: item.quantity,
|
||||
unit_price: item.unit_price || 0,
|
||||
remark: item.remark || ''
|
||||
}))
|
||||
} catch {
|
||||
consumedMaterials.value = []
|
||||
}
|
||||
loadInventory()
|
||||
loadMovements()
|
||||
} catch (e) {
|
||||
handleApiError(e, '保存物料消耗')
|
||||
}
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
showModal.value = false
|
||||
editingItem.value = null
|
||||
form.value = {}
|
||||
moldItems.value = []
|
||||
consumedMaterials.value = []
|
||||
}
|
||||
|
||||
function closeConsumptionModal() {
|
||||
showConsumptionModal.value = false
|
||||
consumptionItems.value = []
|
||||
}
|
||||
|
||||
async function initData() {
|
||||
await loadProductionOrders()
|
||||
await loadCustomers()
|
||||
await loadMaterials()
|
||||
await loadFinishedProducts()
|
||||
}
|
||||
|
||||
initData()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div style="margin-bottom: 12px; display: flex; gap: 12px; align-items: center;">
|
||||
<el-select
|
||||
v-model="productionWarehouseId"
|
||||
placeholder="选择仓库"
|
||||
style="width: 200px;"
|
||||
clearable
|
||||
>
|
||||
<el-option
|
||||
v-for="warehouse in state.warehouses"
|
||||
:key="warehouse.id"
|
||||
:label="warehouse.name"
|
||||
:value="warehouse.id"
|
||||
/>
|
||||
</el-select>
|
||||
<el-button @click="loadProductionOrders">刷新</el-button>
|
||||
<el-button type="primary" @click="openCreateOrder">新增销售订单</el-button>
|
||||
</div>
|
||||
|
||||
<el-table :data="state.productionOrders" v-loading="state.loading" stripe>
|
||||
<el-table-column prop="order_no" label="销售单" />
|
||||
<el-table-column prop="customer_name" label="客户" />
|
||||
<el-table-column label="订单金额">
|
||||
<template #default="{ row }">{{ formatCurrency(row.total_amount) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="交付日期">
|
||||
<template #default="{ row }">{{ formatDate(row.delivery_date) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="订单创建">
|
||||
<template #default="{ row }">{{ formatDateTime(row.created_at) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="实际交付">
|
||||
<template #default="{ row }">{{ row.delivered_at ? formatDateTime(row.delivered_at) : '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="实际收款">
|
||||
<template #default="{ row }">{{ row.paid_at ? formatDateTime(row.paid_at) : '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="订单状态">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.status === 'paid' ? 'success' : row.status === 'delivered' ? 'warning' : 'info'">
|
||||
{{ getSalesOrderStatusLabel(row.status) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="240">
|
||||
<template #default="{ row }">
|
||||
<el-dropdown style="margin-right: 8px;">
|
||||
<el-button size="small">
|
||||
状态 ▾
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item @click="updateStatus(row.id, 'delivered')">已交付</el-dropdown-item>
|
||||
<el-dropdown-item @click="updateStatus(row.id, 'paid')">已收款</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
:disabled="row.status === 'paid'"
|
||||
@click="editOrder(row)"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
size="small"
|
||||
@click="deleteOrder(row.id)"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-empty v-if="!state.loading && state.productionOrders.length === 0" description="暂无销售订单" />
|
||||
</el-table>
|
||||
|
||||
<el-dialog
|
||||
v-model="showModal"
|
||||
:title="(editingItem ? '编辑' : '新增') + '销售订单'"
|
||||
width="900px"
|
||||
@closed="closeModal"
|
||||
>
|
||||
<el-form label-width="100px">
|
||||
<el-form-item label="客户" required>
|
||||
<el-select v-model="form.customer_id" placeholder="请选择客户" style="width:100%">
|
||||
<el-option
|
||||
v-for="customer in state.customers"
|
||||
:key="customer.id"
|
||||
:label="customer.name"
|
||||
:value="customer.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="交付日期">
|
||||
<el-date-picker
|
||||
v-model="form.delivery_date"
|
||||
type="date"
|
||||
placeholder="选择日期"
|
||||
style="width:100%"
|
||||
value-format="YYYY-MM-DD"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注">
|
||||
<el-input v-model="form.remark" placeholder="备注信息" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px;">
|
||||
<span style="font-weight: 600;">模具明细</span>
|
||||
<div style="display: flex; gap: 8px;">
|
||||
<el-button v-if="editingItem" type="warning" size="small" @click="openConsumptionModal">+ 消耗物料</el-button>
|
||||
<el-button type="primary" size="small" @click="addMoldItem">+ 添加模具</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<el-table :data="moldItems" border size="small">
|
||||
<el-table-column label="模式" width="80">
|
||||
<template #default="{ row: line }">
|
||||
<el-select v-model="line.mold_mode" size="small" style="width:100%">
|
||||
<el-option label="新模" value="new" />
|
||||
<el-option label="改模" value="existing" />
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="模具SKU" min-width="120">
|
||||
<template #default="{ row: line }">
|
||||
<el-input v-if="line.mold_mode === 'new'" v-model="line.mold_sku" size="small" placeholder="模具SKU" />
|
||||
<el-select v-else v-model="line.mold_id" placeholder="选择模具" size="small" style="width:100%">
|
||||
<el-option
|
||||
v-for="product in state.finishedProducts"
|
||||
:key="product.id"
|
||||
:label="`${product.sku} - ${product.name}`"
|
||||
:value="product.id"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="模具名称" min-width="120">
|
||||
<template #default="{ row: line }">
|
||||
<el-input
|
||||
v-if="line.mold_mode === 'new'"
|
||||
v-model="line.mold_name"
|
||||
size="small"
|
||||
placeholder="模具名称"
|
||||
/>
|
||||
<span v-else style="padding-left: 8px;">
|
||||
{{ state.finishedProducts.find((p: any) => p.id === line.mold_id)?.name || '-' }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="数量" width="100">
|
||||
<template #default="{ row: line }">
|
||||
<el-input-number v-model="line.quantity" :min="1" size="small" style="width:100%" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="单价" width="120">
|
||||
<template #default="{ row: line }">
|
||||
<el-input-number v-model="line.unit_price" :min="0" :precision="2" :step="0.01" size="small" style="width:100%" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" width="120">
|
||||
<template #default="{ row: line }">
|
||||
<el-input v-model="line.remark" size="small" placeholder="备注" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="80">
|
||||
<template #default="{ $index }">
|
||||
<el-button type="danger" size="small" @click="removeMoldItem($index)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div style="text-align: right; margin-top: 8px; font-weight: 600;">
|
||||
订单总金额:{{ formatCurrency(orderTotalAmount) }}
|
||||
</div>
|
||||
|
||||
<template v-if="editingItem && consumedMaterials.length > 0">
|
||||
<el-divider content-position="left">已消耗物料</el-divider>
|
||||
<el-table :data="consumedMaterials" border size="small">
|
||||
<el-table-column label="物料">
|
||||
<template #default="{ row: item }">{{ item.material_name }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="数量">
|
||||
<template #default="{ row: item }">{{ item.quantity }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="单价">
|
||||
<template #default="{ row: item }">{{ formatCurrency(item.unit_price) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注">
|
||||
<template #default="{ row: item }">{{ item.remark || '-' }}</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="showModal = false">取消</el-button>
|
||||
<el-button type="primary" @click="saveSalesOrder">保存</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog
|
||||
v-model="showConsumptionModal"
|
||||
title="物料消耗"
|
||||
width="800px"
|
||||
@closed="closeConsumptionModal"
|
||||
>
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px;">
|
||||
<span style="font-weight: 600;">消耗物料明细</span>
|
||||
<el-button type="primary" size="small" @click="addConsumptionItem">+ 添加物料</el-button>
|
||||
</div>
|
||||
<el-table :data="consumptionItems" border size="small">
|
||||
<el-table-column label="物料" min-width="200">
|
||||
<template #default="{ row: line }">
|
||||
<el-select v-model="line.material_id" placeholder="请选择物料" style="width:100%">
|
||||
<el-option
|
||||
v-for="material in state.materials"
|
||||
:key="material.id"
|
||||
:label="`${material.sku} - ${material.name}`"
|
||||
:value="material.id"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="数量" width="120">
|
||||
<template #default="{ row: line }">
|
||||
<el-input-number v-model="line.quantity" :min="0.0001" :step="0.0001" size="small" style="width:100%" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="单价" width="120">
|
||||
<template #default="{ row: line }">
|
||||
<el-input-number
|
||||
:model-value="state.materials.find((m: any) => m.id === line.material_id)?.cost_price || 0"
|
||||
disabled
|
||||
size="small"
|
||||
style="width:100%"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="总价" width="120">
|
||||
<template #default="{ row: line }">
|
||||
{{ formatCurrency((state.materials.find((m: any) => m.id === line.material_id)?.cost_price || 0) * (line.quantity || 0)) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" width="150">
|
||||
<template #default="{ row: line }">
|
||||
<el-input v-model="line.remark" size="small" placeholder="备注" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="80">
|
||||
<template #default="{ $index }">
|
||||
<el-button type="danger" size="small" @click="removeConsumptionItem($index)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div style="text-align: right; margin-top: 8px; font-weight: 600;">
|
||||
消耗总金额:{{ formatCurrency(consumptionTotalAmount) }}
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button @click="showConsumptionModal = false">取消</el-button>
|
||||
<el-button type="primary" @click="saveConsumption">保存消耗</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
@@ -1,4 +1,4 @@
|
||||
import { reactive, ref, computed, onMounted, onUnmounted, nextTick, watch } from 'vue'
|
||||
import { reactive, ref, computed } from 'vue'
|
||||
import { apiRequest } from '@/shared/api'
|
||||
import { addNotification, handleApiError } from '@/shared/notification'
|
||||
import { formatCurrency, formatNumber, formatDateTime, formatDate } from '@/shared/utils'
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
import{E as e,_ as t,t as n,y as r}from"./index-C_7kTAur.js";var i={};function a(n,i){return e(),r(`div`,null,[...i[0]||=[t(`div`,{class:`page-header`},[t(`h1`,{class:`page-title`},`设计体系`),t(`p`,{class:`page-subtitle`},`迁移中...`)],-1)]])}var o=n(i,[[`render`,a]]);export{o as default};
|
||||
import{D as e,g as t,t as n,y as r}from"./index-DLhcHKuU.js";var i={};function a(n,i){return e(),r(`div`,null,[...i[0]||=[t(`div`,{class:`page-header`},[t(`h1`,{class:`page-title`},`设计体系`),t(`p`,{class:`page-subtitle`},`迁移中...`)],-1)]])}var o=n(i,[[`render`,a]]);export{o as default};
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
.inventory-tab[data-v-cbf8e7cf]{padding:0}.table-header[data-v-cbf8e7cf]{margin-bottom:16px}.suppliers-tab[data-v-18d0ade0]{padding:0}.table-header[data-v-18d0ade0]{margin-bottom:16px}.customers-tab[data-v-63db53de]{padding:0}.table-header[data-v-63db53de]{margin-bottom:16px}.movements-tab[data-v-5efede77],.page-container[data-v-cbcdda91]{padding:0}.page-header[data-v-cbcdda91]{margin-bottom:var(--space-6)}.page-header h1[data-v-cbcdda91]{font-size:var(--text-2xl);font-weight:var(--font-bold);color:var(--text-primary);margin:0}.page-header p[data-v-cbcdda91]{color:var(--text-tertiary);margin:var(--space-1) 0 0;font-size:var(--text-sm)}.inventory-layout[data-v-cbcdda91]{gap:var(--space-6);min-height:calc(100vh - 200px);display:flex}.inventory-sidebar-wrapper[data-v-cbcdda91]{flex-shrink:0;width:200px}.inventory-content[data-v-cbcdda91]{flex:1;min-width:0}.inventory-content-header[data-v-cbcdda91]{margin-bottom:var(--space-4)}.inventory-breadcrumb[data-v-cbcdda91]{font-size:var(--text-sm);color:var(--text-tertiary)}.inventory-breadcrumb .sep[data-v-cbcdda91]{margin:0 var(--space-2)}.inventory-breadcrumb .current[data-v-cbcdda91]{color:var(--text-primary);font-weight:var(--font-medium)}.inline-alert[data-v-cbcdda91]{padding:var(--space-4);border-radius:var(--radius-md);margin-bottom:var(--space-4)}.inline-alert-warning[data-v-cbcdda91]{background:var(--warning-bg);border:1px solid var(--warning)}.inline-alert-title[data-v-cbcdda91]{font-weight:var(--font-semibold);margin-bottom:var(--space-1)}.inline-alert-message[data-v-cbcdda91]{font-size:var(--text-sm)}.loading-state[data-v-cbcdda91]{align-items:center;gap:var(--space-3);padding:var(--space-8);color:var(--text-tertiary);justify-content:center;display:flex}.spinner[data-v-cbcdda91]{border:2px solid var(--border-default);border-top-color:var(--primary-500);border-radius:50%;width:20px;height:20px;animation:.8s linear infinite spin-cbcdda91}@keyframes spin-cbcdda91{to{transform:rotate(360deg)}}.inventory-sidebar-wrapper[data-v-cbcdda91] .el-menu{background:0 0;border-right:none}.inventory-sidebar-wrapper[data-v-cbcdda91] .sidebar-group-title{padding:var(--space-3) var(--space-4);font-size:var(--text-xs);font-weight:var(--font-semibold);color:var(--text-tertiary);text-transform:uppercase;cursor:pointer;-webkit-user-select:none;user-select:none;justify-content:space-between;align-items:center;display:flex}.inventory-sidebar-wrapper[data-v-cbcdda91] .el-menu-item{height:36px;line-height:36px;font-size:var(--text-sm);margin:2px var(--space-2);border-radius:var(--radius-md);padding:0 var(--space-4)}.inventory-sidebar-wrapper[data-v-cbcdda91] .el-menu-item.is-active{background:var(--primary-50);color:var(--primary-600);font-weight:var(--font-medium)}.inventory-sidebar-wrapper[data-v-cbcdda91] .el-menu-item:hover{background:var(--bg-tertiary)}
|
||||
@@ -1 +0,0 @@
|
||||
.mb-12[data-v-15993ef5]{margin-bottom:12px}.mb-16[data-v-15993ef5]{margin-bottom:16px}.mt-16[data-v-15993ef5]{margin-top:16px}.mr-8[data-v-15993ef5]{margin-right:8px}.text-secondary[data-v-15993ef5]{color:var(--text-secondary)}.flex-row-center[data-v-15993ef5]{flex-wrap:wrap;align-items:center;gap:12px;display:flex}.w-260[data-v-15993ef5]{width:260px}.w-inline-120[data-v-15993ef5]{width:120px;display:inline-block}.w-inline-140[data-v-15993ef5]{width:140px;display:inline-block}.info-display[data-v-15993ef5]{background:var(--bg-secondary);padding:8px 12px}.form-group-spaced[data-v-15993ef5]{margin-bottom:var(--space-4)}.bom-add-btn[data-v-15993ef5]{margin-bottom:8px}.btn-disabled[data-v-15993ef5]{opacity:.5;cursor:not-allowed}.datetime-field[data-v-15993ef5]{cursor:pointer;position:relative}.air-datetime-input[data-v-15993ef5]{cursor:pointer;background:var(--bg-primary)}.datetime-native[data-v-15993ef5]{opacity:0;cursor:pointer;width:100%;position:absolute;inset:0}.datetime-field-icon[data-v-15993ef5]{pointer-events:none;font-size:1rem;position:absolute;top:50%;right:10px;transform:translateY(-50%)}.required[data-v-15993ef5]{color:var(--error);font-weight:var(--font-bold)}.sales-order-items[data-v-15993ef5]{flex-direction:column;gap:16px;display:flex}.sales-order-item[data-v-15993ef5]{border:1px solid var(--border-default);border-radius:var(--radius-md);overflow:hidden}.sales-order-item-header[data-v-15993ef5]{background:var(--bg-tertiary);border-bottom:1px solid var(--border-default);gap:0;display:flex}.sales-order-item-header .btn[data-v-15993ef5]{color:var(--text-secondary);background:0 0;border:none;border-radius:0}.sales-order-item-header .btn.active[data-v-15993ef5]{background:var(--primary-500);color:#fff}.sales-order-item-content[data-v-15993ef5]{padding:12px 16px}.sales-order-item-row[data-v-15993ef5]{flex-wrap:wrap;align-items:flex-end;gap:12px;display:flex}.sales-order-item-column[data-v-15993ef5]{flex:1;min-width:120px}.sales-order-item-actions[data-v-15993ef5]{align-items:flex-end;padding-bottom:4px;display:flex}.purchase-order-items[data-v-15993ef5]{flex-direction:column;gap:16px;display:flex}.purchase-order-item[data-v-15993ef5]{border:1px solid var(--border-default);border-radius:var(--radius-md);overflow:hidden}.purchase-order-item-content[data-v-15993ef5]{padding:12px 16px}.purchase-order-item-row[data-v-15993ef5]{flex-wrap:wrap;align-items:flex-end;gap:12px;display:flex}.purchase-order-item-column[data-v-15993ef5]{flex:1;min-width:120px}.purchase-order-item-actions[data-v-15993ef5]{align-items:flex-end;padding-bottom:4px;display:flex}.material-consumption-list[data-v-15993ef5]{border:1px solid var(--border-default);border-radius:var(--radius-md);overflow:hidden}.material-consumption-header[data-v-15993ef5]{background:var(--bg-tertiary);font-weight:var(--font-semibold);border-bottom:1px solid var(--border-default);display:flex}.material-consumption-row[data-v-15993ef5]{border-bottom:1px solid var(--border-light);display:flex}.material-consumption-row[data-v-15993ef5]:last-child{border-bottom:none}.material-consumption-total[data-v-15993ef5]{background:var(--bg-tertiary);font-weight:var(--font-bold);border-top:1px solid var(--border-default);display:flex}.material-consumption-item[data-v-15993ef5]{flex:1;min-width:80px;padding:8px 12px}.bom-section[data-v-15993ef5]{border-top:1px solid var(--border-default);margin-top:12px;padding-top:12px}.form-section[data-v-15993ef5]{margin-bottom:16px}.section-header[data-v-15993ef5]{justify-content:space-between;align-items:center;margin-bottom:12px;display:flex}.restock-modal[data-v-15993ef5]{max-width:900px}.restock-col-material[data-v-15993ef5]{flex:2}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
import{E as e,O as t,P as n,S as r,_ as i,a,f as o,k as s,l as c,m as l,n as u,o as d,t as f,v as p,y as m}from"./index-C_7kTAur.js";var h={class:`login-container`},g={class:`login-card`},_={class:`form-group`},v={class:`form-group`},y={key:0,class:`form-error`},b=[`disabled`],x=f(r({__name:`LoginView`,setup(r){let f=c(),x=s({username:``,password:``,error:``,loading:!1}),S=async()=>{x.error=``,x.loading=!0;try{let e=await d(`/api/auth/login/json`,{method:`POST`,body:JSON.stringify({username:x.username,password:x.password})});a(e.access_token,e.user),u(`登录成功`,`success`),f.push(`/`)}catch(e){x.error=e.message||`登录失败`}finally{x.loading=!1}};return(r,a)=>(e(),m(`div`,h,[i(`div`,g,[a[4]||=i(`div`,{class:`login-header`},[i(`div`,{class:`login-logo`},`G`),i(`h1`,{class:`login-title`},`Gemold`),i(`p`,{class:`login-subtitle`},`模具制造管理系统`)],-1),i(`form`,{onSubmit:l(S,[`prevent`])},[i(`div`,_,[a[2]||=i(`label`,{class:`form-label`},`用户名`,-1),t(i(`input`,{"onUpdate:modelValue":a[0]||=e=>x.username=e,type:`text`,class:`form-input`,placeholder:`请输入用户名`,required:``},null,512),[[o,x.username]])]),i(`div`,v,[a[3]||=i(`label`,{class:`form-label`},`密码`,-1),t(i(`input`,{"onUpdate:modelValue":a[1]||=e=>x.password=e,type:`password`,class:`form-input`,placeholder:`请输入密码`,required:``},null,512),[[o,x.password]])]),x.error?(e(),m(`div`,y,n(x.error),1)):p(``,!0),i(`button`,{type:`submit`,class:`btn btn-primary w-full mt-4`,disabled:x.loading},n(x.loading?`登录中...`:`登录`),9,b)],32)])]))}}),[[`__scopeId`,`data-v-01da5b35`]]);export{x as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{C as e,D as t,N as n,P as r,a as i,f as a,g as o,l as s,n as c,o as l,p as u,t as d,v as f,y as p,z as m}from"./index-DLhcHKuU.js";var h={class:`login-container`},g={class:`login-card`},_={class:`form-group`},v={class:`form-group`},y={key:0,class:`form-error`},b=[`disabled`],x=d(e({__name:`LoginView`,setup(e){let d=s(),x=r({username:``,password:``,error:``,loading:!1}),S=async()=>{x.error=``,x.loading=!0;try{let e=await l(`/api/auth/login/json`,{method:`POST`,body:JSON.stringify({username:x.username,password:x.password})});i(e.access_token,e.user),c(`登录成功`,`success`),d.push(`/`)}catch(e){x.error=e.message||`登录失败`}finally{x.loading=!1}};return(e,r)=>(t(),p(`div`,h,[o(`div`,g,[r[4]||=o(`div`,{class:`login-header`},[o(`div`,{class:`login-logo`},`G`),o(`h1`,{class:`login-title`},`Gemold`),o(`p`,{class:`login-subtitle`},`模具制造管理系统`)],-1),o(`form`,{onSubmit:u(S,[`prevent`])},[o(`div`,_,[r[2]||=o(`label`,{class:`form-label`},`用户名`,-1),n(o(`input`,{"onUpdate:modelValue":r[0]||=e=>x.username=e,type:`text`,class:`form-input`,placeholder:`请输入用户名`,required:``},null,512),[[a,x.username]])]),o(`div`,v,[r[3]||=o(`label`,{class:`form-label`},`密码`,-1),n(o(`input`,{"onUpdate:modelValue":r[1]||=e=>x.password=e,type:`password`,class:`form-input`,placeholder:`请输入密码`,required:``},null,512),[[a,x.password]])]),x.error?(t(),p(`div`,y,m(x.error),1)):f(``,!0),o(`button`,{type:`submit`,class:`btn btn-primary w-full mt-4`,disabled:x.loading},m(x.loading?`登录中...`:`登录`),9,b)],32)])]))}}),[[`__scopeId`,`data-v-01da5b35`]]);export{x as default};
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
import{E as e,_ as t,t as n,y as r}from"./index-C_7kTAur.js";var i={};function a(n,i){return e(),r(`div`,null,[...i[0]||=[t(`div`,{class:`page-header`},[t(`h1`,{class:`page-title`},`灰度发布与回滚`),t(`p`,{class:`page-subtitle`},`迁移中...`)],-1)]])}var o=n(i,[[`render`,a]]);export{o as default};
|
||||
import{D as e,g as t,t as n,y as r}from"./index-DLhcHKuU.js";var i={};function a(n,i){return e(),r(`div`,null,[...i[0]||=[t(`div`,{class:`page-header`},[t(`h1`,{class:`page-title`},`灰度发布与回滚`),t(`p`,{class:`page-subtitle`},`迁移中...`)],-1)]])}var o=n(i,[[`render`,a]]);export{o as default};
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
-1
@@ -61,7 +61,7 @@
|
||||
} catch (e) {}
|
||||
})();
|
||||
</script>
|
||||
<script type="module" crossorigin src="/static/assets/index-C_7kTAur.js"></script>
|
||||
<script type="module" crossorigin src="/static/assets/index-DLhcHKuU.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/static/assets/index-O0fr6NFx.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
Reference in New Issue
Block a user