调整备货单 及各页面名称

This commit is contained in:
浅念
2025-09-15 17:14:38 +08:00
parent 5503a14408
commit 5c8df36f12
49 changed files with 1944 additions and 433 deletions
+7 -1
View File
@@ -278,5 +278,11 @@ export class CrmService {
params
})
}
//终极备货单 提交
static stockultimateapproval(params: any) {
return request.post<Api.Common.AddResponse>({
url: '/crm/stock/order/ultimate/approval',
params
})
}
}
+113 -52
View File
@@ -45,6 +45,7 @@ export interface BaseForm {
export interface ItemRow {
sku_id: string
vin?: string
_echo_vin?: boolean
mileage?: number
listing_price?: number | undefined
listing_price_currency: string
@@ -161,10 +162,10 @@ export function useStockingForm(isFinal = false, stockingId?: string | number) {
clearance_port_snapshot: '',
latest_arrival_date: '',
contract_exchange_rate: '',
settlement_currency: 'USD',
settlement_currency: 'CNY',
remark: '',
total_cif: undefined,
total_cif_currency: 'USD',
total_cif_currency: 'CNY',
total_cif_currency_snapshot: '',
is_final: isFinal ? '1' : '0',
object_id: stockingId || '0',
@@ -178,12 +179,13 @@ export function useStockingForm(isFinal = false, stockingId?: string | number) {
const newItem = (): ItemRow => ({
sku_id: '',
model_snapshot: '',
_echo_vin: false,
mileage: 0,
listing_price: undefined,
listing_price_currency: 'USD',
listing_price_currency: 'CNY',
listing_price_currency_snapshot: '',
reduction_price: undefined,
reduction_price_currency: 'USD',
reduction_price_currency: 'CNY',
reduction_price_currency_snapshot: '',
color_interior_code: '',
color_interior_snapshot: '',
@@ -194,38 +196,38 @@ export function useStockingForm(isFinal = false, stockingId?: string | number) {
fob_transport_fee: undefined,
port_misc_fee: '',
ocean_freight: '',
ocean_freight_currency: 'USD',
ocean_freight_currency: 'CNY',
ocean_freight_currency_snapshot: '',
retrofit_fee: '',
total_price: undefined,
official_price: undefined,
discount_price: undefined,
fob_fee: undefined,
fob_fee_currency: 'USD',
fob_fee_currency: 'CNY',
fob_fee_currency_snapshot: '',
helper_fee: undefined,
helper_fee_currency: 'USD',
helper_fee_currency: 'CNY',
helper_fee_currency_snapshot: '',
domestic_freight: undefined,
domestic_freight_currency: 'USD',
domestic_freight_currency: 'CNY',
domestic_freight_currency_snapshot: '',
port_fee: undefined,
port_fee_currency: 'USD',
port_fee_currency: 'CNY',
port_fee_currency_snapshot: '',
unit_price_currency: 'USD',
unit_price_currency: 'CNY',
unit_price_currency_snapshot: '',
total_price_currency: 'USD',
total_price_currency: 'CNY',
total_price_currency_snapshot: '',
guide_price_currency: 'USD',
guide_price_currency: 'CNY',
guide_price_currency_snapshot: '',
discount: undefined,
discount_currency: 'USD',
discount_currency: 'CNY',
discount_currency_snapshot: '',
contract_rate: 7.16,
profit: undefined,
modify_ids: [],
modify_list: [],
modify_fee_currency: 'USD',
modify_fee_currency: 'CNY',
modify_fee_currency_snapshot: ''
})
@@ -234,7 +236,7 @@ export function useStockingForm(isFinal = false, stockingId?: string | number) {
remit_country_id: '',
remit_country_name_snapshot: '',
remit_date: '',
currency_code: 'USD',
currency_code: 'CNY',
currency_name_snapshot: '',
account_id: '',
account_name_snapshot: '',
@@ -280,7 +282,7 @@ export function useStockingForm(isFinal = false, stockingId?: string | number) {
res.forEach((item: any) => {
item.cn_addrs = item.cn_addr + '(' + item.en_addr + ')'
})
console.log(res)
// console.log(res)
countryOptions.value = res || []
}
@@ -390,34 +392,69 @@ export function useStockingForm(isFinal = false, stockingId?: string | number) {
return Number.isNaN(r) ? 0 : r
}
const toUSD = (amount: any, code?: string): number => {
const convertCurrency = (amount: any, fromCode?: string, toCode?: string): number => {
const val = amount === undefined || amount === null || amount === '' ? 0 : Number(amount)
if (Number.isNaN(val)) return 0
const cur = code || 'USD'
const rateCur = getRateNumber(cur)
const rateUSD = getRateNumber('USD') || 1
if (!rateCur) return 0
return (val * rateCur) / rateUSD
const from = fromCode || 'USD'
const to = toCode || 'USD'
if (from === to) return val
const rateFrom = getRateNumber(from)
const rateTo = getRateNumber(to) || 1
if (!rateFrom) return 0
return (val * rateFrom) / rateTo
}
const calcProfit = (it: any): number => {
const unit = toUSD(it.unit_price, it.unit_price_currency)
const retrofit = toUSD(it.retrofit_fee, (it as any).modify_fee_currency)
const fob = toUSD(it.fob_transport_fee ?? (it as any).fob_fee, (it as any).fob_fee_currency)
const handling = toUSD(it.handling_fee ?? (it as any).helper_fee, (it as any).helper_fee_currency)
const port = toUSD(it.port_misc_fee ?? (it as any).port_fee, (it as any).port_fee_currency)
const ocean =toUSD(it.ocean_freight ?? (it as any).ocean_freight, (it as any).ocean_freight_currency)
const targetCurrency = baseForm.settlement_currency || 'USD'
const unit = convertCurrency(it.unit_price, it.unit_price_currency, targetCurrency)
const retrofit = convertCurrency(it.retrofit_fee, (it as any).modify_fee_currency, targetCurrency)
const fob = convertCurrency(
it.fob_transport_fee ?? (it as any).fob_fee,
(it as any).fob_fee_currency,
targetCurrency
)
const handling = convertCurrency(
it.handling_fee ?? (it as any).helper_fee,
(it as any).helper_fee_currency,
targetCurrency
)
const port = convertCurrency(
it.port_misc_fee ?? (it as any).port_fee,
(it as any).port_fee_currency,
targetCurrency
)
const ocean = convertCurrency(
it.ocean_freight ?? (it as any).ocean_freight,
(it as any).ocean_freight_currency,
targetCurrency
)
const mileageVal = Number(it.mileage ?? 0)
if (mileageVal > 0) {
// 二手车:用挂牌价与二手减价替代官方指导价与优惠
const listing = toUSD(it.listing_price ?? (it as any).listing_price, (it as any).listing_price_currency)
const reduction = toUSD(it.reduction_price ?? (it as any).reduction_price, (it as any).reduction_price_currency)
const listing = convertCurrency(
it.listing_price ?? (it as any).listing_price,
(it as any).listing_price_currency,
targetCurrency
)
const reduction = convertCurrency(
it.reduction_price ?? (it as any).reduction_price,
(it as any).reduction_price_currency,
targetCurrency
)
return unit - (retrofit + fob + listing + reduction + handling + port + ocean)
}
// 新车/里程<=0:保持原有计算(官方指导价与优惠)
const guide = toUSD(it.official_price ?? (it as any).guide_price, (it as any).guide_price_currency)
const discount = toUSD(it.discount_price ?? (it as any).discount, (it as any).discount_currency)
const guide = convertCurrency(
it.official_price ?? (it as any).guide_price,
(it as any).guide_price_currency,
targetCurrency
)
const discount = convertCurrency(
it.discount_price ?? (it as any).discount,
(it as any).discount_currency,
targetCurrency
)
return unit - (retrofit + fob + guide + discount + handling + port + ocean)
}
@@ -597,7 +634,7 @@ export function useStockingForm(isFinal = false, stockingId?: string | number) {
baseForm.customer_country_snapshot = res.customer_country_snapshot || ''
baseForm.declarant_name = res.declarant_name || ''
baseForm.eta_ship_date = res.eta_ship_date || ''
baseForm.shipping_method_id = res.shipping_method_id || ''
baseForm.shipping_method_id = res.shipping_method_id ? String(res.shipping_method_id) : ''
baseForm.shipping_method_snapshot = res.shipping_method_snapshot || ''
baseForm.domestic_port_id = res.domestic_port_id || ''
baseForm.domestic_port_snapshot = res.domestic_port_snapshot || ''
@@ -607,24 +644,25 @@ export function useStockingForm(isFinal = false, stockingId?: string | number) {
baseForm.clearance_port_snapshot = res.clearance_port_snapshot || ''
baseForm.latest_arrival_date = res.latest_arrival_date || ''
baseForm.remark = res.remark || ''
baseForm.settlement_currency = res.settlement_currency || 'CNY'
// 合计与汇率
baseForm.total_cif = res.cif_total_amount ? Number(res.cif_total_amount) : undefined
baseForm.total_cif_currency = res.settlement_currency || baseForm.total_cif_currency
baseForm.contract_exchange_rate = res.contract_exchange_rate || getCurrencyRate(baseForm.total_cif_currency)
baseForm.contract_exchange_rate =
res.contract_exchange_rate || getCurrencyRate(baseForm.total_cif_currency)
// 明细
const list = Array.isArray(res.veh_list) ? res.veh_list : []
items.value = list.map((it: any) => ({
sku_id: it.sku_id ? String(it.sku_id) : '',
vin: it.vin || '',
_echo_vin: Boolean(it.vin),
mileage: it.mileage ?? 0,
model_snapshot: it.model_snapshot || '',
listing_price: it.listing_price?.amount ? Number(it.listing_price.amount) : undefined,
listing_price_currency: it.listing_price?.currency || 'USD',
listing_price_currency: it.listing_price?.currency || 'CNY',
listing_price_currency_snapshot: '',
reduction_price: it.reduction_price?.amount ? Number(it.reduction_price.amount) : undefined,
reduction_price_currency: it.reduction_price?.currency || 'USD',
reduction_price_currency: it.reduction_price?.currency || 'CNY',
reduction_price_currency_snapshot: '',
color_interior_code: it.color_interior_code || '',
color_interior_snapshot: it.color_interior_snapshot || '',
@@ -635,32 +673,32 @@ export function useStockingForm(isFinal = false, stockingId?: string | number) {
fob_transport_fee: it.fob_transport_fee?.amount ? Number(it.fob_transport_fee.amount) : undefined,
port_misc_fee: it.port_misc_fee?.amount ?? '',
ocean_freight: it.ocean_freight?.amount ?? '',
ocean_freight_currency: it.ocean_freight?.currency || 'USD',
ocean_freight_currency: it.ocean_freight?.currency || 'CNY',
ocean_freight_currency_snapshot: '',
retrofit_fee: it.retrofit_fee?.amount ?? '',
total_price: it.total_price?.amount ? Number(it.total_price.amount) : undefined,
official_price: it.official_price?.amount ? Number(it.official_price.amount) : undefined,
discount_price: it.discount_price?.amount ? Number(it.discount_price.amount) : undefined,
fob_fee: undefined,
fob_fee_currency: it.fob_transport_fee?.currency || 'USD',
fob_fee_currency: it.fob_transport_fee?.currency || 'CNY',
fob_fee_currency_snapshot: '',
helper_fee: undefined,
helper_fee_currency: it.handling_fee?.currency || 'USD',
helper_fee_currency: it.handling_fee?.currency || 'CNY',
helper_fee_currency_snapshot: '',
domestic_freight: undefined,
domestic_freight_currency: it.domestic_freight?.currency || 'USD',
domestic_freight_currency: it.domestic_freight?.currency || 'CNY',
domestic_freight_currency_snapshot: '',
port_fee: undefined,
port_fee_currency: it.port_misc_fee?.currency || 'USD',
port_fee_currency: it.port_misc_fee?.currency || 'CNY',
port_fee_currency_snapshot: '',
unit_price_currency: it.unit_price?.currency || 'USD',
unit_price_currency: it.unit_price?.currency || 'CNY',
unit_price_currency_snapshot: '',
total_price_currency: it.total_price?.currency || 'USD',
total_price_currency: it.total_price?.currency || 'CNY',
total_price_currency_snapshot: '',
guide_price_currency: it.official_price?.currency || 'USD',
guide_price_currency: it.official_price?.currency || 'CNY',
guide_price_currency_snapshot: '',
discount: undefined,
discount_currency: it.discount_price?.currency || 'USD',
discount_currency: it.discount_price?.currency || 'CNY',
discount_currency_snapshot: '',
contract_rate: baseForm.contract_exchange_rate ? Number(baseForm.contract_exchange_rate) : undefined,
profit: undefined,
@@ -669,7 +707,7 @@ export function useStockingForm(isFinal = false, stockingId?: string | number) {
modify_list: Array.isArray(it.modify_list)
? it.modify_list.map((m: any) => ({ option_id: String(m.option_id), option_name_snapshot: m.option_name_snapshot }))
: [],
modify_fee_currency: it.retrofit_fee?.currency || 'USD',
modify_fee_currency: it.retrofit_fee?.currency || 'CNY',
modify_fee_currency_snapshot: ''
}))
// 计划收款
@@ -680,7 +718,7 @@ export function useStockingForm(isFinal = false, stockingId?: string | number) {
remit_date: p.remit_date || '',
currency_code: p.currency_code || '',
currency_name_snapshot: p.currency_name_snapshot || '',
account_id: p.account_id ? String(p.account_id) : '',
account_id: p.account_id ? p.account_id : '',
account_name_snapshot: p.account_name_snapshot || '',
remitter_name: p.remitter_name || '',
amount: p.amount ? Number(p.amount) : undefined
@@ -696,7 +734,16 @@ export function useStockingForm(isFinal = false, stockingId?: string | number) {
}
// 提交方法
const handleSubmit = async (type: number, edit?: string) => {
const handleSubmit = async (type: number, edit?: string, isfinal?: string) => {
// 提交校验:最终提交且为提交动作时(type=1),必须填写 VIN
const isFinalFlag = isfinal === '1' || (isfinal as any) === true
if (isFinalFlag && type === 1) {
const hasEmptyVin = (items.value || []).some((it) => !it.vin || String(it.vin).trim() === '')
if (hasEmptyVin) {
ElMessage.error('存在未填写车架号(VIN)的明细,请补全后再提交')
return
}
}
const mappedItems = items.value.map((it) => ({
sku_id: it.sku_id,
vin: it.vin || '',
@@ -721,7 +768,8 @@ export function useStockingForm(isFinal = false, stockingId?: string | number) {
ocean_freight: wrapMoney(it.ocean_freight, (it as any).ocean_freight_currency, '海运费'),
retrofit_fee: wrapMoney((it as any).retrofit_fee, (it as any).modify_fee_currency, '改装费'),
modify_list: it.modify_list,
total_price: wrapMoney(it.total_price, (it as any).total_price_currency, '总价'),
// total_price: wrapMoney(it.total_price, (it as any).total_price_currency, '总价'),
total_price: wrapMoney(it.unit_price, (it as any).unit_price_currency, '总价'),
official_price: wrapMoney(
(it as any).official_price ?? (it as any).guide_price,
(it as any).guide_price_currency,
@@ -761,7 +809,11 @@ export function useStockingForm(isFinal = false, stockingId?: string | number) {
ElMessage.success(message || '保存成功')
}
setTimeout(() => {
if (isfinal == '1') {
window.location.href = '/crmpc/stocking/final_list'
} else {
window.location.href = '/crmpc/stocking/stocking_list'
}
}, 1000)
} catch (e) {
ElMessage.error('保存失败')
@@ -774,8 +826,15 @@ export function useStockingForm(isFinal = false, stockingId?: string | number) {
(newCurrency) => {
if (newCurrency) {
baseForm.contract_exchange_rate = getCurrencyRate(newCurrency)
if (typeof validateContractRate === 'function') {
validateContractRate()
}
// 结算币种变化时,按新币种重算所有行利润
(items.value || []).forEach((it: any, index: number) => {
const profit = calcProfit(it)
if (items.value[index]) items.value[index].profit = isNaN(profit) ? 0 : Number(profit.toFixed(2))
})
}
}
)
@@ -783,9 +842,11 @@ export function useStockingForm(isFinal = false, stockingId?: string | number) {
() => baseForm.contract_exchange_rate,
(newCurrency) => {
if (newCurrency) {
if (typeof validateContractRate === 'function') {
validateContractRate()
}
}
}
)
watch(
@@ -827,7 +888,7 @@ export function useStockingForm(isFinal = false, stockingId?: string | number) {
])
// 设置默认汇率
baseForm.contract_exchange_rate = getCurrencyRate('USD')
baseForm.contract_exchange_rate = getCurrencyRate('CNY')
// 设置预计发送日默认为当天
const today = new Date()
@@ -319,6 +319,7 @@
import { commonService } from '@/api/commonApi'
import { manageService } from '@/api/adminApi'
import { router } from '@/router'
defineOptions({ name: 'AdminApprovalFlow' })
//审批类型 AWF_STEP_TYPE
const approvalType = ref([])
+1 -1
View File
@@ -319,7 +319,7 @@
import { ElMessage } from 'element-plus'
import { commonService } from '@/api/commonApi'
import { manageService } from '@/api/adminApi'
defineOptions({ name: 'AdminApprovalEdit' })
//审批类型 AWF_STEP_TYPE
const approvalType = ref([])
//角色类型
+1 -1
View File
@@ -106,7 +106,7 @@
import { useAuth } from '@/composables/useAuth'
import { router } from '@/router'
const { hasAuth } = useAuth()
defineOptions({ name: 'VehicleColor' })
defineOptions({ name: 'AdminApprovalList' })
// 响应式数据
const loading = ref(false)
@@ -116,6 +116,7 @@
import { commonService } from '@/api/commonApi'
import OrderDetailContent from '@/components/custom/OrderDetailContent.vue'
import type { OrderData } from '@/composables/useOrderDetailDialog'
defineOptions({ name: 'AdminStockingPage' })
const orderDialog = reactive({
orderData: {} as OrderData
})
@@ -135,6 +135,7 @@
import OrderDetailContent from '@/components/custom/OrderDetailContent.vue'
import type { OrderData } from '@/composables/useOrderDetailDialog'
import { router } from '@/router'
defineOptions({ name: 'AdminStockingAuthDetil' })
const { pdf, frameRef, download } = usePdfPreview()
const route = useRoute()
// const approval_id = route.query.id
@@ -167,6 +167,7 @@
import { ref, reactive, onMounted, watch } from 'vue'
import { useAuth } from '@/composables/useAuth'
import { manageService } from '@/api/adminApi'
defineOptions({ name: 'AdminCustomerAudit' })
const { hasAuth } = useAuth()
const keyword = ref<string>('')
const loading = ref(false)
+1
View File
@@ -257,6 +257,7 @@
import { manageService } from '@/api/adminApi'
import { useAuth } from '@/composables/useAuth'
import emptyimg from '@/assets/img/crm/empty.png'
defineOptions({ name: 'AdminHighseas' })
const TIMELINE_PLACEMENT = 'top'
const activecolor = '#3471ff'
const { hasAuth } = useAuth()
+1
View File
@@ -341,6 +341,7 @@
import { manageService } from '@/api/adminApi'
import { useAuth } from '@/composables/useAuth'
import emptyimg from '@/assets/img/crm/empty.png'
defineOptions({ name: 'AdminCustomer' })
const TIMELINE_PLACEMENT = 'top'
const activecolor = '#3471ff'
const { hasAuth } = useAuth()
+1
View File
@@ -154,6 +154,7 @@
import { ref, reactive, onMounted, watch } from 'vue'
import { manageService } from '@/api/adminApi'
import { useAuth } from '@/composables/useAuth'
defineOptions({ name: 'AdminRecycle' })
const { hasAuth } = useAuth()
const selectValue = ref<string[]>([])
const loading = ref(false)
+1 -1
View File
@@ -182,7 +182,7 @@
import { financeService } from '@/api/financeApi'
import { useAuth } from '@/composables/useAuth'
const { hasAuth } = useAuth()
defineOptions({ name: 'Type ' })
defineOptions({ name: 'TypeCurrency' })
const loading = ref(false)
const dialogVisible = ref(false)
const formRef = ref<FormInstance>()
+1
View File
@@ -349,6 +349,7 @@
import { CrmService } from '@/api/crmApi'
import { manageService } from '@/api/adminApi'
import { useAuth } from '@/composables/useAuth'
defineOptions({ name: 'InquirySystem' })
const { hasAuth } = useAuth()
const loading = ref(false)
const dialogType = ref('add')
+62 -3
View File
@@ -54,7 +54,18 @@
<ElTableColumn label="操作" fixed="right" min-width="120" align="center">
<template #default="scope">
<ElRow :gutter="10" justify="center">
<el-button link type="primary" @click="handleView1(scope.row)">查看终版</el-button>
<el-link
type="warning"
underline="never"
v-if="scope.row.file_url != null && scope.row.file_url != ''"
@click="printfn(scope.row)"
style="margin-right: 8px"
>
<i class="iconfont-sys">&#xe782;</i> 打印预览
</el-link>
<el-link type="primary" underline="never" @click="handleView1(scope.row)">
<i class="iconfont-sys">&#xe76c;</i> 查看终版
</el-link>
</ElRow>
</template>
</ElTableColumn>
@@ -114,6 +125,37 @@
</div>
</template>
</el-dialog>
<!-- PDF 预览弹窗 -->
<el-dialog
v-model="pdf.visible"
title="打印预览"
width="80%"
top="40px"
:close-on-click-modal="false"
>
<div style="max-height: 70vh; display: flex; flex-direction: column; gap: 12px">
<div style="display: flex; justify-content: flex-end; gap: 8px">
<el-button @click="download">下载</el-button>
</div>
<div
v-if="pdf.images.length"
style="overflow: auto; border: 1px solid #eee; border-radius: 6px; padding: 8px"
>
<img
v-for="(img, idx) in pdf.images"
:key="idx"
:src="img"
style="display: block; width: 100%; margin-bottom: 8px; background: #fff"
/>
</div>
<iframe
v-else-if="pdf.blobUrl || pdf.url"
ref="frameRef"
:src="pdf.blobUrl || pdf.url"
style="flex: 1; width: 100%; border: 1px solid #eee; border-radius: 6px;"
/>
</div>
</el-dialog>
</div>
</template>
@@ -124,6 +166,10 @@
import { useAuth } from '@/composables/useAuth'
import OrderDetailContent from '@/components/custom/OrderDetailContent.vue'
import type { OrderData } from '@/composables/useOrderDetailDialog'
import { usePdfPreview } from '@/composables/usePdfPreview'
// PDF 预览/打印(公共)
const { pdf, frameRef, openWithResponse, download, print, preparePreview, prepareImages } = usePdfPreview()
defineOptions({ name: 'AdminFinalListPage' })
// 订单详情弹窗
const orderDialog = reactive({
visible: false,
@@ -132,8 +178,6 @@
})
const { hasAuth } = useAuth()
const loading = ref(false)
const keyword = ref('')
const tableRef = ref()
@@ -183,6 +227,21 @@
pagination.currentPage = page
getList()
}
//打印
const printfn = (row: any) => {
manageService
.printStock({ id: row.id })
.then((res) => {
openWithResponse(res)
// 优先转成图片以适配不带插件的环境
prepareImages()
// 同时保留 blob 预览作为兜底
preparePreview()
})
.catch((err) => {
console.log(err)
})
}
const contractFile = ref<{ name: string; url: string; file_name: string } | null>(null)
const handleView1 = async (row: any) => {
@@ -313,6 +313,7 @@
import { ElMessage } from 'element-plus'
import { usePdfPreview } from '@/composables/usePdfPreview'
const { pdf, frameRef, openWithResponse, download, print, preparePreview } = usePdfPreview()
defineOptions({ name: 'AdminFinalShow' })
const printCurrent = async () => {
const res = await manageService.printStock({ id: stocking_id })
openWithResponse(res)
+1
View File
@@ -308,6 +308,7 @@
import { ElMessage } from 'element-plus'
import { usePdfPreview } from '@/composables/usePdfPreview'
const { pdf, frameRef, openWithResponse, download, print, preparePreview } = usePdfPreview()
defineOptions({ name: 'AdminStockingDetail' })
const printCurrent = async () => {
const res = await manageService.printStock({ id: stocking_id })
openWithResponse(res)
@@ -58,6 +58,7 @@
<el-link
type="warning"
underline="never"
v-if="scope.row.file_url != null && scope.row.file_url != ''"
@click="printfn(scope.row)"
style="margin-right: 8px"
>
@@ -174,11 +175,11 @@
</template>
<script setup lang="ts">
import { router } from '@/router'
import { manageService } from '@/api/adminApi'
import { usePdfPreview } from '@/composables/usePdfPreview'
import OrderDetailContent from '@/components/custom/OrderDetailContent.vue'
import type { OrderData } from '@/composables/useOrderDetailDialog'
defineOptions({ name: 'StockingListAdmin' })
// import { useAuth } from '@/composables/useAuth'
// const { hasAuth } = useAuth()
// 订单详情弹窗
+1 -1
View File
@@ -93,7 +93,7 @@
import { systemService } from '@/api/systemApi'
import { useAuth } from '@/composables/useAuth'
const { hasAuth } = useAuth()
defineOptions({ name: 'Type ' })
defineOptions({ name: 'Company' })
const loading = ref(false)
const dialogVisible = ref(false)
const formRef = ref<FormInstance>()
+3 -1
View File
@@ -19,7 +19,9 @@
@keyup.enter="getTypeList"
placeholder="请输入关键字"
/>
<ElButton style="margin-left: 8px;min-width: 70px;" @click="getTypeList" v-ripple>搜索</ElButton>
<ElButton style="margin-left: 8px; min-width: 70px" @click="getTypeList" v-ripple
>搜索</ElButton
>
<ElButton
type="primary"
style="margin-left: 8px"
+1 -1
View File
@@ -148,7 +148,7 @@
import { UserService } from '@/api/usersApi'
import { useAuth } from '@/composables/useAuth'
const { hasAuth } = useAuth()
defineOptions({ name: 'Type ' })
defineOptions({ name: 'FeishuApp' })
const loading = ref(false)
const dialogVisible = ref(false)
const formRef = ref<FormInstance>()
+1 -1
View File
@@ -119,7 +119,7 @@
import { manageService } from '@/api/adminApi'
import { useAuth } from '@/composables/useAuth'
const { hasAuth } = useAuth()
defineOptions({ name: 'Type ' })
defineOptions({ name: 'AdminNotice' })
const loading = ref(false)
const dialogVisible = ref(false)
const dialogVisiblexq = ref(false)
+1 -1
View File
@@ -155,7 +155,7 @@
import { systemService } from '@/api/systemApi'
import { useAuth } from '@/composables/useAuth'
const { hasAuth } = useAuth()
defineOptions({ name: 'Type ' })
defineOptions({ name: 'Systems' })
const loading = ref(false)
const dialogVisible = ref(false)
const dialogType = ref<'add' | 'edit'>('add')
+1
View File
@@ -410,6 +410,7 @@
import { systemService } from '@/api/systemApi'
import { formatMenuTitle } from '@/router/utils/utils'
import { UserService } from '@/api/usersApi'
defineOptions({ name: 'UserList' })
const { hasAuth } = useAuth()
const activeName = ref('1')
const selectValue = ref('1')
+1 -1
View File
@@ -266,7 +266,7 @@
import QrcodeVue from 'qrcode.vue'
const { uploadHeaders } = useUploadHeaders()
let actionImageUplode = `${import.meta.env.VITE_API_URL}/ticket/upload`
defineOptions({ name: 'UserCenter' })
defineOptions({ name: 'AdminUserCenter' })
// 定义客户详情类型
interface CustomerInfo {
[key: string]: any
+1 -1
View File
@@ -273,7 +273,7 @@
const { uploadHeaders } = useUploadHeaders()
let actionImageUplode = `${import.meta.env.VITE_API_URL}/ticket/upload`
const { hasAuth } = useAuth()
defineOptions({ name: 'VehicleBrand' })
defineOptions({ name: 'AdminVehicleBrand' })
const { width } = useWindowSize()
const isMobile = computed(() => width.value < 500)
const logourl = ref('')
+1 -1
View File
@@ -141,7 +141,7 @@
import { useAuth } from '@/composables/useAuth'
const { hasAuth } = useAuth()
defineOptions({ name: 'VehicleColor' })
defineOptions({ name: 'AdminVehicleColor' })
// 响应式数据
const loading = ref(false)
+1 -1
View File
@@ -126,7 +126,7 @@
import { useAuth } from '@/composables/useAuth'
const { hasAuth } = useAuth()
defineOptions({ name: 'VehicleModification' })
defineOptions({ name: 'AdminVehicleModification' })
// 响应式数据
const loading = ref(false)
+1 -1
View File
@@ -139,7 +139,7 @@
import { commonService } from '@/api/commonApi'
import { useAuth } from '@/composables/useAuth'
const { hasAuth } = useAuth()
defineOptions({ name: 'VehiclePort' })
defineOptions({ name: 'AdminVehiclePort' })
const loading = ref(false)
const dialogVisible = ref(false)
const dialogType = ref<'add' | 'edit'>('add')
+1 -1
View File
@@ -860,7 +860,7 @@
import { Search } from '@element-plus/icons-vue'
import { vehicleService } from '@/api/vehicleApi'
import { commonService } from '@/api/commonApi'
defineOptions({ name: 'AdminVehicleList' })
// 搜索表单
const searchForm = reactive({
keyword: ''
+1 -1
View File
@@ -136,7 +136,7 @@
import OrderDetailContent from '@/components/custom/OrderDetailContent.vue'
import type { OrderData } from '@/composables/useOrderDetailDialog'
defineOptions({ name: 'ApprovalDetail' }) // 定义组件名称,用于 KeepAlive 缓存控制
defineOptions({ name: 'CrmApprovalDetail' }) // 定义组件名称,用于 KeepAlive 缓存控制
const { pdf, frameRef, download } = usePdfPreview()
const route = useRoute()
+1 -1
View File
@@ -117,7 +117,7 @@
import OrderDetailContent from '@/components/custom/OrderDetailContent.vue'
import type { OrderData } from '@/composables/useOrderDetailDialog'
defineOptions({ name: 'StockingAuth' }) // 定义组件名称,用于 KeepAlive 缓存控制
defineOptions({ name: 'CrmStockingAuth' }) // 定义组件名称,用于 KeepAlive 缓存控制
const { hasAuth } = useAuth()
const orderDialog = reactive({
+1
View File
@@ -79,6 +79,7 @@
import dayjs from 'dayjs'
import { ElMessageBox } from 'element-plus'
import { CrmService } from '@/api/crmApi'
defineOptions({ name: 'CrmHighseas' })
const loading = ref(false)
const pagination = reactive({
currentPage: 1,
+1 -1
View File
@@ -7,7 +7,7 @@
import CardList from './widget/CardList.vue'
import { useCommon } from '@/composables/useCommon'
defineOptions({ name: 'Console' })
defineOptions({ name: 'CrmHome' })
useCommon().scrollToTop()
</script>
+1
View File
@@ -38,6 +38,7 @@
import { ref, onMounted } from 'vue'
import { ElMessageBox } from 'element-plus'
import { CrmService } from '@/api/crmApi'
defineOptions({ name: 'RecyclePage' })
const loading = ref(false)
const pagination = reactive({
currentPage: 1,
+21 -7
View File
@@ -392,7 +392,7 @@
<ElInput v-model="item.unit_price_currency_snapshot" type="hidden" />
</ElFormItem>
</ElCol>
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
<!-- <ElCol :sm="12" :md="12" :lg="6" :xl="4">
<ElFormItem label="总价" label-width="85px">
<ElInput v-model="item.total_price" placeholder="请输入总价">
<template #suffix>
@@ -408,7 +408,7 @@
</ElInput>
<ElInput v-model="item.total_price_currency_snapshot" type="hidden" />
</ElFormItem>
</ElCol>
</ElCol> -->
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
<ElFormItem label="利润" label-width="85px">
<ElInput
@@ -417,15 +417,29 @@
:class="Number(item.profit) < 0 ? 'profit-negative' : ''"
disabled
>
<template #suffix> <span style="font-size: 11px">美元</span></template>
<template #suffix>
<span style="font-size: 11px">
{{
((currencyOptions || []).find((c: any) => c.currency_code === baseForm.settlement_currency)?.currency_name) ||
baseForm.settlement_currency
}}
</span>
</template>
</ElInput>
</ElFormItem>
</ElCol>
</ElRow>
<ElRow :gutter="12">
<ElCol :span="24" style="text-align: right">
<el-tag effect="light" type="success" @click="copyRow(idx)" class="row-actions"> 复制 </el-tag>
<el-tag effect="light" @click="addRowAfter(idx)" v-if="idx == items.length - 1" class="row-actions">
<el-tag effect="light" type="success" @click="copyRow(idx)" class="row-actions">
复制
</el-tag>
<el-tag
effect="light"
@click="addRowAfter(idx)"
v-if="idx == items.length - 1"
class="row-actions"
>
新增
</el-tag>
<el-tag type="danger" effect="light" @click="removeRow(idx)" v-if="items.length > 1">
@@ -458,6 +472,7 @@
<template #suffix>
<ElSelect v-model="baseForm.settlement_currency" style="width: 64px">
<ElOption
@change="(val) =>(baseForm.contract_exchange_rate =(currencyOptions || []).find((x: any) => x.currency_code === val)?.exchange_rate || '') "
v-for="c in currencyOptions"
:key="c.currency_code"
:label="c.currency_name"
@@ -852,14 +867,13 @@
</template>
<script setup lang="ts">
import { ElMessage } from 'element-plus'
import { Search } from '@element-plus/icons-vue'
import { useUserStore } from '@/store/modules/user'
import { CrmService } from '@/api/crmApi'
import { useStockingForm } from '@/composables/useStockingForm'
import { onMounted } from 'vue'
defineOptions({ name: 'StockingAdd' }) // 定义组件名称,用于 KeepAlive 缓存控制
defineOptions({ name: 'CrmStockingAdd' }) // 定义组件名称,用于 KeepAlive 缓存控制
const { getUserInfo } = useUserStore()
const route = useRoute()
+44 -33
View File
@@ -393,7 +393,7 @@
<ElInput v-model="item.unit_price_currency_snapshot" type="hidden" />
</ElFormItem>
</ElCol>
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
<!-- <ElCol :sm="12" :md="12" :lg="6" :xl="4">
<ElFormItem label="总价" label-width="85px">
<ElInput v-model="item.total_price" placeholder="请输入总价">
<template #suffix>
@@ -409,7 +409,7 @@
</ElInput>
<ElInput v-model="item.total_price_currency_snapshot" type="hidden" />
</ElFormItem>
</ElCol>
</ElCol> -->
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
<ElFormItem label="利润" label-width="85px">
<ElInput
@@ -418,7 +418,14 @@
:class="Number(item.profit) < 0 ? 'profit-negative' : ''"
disabled
>
<template #suffix> <span style="font-size: 11px">美元</span></template>
<template #suffix>
<span style="font-size: 11px">
{{
((currencyOptions || []).find((c: any) => c.currency_code === baseForm.settlement_currency)?.currency_name) ||
baseForm.settlement_currency
}}
</span>
</template>
</ElInput>
</ElFormItem>
</ElCol>
@@ -440,6 +447,7 @@
:key="c.currency_code"
:label="c.currency_name"
:value="c.currency_code"
@change="(val) =>(baseForm.contract_exchange_rate =(currencyOptions || []).find((x: any) => x.currency_code === val)?.exchange_rate || '') "
/>
</ElSelect>
</template>
@@ -514,14 +522,15 @@
@change="
(val) =>
(baseForm.shipping_method_snapshot =
(transportOptions || []).find((x: any) => x.id === val)?.item_label || '')
(transportOptions || []).find((x: any) => x.item_value === val)?.item_label ||
'')
"
>
<ElOption
v-for="t in transportOptions"
:key="t.id"
:key="t.item_value"
:label="t.item_label"
:value="t.id"
:value="t.item_value"
/>
</ElSelect>
<ElInput v-model="baseForm.shipping_method_snapshot" type="hidden" />
@@ -601,11 +610,17 @@
</ElCol>
<ElCol :sm="12" :md="12" :lg="4" :xl="4">
<ElFormItem label="汇款国" label-width="55px">
<el-select disabled v-model="payment.remit_country_id" filterable placeholder="请选择汇款国" @change="
<el-select
disabled
v-model="payment.remit_country_id"
filterable
placeholder="请选择汇款国"
@change="
(val) =>
(payment.remit_country_name_snapshot =
(countryOptions || []).find((x: any) => x.id === val)?.cn_addrs || '')
">
"
>
<el-option
v-for="item in countryOptions"
:key="item.id"
@@ -631,11 +646,17 @@
</ElCol>
<ElCol :sm="12" :md="12" :lg="5" :xl="5">
<ElFormItem label="账户" label-width="55px">
<ElSelect disabled v-model="payment.account_name_snapshot" placeholder="请选择账户" @change="
<ElSelect
disabled
v-model="payment.account_name_snapshot"
placeholder="请选择账户"
@change="
(val) =>
(payment.account_id =
(accountOptions || []).find((x: any) => x.account_number === val)?.id || '')
">
(accountOptions || []).find((x: any) => x.account_number === val)?.id ||
'')
"
>
<ElOption
v-for="item in accountOptions"
:key="item.id"
@@ -701,7 +722,7 @@
accept=".jpg,.jpeg,.png,.gif,.pdf,.doc,.docx,.xls,.xlsx,.txt"
>
<div class="qrcode-upload-box">
<input type="hidden" v-model="baseForm.contract"/>
<input type="hidden" v-model="baseForm.contract" />
<img v-if="!contractFile" class="avatar" src="@/assets/img/crm/contract.png" />
<div v-else class="file-preview">
<img v-if="isImageFile(contractFile)" :src="contractFile.url" class="preview-image" />
@@ -719,6 +740,9 @@
<div class="form-card">
<!-- 底部信息 -->
<div class="submit-wrap">
<ElButton type="primary" plain size="large" class="submit-btn" @click="handleSubmit(0)"
>保存</ElButton
>
<ElButton type="primary" size="large" class="submit-btn" @click="handleSubmit(1)"
>提交</ElButton
>
@@ -825,14 +849,13 @@
</template>
<script setup lang="ts">
import { ElMessage } from 'element-plus'
import { Search } from '@element-plus/icons-vue'
import { CrmService } from '@/api/crmApi'
import { useUserStore } from '@/store/modules/user'
import { useStockingForm } from '@/composables/useStockingForm'
import { onMounted } from 'vue'
defineOptions({ name: 'StockingFinalAdd' }) // 定义组件名称,用于 KeepAlive 缓存控制
defineOptions({ name: 'CrmStockingFinalAdd' }) // 定义组件名称,用于 KeepAlive 缓存控制
const route = useRoute()
const stocking_id = route.query.id
@@ -852,49 +875,37 @@
modificationOptions,
currencyOptions,
accountOptions,
customerOptions,
portOptions,
portOptionsall,
wcolorOptions,
ncolorOptions,
transportOptions,
trade_termsOptions,
// 上传相关
actionImageUplode,
uploadHeaders,
// 方法
initializeForm,
populateFromDetail,
handleSubmit,
getCurrencyRate,
validateContractRate,
wrapMoney,
calcProfit,
// 合同文件方法
isImageFile,
beforeContractUpload,
handleContractUpload,
removeContractFile,
// 订单项方法
copyRow,
addRowAfter,
removeRow,
// 收款项方法
addPaymentItem,
removePaymentItem,
// // 订单项方法
// copyRow,
// addRowAfter,
// removeRow,
// // 收款项方法
// addPaymentItem,
// removePaymentItem,
// 客户选择方法
openCustomerDialog,
getCustomerList,
handleCustomerSizeChange,
handleCustomerCurrentChange,
chooseCustomer,
// 车辆选择方法
openVehicleDialog,
getVehicleList,
File diff suppressed because it is too large Load Diff
+35 -5
View File
@@ -53,7 +53,7 @@
<div v-else></div>
</template>
</ElTableColumn>
<ElTableColumn label="操作" fixed="right" min-width="120" align="center">
<ElTableColumn label="操作" fixed="right" min-width="160" align="center">
<template #default="scope">
<el-link
v-if="scope.row.file_url != null && scope.row.file_url != ''"
@@ -64,6 +64,24 @@
>
<i class="iconfont-sys">&#xe782;</i> 打印预览
</el-link>
<el-link
v-if="scope.row.approval_status == 0"
type="primary"
underline="never"
@click="handleEdit(scope.row)"
style="margin-right: 8px"
>
<i class="iconfont-sys">&#xe6bf;</i> 编辑
</el-link>
<el-link
v-if="scope.row.approval_status == 0"
type="success"
underline="never"
@click="handleapproval(scope.row)"
style="margin-right: 8px"
>
<i class="iconfont-sys">&#xe641;</i> 提交
</el-link>
<el-link type="primary" underline="never" @click="handleView1(scope.row)">
<i class="iconfont-sys">&#xe76c;</i> 查看终版
</el-link>
@@ -166,6 +184,7 @@
import { usePdfPreview } from '@/composables/usePdfPreview'
import OrderDetailContent from '@/components/custom/OrderDetailContent.vue'
import type { OrderData } from '@/composables/useOrderDetailDialog'
defineOptions({ name: 'CrmFinalList' })
// 订单详情弹窗
const orderDialog = reactive({
visible: false,
@@ -223,10 +242,21 @@
pagination.currentPage = page
getList()
}
const handleCreate = () => {
// TODO: 跳转到创建备货单页面
router.push('/crmpc/stocking/Add')
const handleEdit = (row: any) => {
router.push({
path: '/crmpc/stocking/final_edit',
query: { id: row.id } // 可带客户名等参数
})
}
const handleapproval = (row: any) => {
CrmService.stockultimateapproval({ id: row.id })
.then((res) => {
ElMessage.success(res?.message || '修改成功')
getList()
})
.catch((err) => {
console.log(err)
})
}
// PDF 预览/打印(公共)
const { pdf, frameRef, openWithResponse, download, preparePreview, prepareImages } =
+10 -3
View File
@@ -290,9 +290,15 @@
</div>
</div>
<!-- PDF 预览弹窗 -->
<el-dialog v-model="pdf.visible" title="打印预览" width="80%" top="40px" :close-on-click-modal="false">
<el-dialog
v-model="pdf.visible"
title="打印预览"
width="80%"
top="40px"
:close-on-click-modal="false"
>
<div style="height: 70vh; display: flex; flex-direction: column; gap: 12px;">
<div style="display:flex; justify-content:flex-end; gap: 8px;">
<div style="display: flex; justify-content: flex-end; gap: 8px">
<!-- <el-button type="primary" @click="print()">打印</el-button> -->
<el-button @click="download">下载</el-button>
</div>
@@ -300,7 +306,7 @@
v-if="pdf.blobUrl || pdf.url"
ref="frameRef"
:src="pdf.blobUrl || pdf.url"
style="flex:1; width: 100%; border: 1px solid #eee; border-radius: 6px;"
style="flex: 1; width: 100%; border: 1px solid #eee; border-radius: 6px"
/>
</div>
</el-dialog>
@@ -313,6 +319,7 @@
import { ElMessage } from 'element-plus'
import { usePdfPreview } from '@/composables/usePdfPreview'
const { pdf, frameRef, openWithResponse, download, print, preparePreview } = usePdfPreview()
defineOptions({ name: 'CrmStockingDetail' })
const printCurrent = async () => {
const res = await CrmService.printStock({ id: stocking_id })
openWithResponse(res)
+10 -3
View File
@@ -285,9 +285,15 @@
</div>
</div>
<!-- PDF 预览弹窗 -->
<el-dialog v-model="pdf.visible" title="打印预览" width="80%" top="40px" :close-on-click-modal="false">
<el-dialog
v-model="pdf.visible"
title="打印预览"
width="80%"
top="40px"
:close-on-click-modal="false"
>
<div style="height: 70vh; display: flex; flex-direction: column; gap: 12px;">
<div style="display:flex; justify-content:flex-end; gap: 8px;">
<div style="display: flex; justify-content: flex-end; gap: 8px">
<!-- <el-button type="primary" @click="print()">打印</el-button> -->
<el-button @click="download">下载</el-button>
</div>
@@ -295,7 +301,7 @@
v-if="pdf.blobUrl || pdf.url"
ref="frameRef"
:src="pdf.blobUrl || pdf.url"
style="flex:1; width: 100%; border: 1px solid #eee; border-radius: 6px;"
style="flex: 1; width: 100%; border: 1px solid #eee; border-radius: 6px"
/>
</div>
</el-dialog>
@@ -308,6 +314,7 @@
import { ElMessage } from 'element-plus'
import { usePdfPreview } from '@/composables/usePdfPreview'
const { pdf, frameRef, openWithResponse, download, print, preparePreview } = usePdfPreview()
defineOptions({ name: 'CrmStockingShow' })
const printCurrent = async () => {
const res = await CrmService.printStock({ id: stocking_id })
openWithResponse(res)
+52 -33
View File
@@ -50,28 +50,6 @@
<ElInput v-model="baseForm.clearance_country_snapshot" type="hidden" />
</ElFormItem>
</ElCol>
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
<ElFormItem label="客户来源" label-width="114px">
<el-select
v-model="baseForm.customer_source_id"
filterable
placeholder="请选择客户渠道"
@change="
(val) =>
(baseForm.customer_source_snapshot =
(channelOptions || []).find((x: any) => x.id === val)?.name || '')
"
>
<el-option
v-for="item in channelOptions"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
<ElInput v-model="baseForm.customer_source_snapshot" type="hidden" />
</ElFormItem>
</ElCol>
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
<ElFormItem label="客户名称" label-width="114px">
<ElInput
@@ -87,25 +65,53 @@
<ElInput v-model="baseForm.customer_id" type="hidden" />
</ElFormItem>
</ElCol>
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
<ElFormItem label="客户来源" label-width="114px">
<el-tooltip class="box-item" content="如需修改客户来源,请先编辑修改客户资料">
<el-select
v-model="baseForm.customer_source_id"
filterable
disabled
placeholder="请选择客户渠道"
@change="
(val) =>
(baseForm.customer_source_snapshot =
(channelOptions || []).find((x: any) => x.id === val)?.name || '')
"
>
<el-option
v-for="item in channelOptions"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-tooltip>
<ElInput v-model="baseForm.customer_source_snapshot" type="hidden" />
</ElFormItem>
</ElCol>
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
<ElFormItem label="客户所在国" label-width="114px">
<el-tooltip class="box-item" content="如需修改客户所在国,请先编辑修改客户资料">
<el-select
v-model="baseForm.customer_country_id"
filterable
placeholder="请选择国家"
disabled
@change="
(val) =>
(baseForm.customer_country_snapshot =
(countryOptions || []).find((x: any) => x.id === val)?.cn_addr || '')
(countryOptions || []).find((x: any) => x.id === val)?.cn_addrs || '')
"
>
<el-option
v-for="item in countryOptions"
:key="item.id"
:label="item.cn_addr"
:label="item.cn_addrs"
:value="item.id"
/>
</el-select>
</el-tooltip>
<ElInput v-model="baseForm.customer_country_snapshot" type="hidden" />
</ElFormItem>
</ElCol>
@@ -120,7 +126,7 @@
</div>
<ElForm class="items-wrap" label-position="right">
<div class="item-row form-box" v-for="(item, idx) in items" :key="idx">
<ElRow :gutter="12">
<ElRow :gutter="12" class="item-row-wrap">
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
<ElFormItem label="车型" label-width="85px">
<ElInput
@@ -205,13 +211,13 @@
</el-select>
</ElFormItem>
</ElCol>
</ElRow>
<ElRow :gutter="12">
<ElCol :sm="12" :md="12" :lg="6" :xl="6">
<ElFormItem label="里程" label-width="85px">
<ElInput v-model="item.mileage" placeholder="请输入里程" />
</ElFormItem>
</ElCol>
</ElRow>
<ElRow :gutter="12" class="item-row-wrap">
<ElCol :sm="12" :md="12" :lg="6" :xl="6" v-if="(item.mileage ?? 0) <= 0">
<ElFormItem label="官方指导价" label-width="85px">
<ElInput v-model="item.official_price" placeholder="请输入官方指导价">
@@ -280,6 +286,8 @@
<ElInput v-model="item.reduction_price_currency_snapshot" type="hidden" />
</ElFormItem>
</ElCol>
</ElRow>
<ElRow :gutter="12" class="item-row-wrap">
<ElCol :sm="12" :md="12" :lg="6" :xl="6">
<ElFormItem label="手续费" label-width="85px">
<ElInput v-model="item.handling_fee" placeholder="请输入手续费">
@@ -365,6 +373,8 @@
<ElInput v-model="item.ocean_freight_currency_snapshot" type="hidden" />
</ElFormItem>
</ElCol>
</ElRow>
<ElRow :gutter="12" class="item-row-wrap">
<ElCol :sm="12" :md="12" :lg="6" :xl="6">
<ElFormItem label="成交价" label-width="85px">
<ElInput v-model="item.unit_price" placeholder="请输入成交价">
@@ -382,7 +392,7 @@
<ElInput v-model="item.unit_price_currency_snapshot" type="hidden" />
</ElFormItem>
</ElCol>
<ElCol :sm="12" :md="12" :lg="6" :xl="6">
<!-- <ElCol :sm="12" :md="12" :lg="6" :xl="6">
<ElFormItem label="总价" label-width="85px">
<ElInput v-model="item.total_price" placeholder="请输入总价">
<template #suffix>
@@ -398,7 +408,7 @@
</ElInput>
<ElInput v-model="item.total_price_currency_snapshot" type="hidden" />
</ElFormItem>
</ElCol>
</ElCol> -->
<ElCol :sm="12" :md="12" :lg="6" :xl="6">
<ElFormItem label="利润" label-width="85px">
<ElInput
@@ -407,7 +417,14 @@
:class="Number(item.profit) < 0 ? 'profit-negative' : ''"
disabled
>
<template #suffix> <span style="font-size: 11px">美元</span></template>
<template #suffix>
<span style="font-size: 11px">
{{
((currencyOptions || []).find((c: any) => c.currency_code === baseForm.settlement_currency)?.currency_name) ||
baseForm.settlement_currency
}}
</span>
</template>
</ElInput>
</ElFormItem>
</ElCol>
@@ -452,6 +469,7 @@
:key="c.currency_code"
:label="c.currency_name"
:value="c.currency_code"
@change="(val) =>(baseForm.contract_exchange_rate =(currencyOptions || []).find((x: any) => x.currency_code === val)?.exchange_rate || '') "
/>
</ElSelect>
</template>
@@ -509,14 +527,14 @@
@change="
(val) =>
(baseForm.shipping_method_snapshot =
(transportOptions || []).find((x: any) => x.id === val)?.item_label || '')
(transportOptions || []).find((x: any) => x.item_value === val)?.item_label || '')
"
>
<ElOption
v-for="t in transportOptions"
:key="t.id"
:key="t.item_value"
:label="t.item_label"
:value="t.id"
:value="t.item_value"
/>
</ElSelect>
<ElInput v-model="baseForm.shipping_method_snapshot" type="hidden" />
@@ -836,6 +854,7 @@
import { useUserStore } from '@/store/modules/user'
import { useStockingForm } from '@/composables/useStockingForm'
import { onMounted } from 'vue'
defineOptions({ name: 'CrmStockingEdit' })
const route = useRoute()
const stocking_id = route.query.id
const { getUserInfo } = useUserStore()
+6 -3
View File
@@ -93,7 +93,10 @@
<i class="iconfont-sys">&#xe76c;</i> 查看
</el-link>
<el-link
v-if="scope.row.is_use == 0 && scope.row.approval_status == '1'"
v-if="
scope.row.is_use == 0 &&
scope.row.approval_status == 1 &&(scope.row.appro_status?scope.row.appro_status.status=='approved':false)
"
type="primary"
underline="never"
@click="handleCreateRepair(scope.row)"
@@ -108,7 +111,7 @@
>
<i class="iconfont-sys">&#xe76c;</i> 查看终版
</el-link>
<el-link type="success" underline="never" @click="copys(scope.row)">
<el-link type="warning" underline="never" @click="copys(scope.row)">
<i class="iconfont-sys">&#xe8b7;</i> 复制
</el-link>
</ElRow>
@@ -216,7 +219,7 @@
// import { useAuth } from '@/composables/useAuth'
// const { hasAuth } = useAuth()
defineOptions({ name: 'StockingList' }) // 定义组件名称,用于 KeepAlive 缓存控制
defineOptions({ name: 'CrmStockingList' }) // 定义组件名称,用于 KeepAlive 缓存控制
const loading = ref(false)
const keyword = ref('')
+1
View File
@@ -100,6 +100,7 @@
import { manageService } from '@/api/adminApi'
const selectValue = ref<string[]>([])
const keyword = ref<string>('')
defineOptions({ name: 'TeamCustomer' })
const { hasAuth } = useAuth()
const loading = ref(false)
const pagination = reactive({
+3
View File
@@ -86,6 +86,9 @@
import { ElImageViewer } from 'element-plus'
import { CrmService } from '@/api/crmApi'
import { useAuth } from '@/composables/useAuth'
defineOptions({
name: 'CustomerPage'
})
const { hasAuth } = useAuth()
const selectValue = ref<string[]>([])
const keyword = ref<string>('')
+6 -5
View File
@@ -107,15 +107,16 @@
<ElForm ref="formRef" :model="form" :rules="rules" label-width="100px" class="form">
<ElFormItem label="颜色编码" prop="color_code">
<ElInput v-model="form.color_code" placeholder="请输入颜色编码" />
<el-text class="mx-1" type="warning">请输入颜色的英文名称</el-text>
</ElFormItem>
<ElFormItem label="颜色名称" prop="color_text">
<ElInput v-model="form.color_text" placeholder="请输入颜色名称" />
</ElFormItem>
<ElFormItem label="类型" prop="type">
<el-select v-model="form.type" placeholder="请选择颜色类型" style="width: 100%">
<el-option label="外观颜色" :value="1" />
<el-option label="内饰颜色" :value="2" />
</el-select>
<el-radio-group v-model="form.type">
<el-radio :label="1">外观颜色</el-radio>
<el-radio :label="2">内饰颜色</el-radio>
</el-radio-group>
</ElFormItem>
<ElFormItem label="排序" prop="sort">
<ElInputNumber v-model="form.sort" :min="0" :max="999" style="width: 100%" />
@@ -141,7 +142,7 @@
import { useAuth } from '@/composables/useAuth'
const { hasAuth } = useAuth()
defineOptions({ name: 'VehicleColor' })
defineOptions({ name: 'CrmVehicleColor' })
// 响应式数据
const loading = ref(false)
+1 -1
View File
@@ -860,7 +860,7 @@
import { Search } from '@element-plus/icons-vue'
import { vehicleService } from '@/api/crmpc/vehicleApi'
import { commonService } from '@/api/commonApi'
defineOptions({ name: 'CrmVehicleList' })
// 搜索表单
const searchForm = reactive({
keyword: ''
+2 -2
View File
@@ -324,8 +324,8 @@
const { uploadHeaders } = useUploadHeaders()
const { info } = useUserStore()
const userid = info.user.id
const { hasAuth } = useAuth();
defineOptions({ name: 'Order' })
const { hasAuth } = useAuth()
defineOptions({ name: 'MyOrder' })
let actionImageUplode = `${import.meta.env.VITE_API_URL}/ticket/upload`
const loading = ref(false)
const editorHtml = ref('') // 编辑器内容
+125 -46
View File
@@ -11,14 +11,22 @@
</ElCol>
</ElRow>
<ArtTable ref="tableRef" row-key="id" :loading="loading" :data="tableData" :currentPage="pagination.currentPage"
:pageSize="pagination.pageSize" :total="pagination.total" @size-change="handleSizeChange"
@current-change="handleCurrentChange">
<ArtTable
ref="tableRef"
row-key="id"
:loading="loading"
:data="tableData"
:currentPage="pagination.currentPage"
:pageSize="pagination.pageSize"
:total="pagination.total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
>
<template #default>
<ElTableColumn label="工单编号" prop="code" />
<ElTableColumn label="问题分类" prop="categorie_id">
<template #default="scope">
<div v-html="scope.row.categorie?scope.row.categorie.name:''"></div>
<div v-html="scope.row.categorie ? scope.row.categorie.name : ''"></div>
</template>
</ElTableColumn>
<ElTableColumn label="问题描述" prop="content" min-width="200px">
@@ -28,9 +36,26 @@
</ElTableColumn>
<ElTableColumn label="状态" prop="status">
<template #default="scope">
<ElTag :type="scope.row.status == 1 ? 'info' : scope.row.status == 2 ?'primary' :scope.row.status == 4 ?'success' : 'danger'">
{{ scope.row.status == 1 ? '待受理' : scope.row.status == 2 ? '处理中' :
scope.row.status == 4 ? "已完成":'关闭' }}
<ElTag
:type="
scope.row.status == 1
? 'info'
: scope.row.status == 2
? 'primary'
: scope.row.status == 4
? 'success'
: 'danger'
"
>
{{
scope.row.status == 1
? '待受理'
: scope.row.status == 2
? '处理中'
: scope.row.status == 4
? '已完成'
: '关闭'
}}
</ElTag>
</template>
</ElTableColumn>
@@ -38,9 +63,23 @@
<ElTableColumn fixed="right" label="操作" max-width="200px">
<template #default="scope">
<ElRow>
<el-button v-if="scope.row.status == 1&&hasAuth('work_order@edit')" link @click="showDialog('edit', scope.row)"> 编辑 </el-button>
<el-button v-if="hasAuth('work_order@status')" link @click="statusEdit(scope.row)"> 状态编辑 </el-button>
<el-button v-if="hasAuth('work_order@conversation_list')" link @click="openChat(scope.row)"> 查看/回复 </el-button>
<el-button
v-if="scope.row.status == 1 && hasAuth('work_order@edit')"
link
@click="showDialog('edit', scope.row)"
>
编辑
</el-button>
<el-button v-if="hasAuth('work_order@status')" link @click="statusEdit(scope.row)">
状态编辑
</el-button>
<el-button
v-if="hasAuth('work_order@conversation_list')"
link
@click="openChat(scope.row)"
>
查看/回复
</el-button>
<!-- <el-button link @click="deleteRole()"> 删除 </el-button> -->
</ElRow>
</template>
@@ -48,11 +87,21 @@
</template>
</ArtTable>
<ElDialog v-model="dialogVisible" :title="dialogType === 'add' ? '新增' : '编辑'" width="60%" align-center>
<ElDialog
v-model="dialogVisible"
:title="dialogType === 'add' ? '新增' : '编辑'"
width="60%"
align-center
>
<ElForm ref="formRef" :model="form" label-width="120px" style="padding-right: 30px">
<ElFormItem label="问题分类" prop="categorie_id">
<ElSelect v-model="form.categorie_id">
<ElOption v-for="type in categorieSelectList" :key="type.id" :value="type.id" :label="type.name" />
<ElOption
v-for="type in categorieSelectList"
:key="type.id"
:value="type.id"
:label="type.name"
/>
</ElSelect>
</ElFormItem>
<ElFormItem label="问题描述" prop="content">
@@ -80,13 +129,35 @@
<div class="attachment-list">
<div v-for="(file, idx) in fileList" :key="file.name" class="attachment-item">
<template v-if="/\.(jpg|jpeg|png|gif|webp|bmp)$/i.test(file.name)">
<img :src="file.file_url?file.file_url:file.response.data.file_url" :alt="file.name" class="attachment-thumb" @click="handleImageClick(imageList.findIndex(url => url === file.file_url?file.file_url:file.response.data.file_url))" />
<img
:src="file.file_url ? file.file_url : file.response.data.file_url"
:alt="file.name"
class="attachment-thumb"
@click="
handleImageClick(
imageList.findIndex((url) =>
url === file.file_url ? file.file_url : file.response.data.file_url
)
)
"
/>
</template>
<template v-else-if="/\.(mp4|webm|ogg)$/i.test(file.name)">
<video :src="file.file_url?file.file_url:file.response.data.file_url" controls class="attachment-thumb" @click.prevent="handleVideoClick(file.file_url?file.file_url:file.response.data.file_url)"></video>
<video
:src="file.file_url ? file.file_url : file.response.data.file_url"
controls
class="attachment-thumb"
@click.prevent="
handleVideoClick(file.file_url ? file.file_url : file.response.data.file_url)
"
></video>
</template>
<template v-else>
<a :href="file.file_url?file.file_url:file.response.data.file_url" target="_blank">{{ file.name }}</a>
<a
:href="file.file_url ? file.file_url : file.response.data.file_url"
target="_blank"
>{{ file.name }}</a
>
</template>
<div class="attachment-filename">{{ file.name }}</div>
</div>
@@ -123,11 +194,7 @@
]"
>
<!-- :src="String(message.user_id) === String(userid) ? meAvatar : aiAvatar" -->
<el-avatar
:size="32"
:src="message.user_avatar"
class="message-avatar"
/>
<el-avatar :size="32" :src="message.user_avatar" class="message-avatar" />
<div class="message-content">
<div class="message-info">
<span class="sender-name">{{ message.user_name }}</span>
@@ -135,9 +202,18 @@
</div>
<div class="message-text" v-html="message.content"></div>
<!-- 附件显示区域 -->
<div class="message-attachments" v-if="message.attachment && message.attachment.length > 0">
<div class="attachment-item" v-for="(attachment, attIndex) in message.attachment" :key="attIndex">
<template v-if="/\.(jpg|jpeg|png|gif|webp|bmp)$/i.test(attachment.original_name)">
<div
class="message-attachments"
v-if="message.attachment && message.attachment.length > 0"
>
<div
class="attachment-item"
v-for="(attachment, attIndex) in message.attachment"
:key="attIndex"
>
<template
v-if="/\.(jpg|jpeg|png|gif|webp|bmp)$/i.test(attachment.original_name)"
>
<img
:src="attachment.storage_path_full"
:alt="attachment.original_name"
@@ -206,12 +282,7 @@
<el-icon class="file-icon"><Document /></el-icon>
</template>
<span class="file-name">{{ file.name }}</span>
<el-button
type="danger"
size="small"
link
@click="removeChatFile(idx)"
>
<el-button type="danger" size="small" link @click="removeChatFile(idx)">
<el-icon><Close /></el-icon>
</el-button>
</div>
@@ -229,7 +300,6 @@
<el-button type="primary" @click="sendMessage" v-ripple>发送</el-button>
</div>
</div>
</div>
</el-drawer>
@@ -237,7 +307,12 @@
<ElForm ref="formRef" :model="statusform" label-width="120px">
<ElFormItem label="状态" prop="status">
<ElSelect v-model="statusform.status">
<ElOption v-for="type in statusList" :key="type.value" :value="type.value" :label="type.text" />
<ElOption
v-for="type in statusList"
:key="type.value"
:value="type.value"
:label="type.text"
/>
</ElSelect>
</ElFormItem>
<ElFormItem label="描述" prop="msg">
@@ -253,10 +328,21 @@
</ElDialog>
<!-- 图片预览弹窗 -->
<ElImageViewer v-if="imageViewerVisible" :url-list="chatImageList.length > 0 ? chatImageList : imageList" :initial-index="imageViewerIndex" @close="closeImageViewer" />
<ElImageViewer
v-if="imageViewerVisible"
:url-list="chatImageList.length > 0 ? chatImageList : imageList"
:initial-index="imageViewerIndex"
@close="closeImageViewer"
/>
<!-- 视频预览弹窗 -->
<ElDialog v-model="videoDialogVisible" width="60%" top="10vh" :show-close="true" append-to-body>
<video v-if="currentVideoUrl" :src="currentVideoUrl" controls autoplay style="width:100%;max-height:70vh;display:block;"></video>
<video
v-if="currentVideoUrl"
:src="currentVideoUrl"
controls
autoplay
style="width: 100%; max-height: 70vh; display: block"
></video>
</ElDialog>
</div>
</template>
@@ -279,7 +365,6 @@
const editorHtml = ref('') // 编辑器内容
const dialogVisible = ref(false)
const dialogVisiblestatus = ref(false)
const typeList = ref<any[]>([])
const formRef = ref<FormInstance>()
const { width } = useWindowSize()
const isMobile = computed(() => width.value < 500)
@@ -288,10 +373,6 @@
const { info } = useUserStore()
const userid = info.user.id
console.log(userid)
import meAvatar from '@/assets/img/avatar/avatar5.webp'
import aiAvatar from '@/assets/img/avatar/avatar10.webp'
// 消息相关数据
const messages = ref<any[]>([])
const messageText = ref('')
@@ -386,8 +467,8 @@
}
//获取工单记录 getconversationList
const ticket_id =ref()
const getConversationList = async (id:any) => {
const ticket_id = ref()
const getConversationList = async (id: any) => {
loading.value = true
try {
const { currentPage, pageSize } = paginationmsg
@@ -412,9 +493,9 @@
const text = messageText.value.trim()
const ids = ticket_id.value
if (!text && chatFileList.value.length === 0) return
// 准备附件数据
const attachments = chatFileList.value.map(file => {
const attachments = chatFileList.value
.map((file) => {
const data = file.response?.data
if (data) {
return {
@@ -423,8 +504,8 @@
}
}
return null
}).filter(Boolean)
})
.filter(Boolean)
const { message } = await WorkService.conversationAdd({
content: text,
ticket_id: ids,
@@ -436,8 +517,6 @@
getConversationList(ids)
scrollToBottom()
}
const dialogType = ref('add')
const showDialog = (type: string, row?: any) => {
+13 -17
View File
@@ -61,14 +61,13 @@
</template>
</ElDialog>
</div>
</template>
<script setup lang="ts">
</template>
<script setup lang="ts">
import { ElMessage, ElMessageBox } from 'element-plus'
import type { FormInstance, FormRules } from 'element-plus'
import { Ordertype} from '@/mock/temp/formData'
import { Ordertype } from '@/mock/temp/formData'
import { WorkService } from '@/api/workApi'
defineOptions({ name: 'Type ' })
defineOptions({ name: 'OrderType ' })
const loading = ref(false)
const dialogVisible = ref(false)
const typeList = ref<any[]>([])
@@ -76,14 +75,12 @@
const { width } = useWindowSize()
const isMobile = computed(() => width.value < 500)
const rules = reactive<FormRules>({
name: [
{ required: true, message: '请输入分类名称', trigger: 'blur' }
]
name: [{ required: true, message: '请输入分类名称', trigger: 'blur' }]
})
const form = reactive<Ordertype>({
name: '',
id:''
id: ''
})
const pagination = reactive({
currentPage: 1,
@@ -141,7 +138,7 @@
type: 'error'
}).then(async () => {
const { message } = await WorkService.Workcategoriedelete({
id: row.id,
id: row.id
})
ElMessage.success(message)
getWorkList()
@@ -150,26 +147,26 @@
const handleSubmit = async (formEl: FormInstance | undefined) => {
if (!formEl) return
if(dialogType.value === 'add'){
if (dialogType.value === 'add') {
const { message } = await WorkService.WorkcategorieAdd({
name: form.name,
name: form.name
})
ElMessage.success(message)
dialogVisible.value = false
getWorkList()
}else{
} else {
const { message } = await WorkService.Workcategorieedit({
name: form.name,
id:form.id
id: form.id
})
ElMessage.success(message)
dialogVisible.value = false
getWorkList()
}
}
</script>
</script>
<style lang="scss" scoped>
<style lang="scss" scoped>
:deep(.el-table__empty-block) {
height: 100% !important;
min-height: 200px;
@@ -187,4 +184,3 @@
}
}
</style>