添加 采集 修改车辆信息页面 备货单页面调整 审核菜单徽标
This commit is contained in:
+17
@@ -152,6 +152,17 @@
|
||||
|
||||
// 监听ws消息,用于系统级通知
|
||||
mittBus.on('ws:message', handleWsMessageNotification)
|
||||
|
||||
// 初始化菜单徽标系统(全局初始化,避免重复)
|
||||
import('@/composables/useMenuBadge').then(({ useMenuBadge }) => {
|
||||
const { initBadgeSystem } = useMenuBadge()
|
||||
initBadgeSystem()
|
||||
})
|
||||
|
||||
// 加载菜单徽标测试工具(仅开发环境)
|
||||
if (import.meta.env.DEV) {
|
||||
import('@/utils/menuBadgeTest')
|
||||
}
|
||||
})
|
||||
|
||||
// 登录后,accessToken 变更时立即初始化 WebSocket(无需刷新)
|
||||
@@ -166,6 +177,12 @@
|
||||
) {
|
||||
initWebSocket(token)
|
||||
}
|
||||
|
||||
// 重新初始化徽标系统(用户重新登录时)
|
||||
import('@/composables/useMenuBadge').then(({ useMenuBadge }) => {
|
||||
const { initBadgeSystem } = useMenuBadge()
|
||||
initBadgeSystem()
|
||||
})
|
||||
} else {
|
||||
// 退出登录时,关闭并重置
|
||||
const { closeWebSocket, resetWebSocketReconnect } = await import('@/utils/sys/wsClient')
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import request from '@/utils/http'
|
||||
|
||||
export class userConService {
|
||||
//分组列表
|
||||
static getGroupList(params: Api.Common.PaginatingParams) {
|
||||
return request.get<Api.Common.CommonListData>({
|
||||
url: '/admin/user/group/list',
|
||||
params
|
||||
})
|
||||
}
|
||||
//添加分组
|
||||
static addGroup(params: any) {
|
||||
return request.post<Api.Common.AddResponse>({
|
||||
url: '/admin/user/group/store',
|
||||
data: params
|
||||
})
|
||||
}
|
||||
//编辑分组
|
||||
static editGroup(params: any) {
|
||||
return request.post<Api.Common.AddResponse>({
|
||||
url: '/admin/user/group/update',
|
||||
data: params
|
||||
})
|
||||
}
|
||||
//删除分组
|
||||
static deleteGroup(params: any) {
|
||||
return request.post<Api.Common.AddResponse>({
|
||||
url: '/admin/user/group/destroy',
|
||||
data: params
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -61,6 +61,13 @@ export class commonService {
|
||||
params
|
||||
})
|
||||
}
|
||||
//车型下拉列表
|
||||
static getModelsSelect(params: any) {
|
||||
return request.get<any>({
|
||||
url: '/crm/vehicle/sku/preset',
|
||||
params
|
||||
})
|
||||
}
|
||||
//港口下拉列表
|
||||
static getPortSelect(params: any) {
|
||||
return request.get<any>({
|
||||
@@ -132,4 +139,17 @@ export class commonService {
|
||||
params
|
||||
})
|
||||
}
|
||||
//人员分组场景
|
||||
static getUserGroupScene() {
|
||||
return request.get<any>({
|
||||
url: '/common/preset/usergroup/scene'
|
||||
})
|
||||
}
|
||||
//人员分组
|
||||
static getUserGroupSelect(params: any) {
|
||||
return request.get<any>({
|
||||
url: '/common/preset/usergroup',
|
||||
params
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -285,4 +285,39 @@ export class CrmService {
|
||||
params
|
||||
})
|
||||
}
|
||||
//备货单撤回
|
||||
static stockrecall(params: any) {
|
||||
return request.post<Api.Common.AddResponse>({
|
||||
url: '/crm/stock/order/revoke/approval',
|
||||
params
|
||||
})
|
||||
}
|
||||
//备货单作废
|
||||
static stockinvalid(params: any) {
|
||||
return request.post<Api.Common.AddResponse>({
|
||||
url: '/crm/stock/order/nullify/approval',
|
||||
params
|
||||
})
|
||||
}
|
||||
//备货单删除
|
||||
static stockdelete(params: any) {
|
||||
return request.post<Api.Common.AddResponse>({
|
||||
url: '/crm/stock/order/destroy',
|
||||
params
|
||||
})
|
||||
}
|
||||
//审核详情
|
||||
static stockapprovaldetailborder(params: any) {
|
||||
return request.get<any>({
|
||||
url: '/crm/approval/records/border',
|
||||
params
|
||||
})
|
||||
}
|
||||
//审批转交
|
||||
static stockapprovaltransfer(params: any) {
|
||||
return request.post<Api.Common.AddResponse>({
|
||||
url: '/crm/approval/records/transfer',
|
||||
params
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,4 +95,127 @@ export class vehicleService {
|
||||
params
|
||||
})
|
||||
}
|
||||
// 港口管理
|
||||
static getPortList(params: Api.Common.PaginatingParams & any) {
|
||||
return request.get<Api.Common.CommonListData>({
|
||||
url: '/common/port/list',
|
||||
params
|
||||
})
|
||||
}
|
||||
static portAdd(params: any) {
|
||||
return request.post<Api.Common.AddResponse>({
|
||||
url: '/common/port/store',
|
||||
params
|
||||
})
|
||||
}
|
||||
static portDetail(params: any) {
|
||||
return request.get<any>({
|
||||
url: '/common/port/show',
|
||||
params
|
||||
})
|
||||
}
|
||||
static portEdit(params: any) {
|
||||
return request.post<Api.Common.AddResponse>({
|
||||
url: '/common/port/update',
|
||||
params
|
||||
})
|
||||
}
|
||||
static portDelete(params: any) {
|
||||
return request.post<Api.Common.AddResponse>({
|
||||
url: '/common/port/destroy',
|
||||
params
|
||||
})
|
||||
}
|
||||
// 车辆品牌列表
|
||||
static getBrandList(params: Api.Common.PaginatingParams & any) {
|
||||
return request.get<Api.Common.CommonListData>({
|
||||
url: '/common/brand/list',
|
||||
params
|
||||
})
|
||||
}
|
||||
// 车辆品牌新增/详情/编辑/删除
|
||||
static brandAdd(params: any) {
|
||||
return request.post<Api.Common.AddResponse>({
|
||||
url: '/common/brand/store',
|
||||
params
|
||||
})
|
||||
}
|
||||
static brandDetail(params: any) {
|
||||
return request.get<any>({
|
||||
url: '/common/brand/show',
|
||||
params
|
||||
})
|
||||
}
|
||||
static brandEdit(params: any) {
|
||||
return request.post<Api.Common.AddResponse>({
|
||||
url: '/common/brand/update',
|
||||
params
|
||||
})
|
||||
}
|
||||
static brandDelete(params: any) {
|
||||
return request.post<Api.Common.AddResponse>({
|
||||
url: '/common/brand/destroy',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// 车辆系列列表
|
||||
static getSeriesList(params: Api.Common.PaginatingParams & any) {
|
||||
return request.get<Api.Common.CommonListData>({
|
||||
url: '/common/brand/series/list',
|
||||
params
|
||||
})
|
||||
}
|
||||
// 车辆系列新增/详情/编辑/删除
|
||||
static seriesAdd(params: any) {
|
||||
return request.post<Api.Common.AddResponse>({
|
||||
url: '/common/brand/series/store',
|
||||
params
|
||||
})
|
||||
}
|
||||
static seriesDetail(params: any) {
|
||||
return request.get<any>({
|
||||
url: '/common/brand/series/show',
|
||||
params
|
||||
})
|
||||
}
|
||||
static seriesEdit(params: any) {
|
||||
return request.post<Api.Common.AddResponse>({
|
||||
url: '/common/brand/series/update',
|
||||
params
|
||||
})
|
||||
}
|
||||
static seriesDelete(params: any) {
|
||||
return request.post<Api.Common.AddResponse>({
|
||||
url: '/common/brand/series/destroy',
|
||||
params
|
||||
})
|
||||
}
|
||||
// 车系采集相关接口
|
||||
static collectVehicleSeries(params: any) {
|
||||
return request.get<any>({
|
||||
url: '/common/brand/crawlSeries',
|
||||
params
|
||||
})
|
||||
}
|
||||
static saveVehicleSeries(params: any) {
|
||||
return request.post<Api.Common.AddResponse>({
|
||||
url: '/common/brand/crawlSeriesCreate',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// 车型采集相关接口
|
||||
static collectVehicleModels(params: any) {
|
||||
return request.get<any>({
|
||||
url: '/common/brand/series/crawlModels',
|
||||
params
|
||||
})
|
||||
}
|
||||
static saveVehicleModels(params: any) {
|
||||
return request.post<Api.Common.AddResponse>({
|
||||
url: '/common/brand/series/crawlModelCreate',
|
||||
params
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,4 +193,32 @@ export class vehicleService {
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// 车系采集相关接口
|
||||
static collectVehicleSeries(params: any) {
|
||||
return request.get<any>({
|
||||
url: '/common/brand/crawlSeries',
|
||||
params
|
||||
})
|
||||
}
|
||||
static saveVehicleSeries(params: any) {
|
||||
return request.post<Api.Common.AddResponse>({
|
||||
url: '/common/brand/crawlSeriesCreate',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// 车型采集相关接口
|
||||
static collectVehicleModels(params: any) {
|
||||
return request.get<any>({
|
||||
url: '/common/brand/series/crawlModels',
|
||||
params
|
||||
})
|
||||
}
|
||||
static saveVehicleModels(params: any) {
|
||||
return request.post<Api.Common.AddResponse>({
|
||||
url: '/common/brand/series/crawlModelCreate',
|
||||
params
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
//
|
||||
'white': #ffffff,
|
||||
'black': #000000,
|
||||
'success': ('base': #13deb9),
|
||||
'success': ('base': #00A43F),
|
||||
'warning': ('base': #ffae1f),
|
||||
'danger': ('base': #ff4d4f),
|
||||
'error': ('base': #fa896b)
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
//
|
||||
'white': #ffffff,
|
||||
'black': #000000,
|
||||
'success': ('base': #13deb9),
|
||||
'success': ('base': #00A43F),
|
||||
'warning': ('base': #ffae1f),
|
||||
'danger': ('base': #ff4d4f),
|
||||
'error': ('base': #fa896b)
|
||||
|
||||
+8
-1
@@ -32,7 +32,10 @@
|
||||
v-html="item.meta.icon"
|
||||
></i>
|
||||
<span>{{ formatMenuTitle(item.meta.title) }}</span>
|
||||
<div class="badge" v-if="item.meta.showBadge"></div>
|
||||
<div v-if="isMenuBadgeVisible(item.path) && !getMenuTextBadge(item.path)" class="badge"></div>
|
||||
<div v-if="getMenuTextBadge(item.path)" class="text-badge">
|
||||
{{ getMenuTextBadge(item.path) }}
|
||||
</div>
|
||||
</el-menu-item>
|
||||
</template>
|
||||
|
||||
@@ -41,6 +44,7 @@
|
||||
import { AppRouteRecord } from '@/types/router'
|
||||
import { handleMenuJump } from '@/utils/navigation'
|
||||
import { formatMenuTitle } from '@/router/utils/utils'
|
||||
import { useMenuBadge } from '@/composables/useMenuBadge'
|
||||
|
||||
const props = defineProps({
|
||||
item: {
|
||||
@@ -60,6 +64,9 @@
|
||||
|
||||
const emit = defineEmits(['close'])
|
||||
|
||||
// 使用菜单徽标系统
|
||||
const { isMenuBadgeVisible, getMenuTextBadge } = useMenuBadge()
|
||||
|
||||
// 计算当前项是否有子菜单
|
||||
const hasChildren = computed(() => {
|
||||
return props.item.children && props.item.children.length > 0
|
||||
|
||||
@@ -112,16 +112,17 @@
|
||||
top: 0;
|
||||
right: 12px;
|
||||
bottom: 0;
|
||||
min-width: 20px;
|
||||
height: 20px;
|
||||
padding: 0 5px;
|
||||
min-width: 16px;
|
||||
height: 16px;
|
||||
// padding: 0 5px;
|
||||
margin: auto;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
font-size: 10px;
|
||||
line-height: 16px;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
background: #fd4e4e;
|
||||
border-radius: 5px;
|
||||
// border-radius: 5px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.header {
|
||||
|
||||
@@ -5,7 +5,10 @@
|
||||
<template #title>
|
||||
<MenuItemIcon :icon="item.meta.icon" :color="theme?.iconColor" />
|
||||
<span class="menu-name">{{ formatMenuTitle(item.meta.title) }}</span>
|
||||
<div v-if="item.meta.showBadge" class="badge" style="right: 35px" />
|
||||
<div v-if="isMenuBadgeVisible(item.path) && !getMenuTextBadge(item.path)" class="badge" style="right: 35px" />
|
||||
<div v-if="getMenuTextBadge(item.path)" class="text-badge" style="right: 35px">
|
||||
{{ getMenuTextBadge(item.path) }}
|
||||
</div>
|
||||
</template>
|
||||
<SidebarSubmenu
|
||||
:list="item.children"
|
||||
@@ -26,9 +29,9 @@
|
||||
<MenuItemIcon :icon="item.meta.icon" :color="theme?.iconColor" />
|
||||
<template #title>
|
||||
<span class="menu-name">{{ formatMenuTitle(item.meta.title) }}</span>
|
||||
<div v-if="item.meta.showBadge" class="badge" />
|
||||
<div v-if="item.meta.showTextBadge" class="text-badge">
|
||||
{{ item.meta.showTextBadge }}
|
||||
<div v-if="isMenuBadgeVisible(item.path) && !getMenuTextBadge(item.path)" class="badge" />
|
||||
<div v-if="getMenuTextBadge(item.path)" class="text-badge">
|
||||
{{ getMenuTextBadge(item.path) }}
|
||||
</div>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
@@ -40,6 +43,7 @@
|
||||
import type { AppRouteRecord } from '@/types/router'
|
||||
import { formatMenuTitle } from '@/router/utils/utils'
|
||||
import { handleMenuJump } from '@/utils/navigation'
|
||||
import { useMenuBadge } from '@/composables/useMenuBadge'
|
||||
|
||||
// 类型定义
|
||||
interface Props {
|
||||
@@ -66,6 +70,9 @@
|
||||
(e: 'close'): void
|
||||
}>()
|
||||
|
||||
// 使用菜单徽标系统
|
||||
const { isMenuBadgeVisible, getMenuTextBadge } = useMenuBadge()
|
||||
|
||||
// 计算属性
|
||||
const filteredMenuItems = computed(() => filterRoutes(props.list))
|
||||
|
||||
|
||||
@@ -0,0 +1,264 @@
|
||||
import { computed } from 'vue'
|
||||
import { useMenuBadgeStore } from '@/store/modules/menuBadge'
|
||||
import { useMenuStore } from '@/store/modules/menu'
|
||||
import mittBus from '@/utils/sys/mittBus'
|
||||
|
||||
// 全局初始化状态
|
||||
let globalInitialized = false
|
||||
|
||||
// 重置全局初始化状态(用于退出登录后重新初始化)
|
||||
export const resetGlobalInitialized = () => {
|
||||
globalInitialized = false
|
||||
// console.log('徽标系统全局初始化状态已重置')
|
||||
}
|
||||
|
||||
/**
|
||||
* 菜单徽标管理 Composable
|
||||
* 提供菜单徽标的统一管理接口
|
||||
*/
|
||||
export function useMenuBadge() {
|
||||
const menuBadgeStore = useMenuBadgeStore()
|
||||
const menuStore = useMenuStore()
|
||||
|
||||
// 计算属性:获取菜单列表(带徽标状态)
|
||||
const menuListWithBadges = computed(() => {
|
||||
const menuList = menuStore.menuList
|
||||
if (!menuList.length) return menuList
|
||||
|
||||
// 同步徽标状态到菜单列表
|
||||
menuBadgeStore.syncBadgeWithMenuList(menuList)
|
||||
return menuList
|
||||
})
|
||||
|
||||
// 更新菜单徽标
|
||||
const updateMenuBadge = (path: string, count: number, type: 'dot' | 'text' = 'text') => {
|
||||
menuBadgeStore.updateBadgeData(path, count, type)
|
||||
}
|
||||
|
||||
// 批量更新菜单徽标
|
||||
const batchUpdateMenuBadges = (
|
||||
updates: Array<{ path: string; count: number; type?: 'dot' | 'text' }>
|
||||
) => {
|
||||
menuBadgeStore.batchUpdateBadgeData(updates)
|
||||
}
|
||||
|
||||
// 清除菜单徽标
|
||||
const clearMenuBadge = (path: string) => {
|
||||
menuBadgeStore.clearBadge(path)
|
||||
}
|
||||
|
||||
// 清除所有菜单徽标
|
||||
const clearAllMenuBadges = () => {
|
||||
menuBadgeStore.clearAllBadges()
|
||||
}
|
||||
|
||||
// 获取菜单徽标数量
|
||||
const getMenuBadgeCount = (path: string) => {
|
||||
return menuBadgeStore.getBadgeCount(path)
|
||||
}
|
||||
|
||||
// 检查菜单是否显示徽标
|
||||
const isMenuBadgeVisible = (path: string) => {
|
||||
return menuBadgeStore.shouldShowBadge(path)
|
||||
}
|
||||
|
||||
// 获取菜单文本徽标
|
||||
const getMenuTextBadge = (path: string) => {
|
||||
return menuBadgeStore.shouldShowTextBadge(path)
|
||||
}
|
||||
|
||||
// 处理WebSocket徽标更新消息
|
||||
const handleWebSocketBadgeUpdate = (data: any) => {
|
||||
// console.log('收到WebSocket徽标更新消息:', data)
|
||||
|
||||
try {
|
||||
// 根据消息类型处理不同的徽标更新
|
||||
if (data.event === 'menu.badge.update') {
|
||||
const { path, count, type } = data.data
|
||||
if (path && typeof count === 'number') {
|
||||
updateMenuBadge(path, count, type || 'text')
|
||||
}
|
||||
} else if (data.event === 'menu.badge.batch_update') {
|
||||
const { updates } = data.data
|
||||
if (Array.isArray(updates)) {
|
||||
batchUpdateMenuBadges(updates)
|
||||
}
|
||||
} else if (data.event === 'menu.badge.clear') {
|
||||
const { path } = data.data
|
||||
if (path) {
|
||||
clearMenuBadge(path)
|
||||
} else {
|
||||
clearAllMenuBadges()
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('处理WebSocket徽标更新消息时出错:', error, data)
|
||||
}
|
||||
}
|
||||
|
||||
// 处理菜单徽标数据数组格式
|
||||
const handleMenuBadgeDataArray = (data: any) => {
|
||||
// console.log('收到菜单徽标数据数组:', data)
|
||||
|
||||
if (Array.isArray(data.data)) {
|
||||
const updates = data.data
|
||||
.filter((item: any) => item.url && typeof item.badge === 'number')
|
||||
.map((item: any) => ({
|
||||
path: item.url,
|
||||
count: item.badge,
|
||||
type: 'text' as const
|
||||
}))
|
||||
|
||||
if (updates.length > 0) {
|
||||
// console.log('批量更新菜单徽标:', updates)
|
||||
batchUpdateMenuBadges(updates)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理通知相关的徽标更新
|
||||
const handleNotificationBadgeUpdate = (data: any) => {
|
||||
// console.log('收到通知徽标更新消息:', data)
|
||||
|
||||
// 根据通知类型更新对应菜单的徽标
|
||||
if (data.event === 'notify.receive') {
|
||||
// 通知接收,更新通知菜单徽标
|
||||
const currentCount = getMenuBadgeCount('/notification')
|
||||
updateMenuBadge('/notification', currentCount + 1, 'text')
|
||||
} else if (data.event === 'notify.read') {
|
||||
// 通知已读,减少通知菜单徽标
|
||||
const currentCount = getMenuBadgeCount('/notification')
|
||||
updateMenuBadge('/notification', Math.max(0, currentCount - 1), 'text')
|
||||
} else if (data.event === 'notify.clear') {
|
||||
// 清除所有通知,清除通知菜单徽标
|
||||
clearMenuBadge('/notification')
|
||||
}
|
||||
}
|
||||
|
||||
// 处理审批相关的徽标更新
|
||||
const handleApprovalBadgeUpdate = (data: any) => {
|
||||
// console.log('收到审批徽标更新消息:', data)
|
||||
|
||||
if (data.event === 'approval.new') {
|
||||
// 新审批,更新审批菜单徽标
|
||||
const currentCount = getMenuBadgeCount('/admin/approval')
|
||||
updateMenuBadge('/admin/approval', currentCount + 1, 'text')
|
||||
} else if (data.event === 'approval.processed') {
|
||||
// 审批已处理,减少审批菜单徽标
|
||||
const currentCount = getMenuBadgeCount('/admin/approval')
|
||||
updateMenuBadge('/admin/approval', Math.max(0, currentCount - 1), 'text')
|
||||
}
|
||||
}
|
||||
|
||||
// 处理库存相关的徽标更新
|
||||
const handleStockBadgeUpdate = (data: any) => {
|
||||
// console.log('收到库存徽标更新消息:', data)
|
||||
|
||||
if (data.event === 'stock.low') {
|
||||
// 库存不足,更新库存菜单徽标
|
||||
const currentCount = getMenuBadgeCount('/admin/stock')
|
||||
updateMenuBadge('/admin/stock', currentCount + 1, 'dot')
|
||||
} else if (data.event === 'stock.normal') {
|
||||
// 库存正常,减少库存菜单徽标
|
||||
const currentCount = getMenuBadgeCount('/admin/stock')
|
||||
updateMenuBadge('/admin/stock', Math.max(0, currentCount - 1), 'dot')
|
||||
}
|
||||
}
|
||||
|
||||
// 统一的WebSocket消息处理
|
||||
const handleWebSocketMessage = (data: any) => {
|
||||
if (!data || !data.event) return
|
||||
|
||||
// 处理菜单徽标数据数组格式(如 approval.todo.counts)
|
||||
if (data.event.includes('.todo.counts') || data.event.includes('.badge.counts')) {
|
||||
handleMenuBadgeDataArray(data)
|
||||
} else if (data.event.includes('menu.badge')) {
|
||||
handleWebSocketBadgeUpdate(data)
|
||||
} else if (data.event.includes('notify')) {
|
||||
handleNotificationBadgeUpdate(data)
|
||||
} else if (data.event.includes('approval')) {
|
||||
handleApprovalBadgeUpdate(data)
|
||||
} else if (data.event.includes('stock')) {
|
||||
handleStockBadgeUpdate(data)
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化徽标系统
|
||||
const initBadgeSystem = () => {
|
||||
if (globalInitialized) {
|
||||
// console.log('菜单徽标系统已经全局初始化,跳过重复初始化')
|
||||
return
|
||||
}
|
||||
|
||||
// console.log('初始化菜单徽标系统')
|
||||
globalInitialized = true
|
||||
|
||||
// 标记页面已加载完成
|
||||
menuBadgeStore.setPageReady(true)
|
||||
|
||||
// 先清理可能存在的旧监听器,避免重复注册
|
||||
mittBus.off('ws:message', handleWebSocketMessage)
|
||||
mittBus.off('menu-badge-update', handleWebSocketBadgeUpdate)
|
||||
mittBus.off('approval-badge-update', handleApprovalBadgeUpdate)
|
||||
mittBus.off('stock-badge-update', handleStockBadgeUpdate)
|
||||
mittBus.off('notification-badge-update', handleNotificationBadgeUpdate)
|
||||
mittBus.off('menu:updated')
|
||||
|
||||
// 监听WebSocket消息
|
||||
mittBus.on('ws:message', handleWebSocketMessage)
|
||||
|
||||
// 监听特定的徽标更新事件
|
||||
mittBus.on('menu-badge-update', handleWebSocketBadgeUpdate)
|
||||
mittBus.on('approval-badge-update', handleApprovalBadgeUpdate)
|
||||
mittBus.on('stock-badge-update', handleStockBadgeUpdate)
|
||||
mittBus.on('notification-badge-update', handleNotificationBadgeUpdate)
|
||||
|
||||
// 监听菜单数据变化,同步徽标状态
|
||||
mittBus.on('menu:updated', () => {
|
||||
menuBadgeStore.syncBadgeWithMenuList(menuStore.menuList)
|
||||
})
|
||||
}
|
||||
|
||||
// 清理徽标系统
|
||||
const cleanupBadgeSystem = () => {
|
||||
if (!globalInitialized) {
|
||||
// console.log('菜单徽标系统未全局初始化,跳过清理')
|
||||
return
|
||||
}
|
||||
|
||||
// console.log('清理菜单徽标系统')
|
||||
globalInitialized = false
|
||||
|
||||
// 移除事件监听
|
||||
mittBus.off('ws:message', handleWebSocketMessage)
|
||||
mittBus.off('menu-badge-update', handleWebSocketBadgeUpdate)
|
||||
mittBus.off('approval-badge-update', handleApprovalBadgeUpdate)
|
||||
mittBus.off('stock-badge-update', handleStockBadgeUpdate)
|
||||
mittBus.off('notification-badge-update', handleNotificationBadgeUpdate)
|
||||
mittBus.off('menu:updated')
|
||||
// 重置页面加载状态
|
||||
menuBadgeStore.setPageReady(false)
|
||||
}
|
||||
|
||||
// 注意:不在组件中自动初始化,避免重复初始化
|
||||
// 初始化应该在App.vue中全局调用一次
|
||||
|
||||
return {
|
||||
// 计算属性
|
||||
menuListWithBadges,
|
||||
|
||||
// 方法
|
||||
updateMenuBadge,
|
||||
batchUpdateMenuBadges,
|
||||
clearMenuBadge,
|
||||
clearAllMenuBadges,
|
||||
getMenuBadgeCount,
|
||||
isMenuBadgeVisible,
|
||||
getMenuTextBadge,
|
||||
|
||||
// 系统方法
|
||||
initBadgeSystem,
|
||||
cleanupBadgeSystem,
|
||||
handleWebSocketMessage
|
||||
}
|
||||
}
|
||||
@@ -407,7 +407,7 @@ export function useStockingForm(isFinal = false, stockingId?: string | number) {
|
||||
// 金额包装方法
|
||||
const wrapMoney = (amount: any, currency: string, remark: string) => ({
|
||||
currency: currency || '',
|
||||
amount: amount === undefined || amount === null ? '' : String(amount),
|
||||
amount: amount === undefined || amount === null ? 0 : String(amount),
|
||||
rate: getCurrencyRate(currency),
|
||||
remark
|
||||
})
|
||||
|
||||
@@ -0,0 +1,303 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref, computed } from 'vue'
|
||||
import { AppRouteRecord } from '@/types/router'
|
||||
import { useUserStore } from './user'
|
||||
|
||||
// 菜单徽标数据类型
|
||||
export interface MenuBadgeData {
|
||||
/** 菜单路径 */
|
||||
path: string
|
||||
/** 徽标数量 */
|
||||
count: number
|
||||
/** 徽标类型:dot(红点) | text(数字) */
|
||||
type: 'dot' | 'text'
|
||||
/** 最后更新时间 */
|
||||
lastUpdated: number
|
||||
}
|
||||
|
||||
// 菜单徽标Store
|
||||
export const useMenuBadgeStore = defineStore(
|
||||
'menuBadgeStore',
|
||||
() => {
|
||||
// 徽标数据映射 - 使用普通对象而不是Map,避免持久化问题
|
||||
const badgeData = ref<Record<string, MenuBadgeData>>({})
|
||||
|
||||
// 页面是否已加载完成
|
||||
const isPageReady = ref(false)
|
||||
|
||||
// 待处理的徽标更新队列(页面未加载完成时使用)
|
||||
const pendingUpdates = ref<Array<{ path: string; count: number; type: 'dot' | 'text' }>>([])
|
||||
|
||||
// 获取当前用户ID
|
||||
const getCurrentUserId = (): string => {
|
||||
try {
|
||||
const userStore = useUserStore()
|
||||
return userStore.info?.userId?.toString() || 'anonymous'
|
||||
} catch {
|
||||
return 'anonymous'
|
||||
}
|
||||
}
|
||||
|
||||
// 获取用户特定的存储key
|
||||
const getUserStorageKey = (): string => {
|
||||
const userId = getCurrentUserId()
|
||||
return `menu-badge-${userId}`
|
||||
}
|
||||
|
||||
// 从localStorage加载用户特定的徽标数据
|
||||
const loadUserBadgeData = () => {
|
||||
try {
|
||||
const storageKey = getUserStorageKey()
|
||||
const stored = localStorage.getItem(storageKey)
|
||||
if (stored) {
|
||||
const parsed = JSON.parse(stored)
|
||||
badgeData.value = parsed.badgeData || {}
|
||||
isPageReady.value = parsed.isPageReady || false
|
||||
pendingUpdates.value = parsed.pendingUpdates || []
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('加载用户徽标数据失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 保存用户特定的徽标数据到localStorage
|
||||
const saveUserBadgeData = () => {
|
||||
try {
|
||||
const storageKey = getUserStorageKey()
|
||||
const data = {
|
||||
badgeData: badgeData.value,
|
||||
isPageReady: isPageReady.value,
|
||||
pendingUpdates: pendingUpdates.value
|
||||
}
|
||||
localStorage.setItem(storageKey, JSON.stringify(data))
|
||||
} catch (error) {
|
||||
console.warn('保存用户徽标数据失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 清理其他用户的徽标数据
|
||||
const cleanupOtherUserData = () => {
|
||||
try {
|
||||
const currentUserId = getCurrentUserId()
|
||||
const keys = Object.keys(localStorage)
|
||||
keys.forEach((key) => {
|
||||
if (key.startsWith('menu-badge-') && !key.endsWith(`-${currentUserId}`)) {
|
||||
localStorage.removeItem(key)
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
console.warn('清理其他用户数据失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 计算属性:获取所有徽标数据
|
||||
const allBadgeData = computed(() => {
|
||||
return { ...badgeData.value }
|
||||
})
|
||||
|
||||
// 获取指定菜单的徽标数据
|
||||
const getBadgeData = (path: string): MenuBadgeData | null => {
|
||||
return badgeData.value[path] || null
|
||||
}
|
||||
|
||||
// 获取指定菜单的徽标数量
|
||||
const getBadgeCount = (path: string): number => {
|
||||
const data = badgeData.value[path]
|
||||
return data?.count || 0
|
||||
}
|
||||
|
||||
// 检查菜单是否应该显示徽标
|
||||
const shouldShowBadge = (path: string): boolean => {
|
||||
const data = badgeData.value[path]
|
||||
return Boolean(data && data.count > 0)
|
||||
}
|
||||
|
||||
// 检查菜单是否应该显示文本徽标
|
||||
const shouldShowTextBadge = (path: string): string | null => {
|
||||
const data = badgeData.value[path]
|
||||
if (data && data.count > 0 && data.type === 'text') {
|
||||
return data.count > 99 ? '99+' : data.count.toString()
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
// 设置页面加载状态
|
||||
const setPageReady = (ready: boolean) => {
|
||||
isPageReady.value = ready
|
||||
|
||||
// 页面加载完成时,加载用户特定的徽标数据
|
||||
if (ready) {
|
||||
loadUserBadgeData()
|
||||
// 处理待处理的更新
|
||||
if (pendingUpdates.value.length > 0) {
|
||||
// console.log('页面加载完成,处理待处理的徽标更新:', pendingUpdates.value.length)
|
||||
pendingUpdates.value.forEach((update) => {
|
||||
updateBadgeData(update.path, update.count, update.type)
|
||||
})
|
||||
pendingUpdates.value = []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 更新徽标数据
|
||||
const updateBadgeData = (path: string, count: number, type: 'dot' | 'text' = 'text') => {
|
||||
// 如果页面未加载完成,将更新加入队列
|
||||
if (!isPageReady.value) {
|
||||
// console.log('页面未加载完成,将徽标更新加入队列:', { path, count, type })
|
||||
// 检查是否已存在相同路径的待处理更新,避免重复
|
||||
const existingIndex = pendingUpdates.value.findIndex((item) => item.path === path)
|
||||
if (existingIndex >= 0) {
|
||||
pendingUpdates.value[existingIndex] = { path, count, type }
|
||||
} else {
|
||||
pendingUpdates.value.push({ path, count, type })
|
||||
}
|
||||
// 保存到用户特定存储
|
||||
saveUserBadgeData()
|
||||
return
|
||||
}
|
||||
|
||||
// console.log('更新菜单徽标:', { path, count, type })
|
||||
|
||||
if (count <= 0) {
|
||||
// 如果数量为0或负数,移除徽标
|
||||
delete badgeData.value[path]
|
||||
} else {
|
||||
// 更新或创建徽标数据
|
||||
badgeData.value[path] = {
|
||||
path,
|
||||
count,
|
||||
type,
|
||||
lastUpdated: Date.now()
|
||||
}
|
||||
}
|
||||
|
||||
// 保存到用户特定存储
|
||||
saveUserBadgeData()
|
||||
}
|
||||
|
||||
// 批量更新徽标数据
|
||||
const batchUpdateBadgeData = (
|
||||
updates: Array<{ path: string; count: number; type?: 'dot' | 'text' }>
|
||||
) => {
|
||||
if (!isPageReady.value) {
|
||||
// console.log('页面未加载完成,将批量徽标更新加入队列:', updates.length)
|
||||
updates.forEach((update) => {
|
||||
const existingIndex = pendingUpdates.value.findIndex((item) => item.path === update.path)
|
||||
if (existingIndex >= 0) {
|
||||
pendingUpdates.value[existingIndex] = {
|
||||
path: update.path,
|
||||
count: update.count,
|
||||
type: update.type || 'text'
|
||||
}
|
||||
} else {
|
||||
pendingUpdates.value.push({
|
||||
path: update.path,
|
||||
count: update.count,
|
||||
type: update.type || 'text'
|
||||
})
|
||||
}
|
||||
})
|
||||
// 保存到用户特定存储
|
||||
saveUserBadgeData()
|
||||
return
|
||||
}
|
||||
|
||||
// console.log('批量更新菜单徽标:', updates.length)
|
||||
updates.forEach((update) => {
|
||||
updateBadgeData(update.path, update.count, update.type || 'text')
|
||||
})
|
||||
// 批量更新后保存
|
||||
saveUserBadgeData()
|
||||
}
|
||||
|
||||
// 清除指定菜单的徽标
|
||||
const clearBadge = (path: string) => {
|
||||
updateBadgeData(path, 0)
|
||||
}
|
||||
|
||||
// 清除所有徽标
|
||||
const clearAllBadges = () => {
|
||||
badgeData.value = {}
|
||||
pendingUpdates.value = []
|
||||
// 保存到用户特定存储
|
||||
saveUserBadgeData()
|
||||
}
|
||||
|
||||
// 从菜单列表同步徽标状态
|
||||
const syncBadgeWithMenuList = (menuList: AppRouteRecord[]) => {
|
||||
if (!isPageReady.value) {
|
||||
// console.log('页面未加载完成,跳过菜单徽标同步')
|
||||
return
|
||||
}
|
||||
|
||||
const updateMenuBadge = (menus: AppRouteRecord[]) => {
|
||||
menus.forEach((menu) => {
|
||||
const badgeData = getBadgeData(menu.path)
|
||||
if (badgeData) {
|
||||
// 更新菜单的徽标状态
|
||||
menu.meta.showBadge = badgeData.type === 'dot' && badgeData.count > 0
|
||||
menu.meta.showTextBadge =
|
||||
badgeData.type === 'text' && badgeData.count > 0
|
||||
? badgeData.count > 99
|
||||
? '99+'
|
||||
: badgeData.count.toString()
|
||||
: undefined
|
||||
} else {
|
||||
// 清除菜单的徽标状态
|
||||
menu.meta.showBadge = false
|
||||
menu.meta.showTextBadge = undefined
|
||||
}
|
||||
|
||||
// 递归处理子菜单
|
||||
if (menu.children) {
|
||||
updateMenuBadge(menu.children)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
updateMenuBadge(menuList)
|
||||
}
|
||||
|
||||
// 获取待处理更新的数量
|
||||
const getPendingUpdatesCount = () => pendingUpdates.value.length
|
||||
|
||||
// 获取待处理的更新列表
|
||||
const getPendingUpdates = () => [...pendingUpdates.value]
|
||||
|
||||
return {
|
||||
// 状态
|
||||
badgeData,
|
||||
isPageReady,
|
||||
pendingUpdates,
|
||||
|
||||
// 计算属性
|
||||
allBadgeData,
|
||||
|
||||
// 方法
|
||||
getBadgeData,
|
||||
getBadgeCount,
|
||||
shouldShowBadge,
|
||||
shouldShowTextBadge,
|
||||
setPageReady,
|
||||
updateBadgeData,
|
||||
batchUpdateBadgeData,
|
||||
clearBadge,
|
||||
clearAllBadges,
|
||||
syncBadgeWithMenuList,
|
||||
getPendingUpdatesCount,
|
||||
getPendingUpdates,
|
||||
|
||||
// 用户隔离相关方法
|
||||
loadUserBadgeData,
|
||||
saveUserBadgeData,
|
||||
cleanupOtherUserData,
|
||||
getCurrentUserId
|
||||
}
|
||||
},
|
||||
{
|
||||
persist: {
|
||||
key: 'menu-badge',
|
||||
storage: localStorage
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -140,6 +140,20 @@ export const useUserStore = defineStore(
|
||||
}
|
||||
|
||||
const logOut = () => {
|
||||
// 清理徽标系统
|
||||
import('@/composables/useMenuBadge').then(({ useMenuBadge, resetGlobalInitialized }) => {
|
||||
const { cleanupBadgeSystem, clearAllMenuBadges } = useMenuBadge()
|
||||
clearAllMenuBadges()
|
||||
cleanupBadgeSystem()
|
||||
resetGlobalInitialized()
|
||||
})
|
||||
|
||||
// 清理徽标store中的其他用户数据
|
||||
import('@/store/modules/menuBadge').then(({ useMenuBadgeStore }) => {
|
||||
const menuBadgeStore = useMenuBadgeStore()
|
||||
menuBadgeStore.cleanupOtherUserData()
|
||||
})
|
||||
|
||||
// 清空所有本地缓存
|
||||
localStorage.clear()
|
||||
sessionStorage.clear()
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
/**
|
||||
* 菜单徽标管理工具
|
||||
* 用于管理菜单徽标功能
|
||||
*/
|
||||
|
||||
import { useMenuBadgeStore } from '@/store/modules/menuBadge'
|
||||
|
||||
/**
|
||||
* 菜单徽标管理类
|
||||
*/
|
||||
export class MenuBadgeTest {
|
||||
private menuBadgeStore = useMenuBadgeStore()
|
||||
|
||||
/**
|
||||
* 直接更新徽标
|
||||
*/
|
||||
updateBadge(path: string, count: number, type: 'dot' | 'text' = 'text') {
|
||||
// console.log(`直接更新徽标: ${path} = ${count} (${type})`)
|
||||
this.menuBadgeStore.updateBadgeData(path, count, type)
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量更新徽标
|
||||
*/
|
||||
batchUpdateBadges(updates: Array<{ path: string; count: number; type?: 'dot' | 'text' }>) {
|
||||
// console.log('批量更新徽标:', updates)
|
||||
this.menuBadgeStore.batchUpdateBadgeData(updates)
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除徽标
|
||||
*/
|
||||
clearBadge(path: string) {
|
||||
// console.log(`清除徽标: ${path}`)
|
||||
this.menuBadgeStore.clearBadge(path)
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除所有徽标
|
||||
*/
|
||||
clearAllBadges() {
|
||||
// console.log('清除所有徽标')
|
||||
this.menuBadgeStore.clearAllBadges()
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取徽标状态
|
||||
*/
|
||||
getBadgeStatus(path: string) {
|
||||
const data = this.menuBadgeStore.getBadgeData(path)
|
||||
// console.log(`徽标状态 ${path}:`, data)
|
||||
return data
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有徽标状态
|
||||
*/
|
||||
getAllBadgeStatus() {
|
||||
const allData = this.menuBadgeStore.allBadgeData
|
||||
// console.log('所有徽标状态:', allData)
|
||||
return allData
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取待处理更新数量
|
||||
*/
|
||||
getPendingUpdatesCount() {
|
||||
const count = this.menuBadgeStore.getPendingUpdatesCount()
|
||||
// console.log(`待处理更新数量: ${count}`)
|
||||
return count
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置页面加载状态
|
||||
*/
|
||||
setPageReady(ready: boolean) {
|
||||
// console.log(`设置页面加载状态: ${ready}`)
|
||||
this.menuBadgeStore.setPageReady(ready)
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查实际菜单路径
|
||||
*/
|
||||
checkActualMenuPaths() {
|
||||
// console.log('检查实际菜单路径...')
|
||||
|
||||
try {
|
||||
// 获取菜单store
|
||||
import('@/store/modules/menu')
|
||||
.then(({ useMenuStore }) => {
|
||||
const menuStore = useMenuStore()
|
||||
this.printMenuPaths(menuStore.menuList)
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('动态导入菜单store失败:', error)
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('检查菜单路径失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
private printMenuPaths(menuList: any[]) {
|
||||
// console.log('当前菜单列表:', menuList)
|
||||
|
||||
// 递归打印所有菜单路径
|
||||
const printMenuPaths = (menus: any[], level = 0) => {
|
||||
menus.forEach((menu) => {
|
||||
// const indent = ' '.repeat(level)
|
||||
// console.log(`${indent}${menu.path} - ${menu.meta?.title}`)
|
||||
if (menu.children && menu.children.length > 0) {
|
||||
printMenuPaths(menu.children, level + 1)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (menuList && menuList.length > 0) {
|
||||
printMenuPaths(menuList)
|
||||
} else {
|
||||
console.log('菜单列表为空,可能还未加载完成')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 运行基础功能测试
|
||||
*/
|
||||
runBasicTest() {
|
||||
// console.log('开始运行基础功能测试...')
|
||||
|
||||
// 1. 测试页面未加载完成时的更新
|
||||
// console.log('\n=== 测试页面未加载完成时的更新 ===')
|
||||
this.setPageReady(false)
|
||||
this.updateBadge('/admin/approval', 3, 'text')
|
||||
this.updateBadge('/admin/stock', 1, 'dot')
|
||||
|
||||
// 2. 设置页面加载完成
|
||||
// console.log('\n=== 设置页面加载完成 ===')
|
||||
this.setPageReady(true)
|
||||
|
||||
// 3. 测试批量更新
|
||||
// console.log('\n=== 测试批量更新 ===')
|
||||
this.batchUpdateBadges([
|
||||
{ path: '/admin/approval', count: 3, type: 'text' },
|
||||
{ path: '/admin/stock', count: 1, type: 'dot' }
|
||||
])
|
||||
|
||||
// 4. 显示最终状态
|
||||
// console.log('\n=== 最终状态 ===')
|
||||
this.getAllBadgeStatus()
|
||||
|
||||
// console.log('\n基础功能测试完成!')
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查WebSocket连接状态
|
||||
*/
|
||||
checkWebSocketStatus() {
|
||||
// console.log('检查WebSocket连接状态...')
|
||||
|
||||
// 检查全局WebSocket状态函数
|
||||
if (typeof window !== 'undefined' && (window as any).getWebSocketStatus) {
|
||||
const status = (window as any).getWebSocketStatus()
|
||||
console.log('WebSocket状态:', status)
|
||||
} else {
|
||||
console.log('WebSocket状态函数不可用')
|
||||
}
|
||||
|
||||
// 检查菜单徽标系统初始化状态
|
||||
const pendingCount = this.menuBadgeStore.getPendingUpdatesCount()
|
||||
console.log('待处理的徽标更新数量:', pendingCount)
|
||||
|
||||
if (pendingCount > 0) {
|
||||
console.log('待处理的更新:', this.menuBadgeStore.getPendingUpdates())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 创建全局测试实例
|
||||
const menuBadgeTest = new MenuBadgeTest()
|
||||
|
||||
// 将测试工具挂载到全局对象,方便在控制台使用
|
||||
if (typeof window !== 'undefined') {
|
||||
// @ts-expect-error - 挂载到全局对象用于调试
|
||||
window.menuBadgeTest = menuBadgeTest
|
||||
}
|
||||
|
||||
export default menuBadgeTest
|
||||
@@ -1,4 +1,4 @@
|
||||
import mittBus from './mittBus'
|
||||
import mittBus from './mittBus'
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
import { ElNotification } from 'element-plus'
|
||||
|
||||
@@ -94,10 +94,14 @@ const handleMessage = (event: MessageEvent) => {
|
||||
let data: any = event.data
|
||||
try {
|
||||
data = JSON.parse(event.data)
|
||||
} catch {
|
||||
// 忽略解析错误
|
||||
} catch (error) {
|
||||
// console.warn('WebSocket消息解析失败:', error, '原始数据:', event.data)
|
||||
// 对于无法解析的消息,仍然尝试处理原始数据
|
||||
data = { event: 'unknown', data: event.data }
|
||||
}
|
||||
console.log('WebSocket message:', data)
|
||||
|
||||
// 处理通知消息
|
||||
if (data.event == 'notify.receive') {
|
||||
ElNotification({
|
||||
title: data.data.notice_data.message.title || '通知',
|
||||
@@ -106,22 +110,85 @@ const handleMessage = (event: MessageEvent) => {
|
||||
})
|
||||
mittBus.emit('notification-data-changed', 1)
|
||||
}
|
||||
|
||||
// 处理菜单徽标更新消息
|
||||
if (data.event && data.event.includes('menu.badge')) {
|
||||
// console.log('收到菜单徽标更新消息:', data)
|
||||
// 直接触发徽标更新事件
|
||||
mittBus.emit('menu-badge-update', data)
|
||||
}
|
||||
|
||||
// 处理审批相关消息
|
||||
if (data.event && data.event.includes('approval')) {
|
||||
// console.log('收到审批相关消息:', data)
|
||||
// 如果是审批待办数量消息,直接触发徽标更新
|
||||
if (data.event.includes('.todo.counts') || data.event.includes('.badge.counts')) {
|
||||
mittBus.emit('menu-badge-update', data)
|
||||
} else {
|
||||
// 其他审批消息触发审批徽标更新事件
|
||||
mittBus.emit('approval-badge-update', data)
|
||||
}
|
||||
}
|
||||
|
||||
// 处理库存相关消息
|
||||
if (data.event && data.event.includes('stock')) {
|
||||
// console.log('收到库存相关消息:', data)
|
||||
// 触发库存徽标更新事件
|
||||
mittBus.emit('stock-badge-update', data)
|
||||
}
|
||||
|
||||
// 处理通知相关消息
|
||||
if (data.event && data.event.includes('notify')) {
|
||||
// console.log('收到通知相关消息:', data)
|
||||
// 触发通知徽标更新事件
|
||||
mittBus.emit('notification-badge-update', data)
|
||||
}
|
||||
|
||||
// 处理其他可能的徽标相关消息
|
||||
if (data.event && (data.event.includes('badge') || data.event.includes('count'))) {
|
||||
// console.log('收到其他徽标相关消息:', data)
|
||||
// 尝试作为通用徽标更新处理
|
||||
mittBus.emit('menu-badge-update', data)
|
||||
}
|
||||
|
||||
// 发送原始消息到事件总线
|
||||
mittBus.emit('ws:message', data)
|
||||
|
||||
// 检查WebSocket连接状态,如果连接异常则记录
|
||||
if (socket && socket.readyState !== WebSocket.OPEN) {
|
||||
// console.warn('WebSocket连接状态异常,当前状态:', socket.readyState, '消息可能丢失')
|
||||
}
|
||||
}
|
||||
|
||||
// 设置WebSocket事件处理器
|
||||
const setupWebSocketHandlers = (ws: WebSocket) => {
|
||||
ws.onopen = () => {
|
||||
console.log('WebSocket连接已建立')
|
||||
isInitialConnection = false
|
||||
isConnecting = false
|
||||
startHeartbeat()
|
||||
mittBus.emit('ws:open')
|
||||
sendPing()
|
||||
// 连接建立成功后推送一个连接确认消息
|
||||
const connectMessage = {
|
||||
event: 'system.connect',
|
||||
data: {
|
||||
timestamp: Date.now(),
|
||||
message: 'WebSocket连接已建立'
|
||||
},
|
||||
trace_id: generateTraceId()
|
||||
}
|
||||
try {
|
||||
ws.send(JSON.stringify(connectMessage))
|
||||
} catch (error) {
|
||||
console.error('发送连接确认消息失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
ws.onmessage = handleMessage
|
||||
|
||||
ws.onerror = (err) => {
|
||||
console.error('WebSocket连接错误:', err)
|
||||
mittBus.emit('ws:error', err)
|
||||
isConnecting = false
|
||||
try {
|
||||
@@ -134,6 +201,7 @@ const setupWebSocketHandlers = (ws: WebSocket) => {
|
||||
}
|
||||
|
||||
ws.onclose = () => {
|
||||
console.log('WebSocket连接已关闭')
|
||||
mittBus.emit('ws:close')
|
||||
isConnecting = false
|
||||
if (reconnectAttempts < MAX_RECONNECT_ATTEMPTS) {
|
||||
@@ -187,32 +255,50 @@ const scheduleReconnect = () => {
|
||||
// 初始化WebSocket连接
|
||||
export const initWebSocket = (token: string) => {
|
||||
try {
|
||||
if (!token) return
|
||||
if (!token) {
|
||||
console.log('WebSocket初始化失败:没有token')
|
||||
return
|
||||
}
|
||||
const now = Date.now()
|
||||
// 简单防抖:在短时间内重复触发则跳过
|
||||
if (now - lastInitAt < INIT_DEBOUNCE_MS) {
|
||||
console.log('WebSocket初始化被防抖跳过')
|
||||
return
|
||||
}
|
||||
// 避免并发初始化:已在连接中或已连接且 token 未变化则跳过
|
||||
if (isConnecting) return
|
||||
if (socket && (socket.readyState === WebSocket.OPEN || socket.readyState === WebSocket.CONNECTING)) {
|
||||
if (isConnecting) {
|
||||
console.log('WebSocket正在连接中,跳过重复初始化')
|
||||
return
|
||||
}
|
||||
if (
|
||||
socket &&
|
||||
(socket.readyState === WebSocket.OPEN || socket.readyState === WebSocket.CONNECTING)
|
||||
) {
|
||||
if (lastInitToken === token) {
|
||||
console.log('WebSocket已连接且token未变化,跳过初始化')
|
||||
return
|
||||
}
|
||||
// token 变更:需要切换连接
|
||||
try { socket.close() } catch {}
|
||||
console.log('WebSocket token变更,关闭旧连接')
|
||||
try {
|
||||
socket.close()
|
||||
} catch {
|
||||
// 忽略关闭错误
|
||||
}
|
||||
socket = null
|
||||
}
|
||||
|
||||
cleanup()
|
||||
|
||||
const url = `${WS_BASE}${token}`
|
||||
console.log('正在初始化WebSocket连接:', url)
|
||||
isConnecting = true
|
||||
lastInitToken = token
|
||||
lastInitAt = now
|
||||
socket = new WebSocket(url)
|
||||
setupWebSocketHandlers(socket)
|
||||
} catch (e) {
|
||||
console.error('WebSocket初始化失败:', e)
|
||||
mittBus.emit('ws:error', e)
|
||||
isConnecting = false
|
||||
scheduleReconnect()
|
||||
@@ -270,4 +356,4 @@ export const getWebSocketStatus = () => ({
|
||||
isInitialConnection,
|
||||
isConnecting,
|
||||
lastInitToken
|
||||
})
|
||||
})
|
||||
|
||||
@@ -425,7 +425,13 @@
|
||||
// const businessUsers = ref([])
|
||||
|
||||
// 选项缓存,避免重复请求
|
||||
const optionsCache = reactive({ role: [], user: [], department: [] })
|
||||
const optionsCache = reactive({
|
||||
role: [],
|
||||
user: [],
|
||||
department: [],
|
||||
grouplist: [],
|
||||
group_members: []
|
||||
})
|
||||
|
||||
// 懒加载并缓存不同类型的选项
|
||||
const ensureOptionsLoaded = async (group) => {
|
||||
@@ -449,6 +455,21 @@
|
||||
optionsCache.user = response || []
|
||||
}
|
||||
group.options = optionsCache.user
|
||||
} else if (type === 'scenario_leader_of_initiator') {
|
||||
if (!optionsCache.grouplist || optionsCache.grouplist.length === 0) {
|
||||
const response = await commonService.getUserGroupScene()
|
||||
optionsCache.grouplist = response || []
|
||||
}
|
||||
group.options = optionsCache.grouplist
|
||||
} else if (type === 'group_members') {
|
||||
if (!optionsCache.group_members || optionsCache.group_members.length === 0) {
|
||||
const response = await commonService.getUserGroupSelect()
|
||||
response.forEach((group) => {
|
||||
group.name = group.title
|
||||
})
|
||||
optionsCache.group_members = response || []
|
||||
}
|
||||
group.options = optionsCache.group_members
|
||||
} else {
|
||||
group.options = []
|
||||
}
|
||||
|
||||
@@ -453,6 +453,21 @@
|
||||
optionsCache.user = response || []
|
||||
}
|
||||
group.options = optionsCache.user
|
||||
} else if (type === 'scenario_leader_of_initiator') {
|
||||
if (!optionsCache.grouplist || optionsCache.grouplist.length === 0) {
|
||||
const response = await commonService.getUserGroupScene()
|
||||
optionsCache.grouplist = response || []
|
||||
}
|
||||
group.options = optionsCache.grouplist
|
||||
} else if (type === 'group_members') {
|
||||
if (!optionsCache.group_members || optionsCache.group_members.length === 0) {
|
||||
const response = await commonService.getUserGroupSelect()
|
||||
response.forEach((group) => {
|
||||
group.name = group.title
|
||||
})
|
||||
optionsCache.group_members = response || []
|
||||
}
|
||||
group.options = optionsCache.group_members
|
||||
} else {
|
||||
group.options = []
|
||||
}
|
||||
@@ -590,10 +605,21 @@
|
||||
if (ap.type === 'initiator_leader') {
|
||||
group.users = []
|
||||
} else if (ap.value) {
|
||||
group.users = String(ap.value)
|
||||
const values = String(ap.value)
|
||||
.split(',')
|
||||
.map((v) => Number((v || '').trim()))
|
||||
.filter((n) => !Number.isNaN(n))
|
||||
.map((v) => (v || '').trim())
|
||||
.filter(Boolean)
|
||||
// 判断是否为纯数字ID
|
||||
const isNumericIds = values.every(
|
||||
(v) => !Number.isNaN(Number(v)) && Number.isInteger(Number(v))
|
||||
)
|
||||
if (isNumericIds) {
|
||||
// 如果是数字ID,转换为数字数组
|
||||
group.users = values.map((v) => Number(v))
|
||||
} else {
|
||||
// 如果是字符串名称,保持为字符串数组
|
||||
group.users = values
|
||||
}
|
||||
}
|
||||
node.approverGroups.push(group)
|
||||
}
|
||||
|
||||
@@ -29,7 +29,13 @@
|
||||
@current-change="handleCurrentChange"
|
||||
>
|
||||
<template #default>
|
||||
<ElTableColumn label="审批单名称" prop="flows_name" min-width="140" />
|
||||
<ElTableColumn label="审批单名称" prop="flows_name" min-width="140">
|
||||
<template #default="scope">
|
||||
<span :style="{ color: getFlowTypeColor(scope.row.flow_type) }">
|
||||
{{ scope.row.flows_name }}
|
||||
</span>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="审批说明" prop="biz_desc" min-width="160" />
|
||||
<ElTableColumn label="提交人" prop="initiator_name" min-width="120">
|
||||
<template #default="scope">
|
||||
@@ -79,7 +85,9 @@
|
||||
>
|
||||
<div class="approval-content">
|
||||
<!-- 头部信息 -->
|
||||
<div v-if="orderDialog.type == 'stock_orders'">
|
||||
<div
|
||||
v-if="orderDialog.type == 'stock_orders' || orderDialog.type == 'stock_orders_nullify'"
|
||||
>
|
||||
<OrderDetailContent :orderData="orderDialog.orderData" />
|
||||
</div>
|
||||
<div v-if="orderDialog.type == 'purchase_orders'">
|
||||
@@ -114,7 +122,12 @@
|
||||
<ElButton type="primary" size="large" class="submit-btn" @click="handleApprove('approve')"
|
||||
>同意</ElButton
|
||||
>
|
||||
<ElButton type="warning" size="large" class="submit-btn" @click="handleTransfer()"
|
||||
<ElButton
|
||||
v-if="orderDialog.type == 'purchase_orders'"
|
||||
type="warning"
|
||||
size="large"
|
||||
class="submit-btn"
|
||||
@click="handleTransfer()"
|
||||
>转交</ElButton
|
||||
>
|
||||
</div>
|
||||
@@ -178,6 +191,26 @@
|
||||
userOptions.value = res || []
|
||||
// return res || []
|
||||
}
|
||||
|
||||
// 根据 flow_type 获取审批单名称的字体颜色
|
||||
const getFlowTypeColor = (flowType: string): string => {
|
||||
switch (flowType) {
|
||||
case 'stock_orders':
|
||||
return '#409EFF' // 蓝色 - 备货单
|
||||
case 'stock_orders_nullify':
|
||||
return '#F56C6C' // 红色 - 备货单作废
|
||||
case 'purchase_orders':
|
||||
return '#67C23A' // 绿色 - 采购单
|
||||
case 'purchase_orders_nullify':
|
||||
return '#E6A23C' // 橙色 - 采购单作废
|
||||
case 'return_orders':
|
||||
return '#909399' // 灰色 - 退货单
|
||||
case 'refund_orders':
|
||||
return '#9C27B0' // 紫色 - 退款单
|
||||
default:
|
||||
return '#303133' // 默认黑色
|
||||
}
|
||||
}
|
||||
const zjForm = reactive({
|
||||
to_uid: '',
|
||||
instance_id: '',
|
||||
@@ -258,25 +291,38 @@
|
||||
id: row.id,
|
||||
flow_type: row.flow_type
|
||||
})
|
||||
if (row.flow_type == 'stock_orders') {
|
||||
const orderData: OrderData = res
|
||||
orderDialog.orderData = orderData
|
||||
orderDialog.type = row.flow_type
|
||||
orderDialog.title = '合塑实业-汽车事业部-备货单(客户下定)'
|
||||
} else if (row.flow_type == 'purchase_orders') {
|
||||
const orderData: OrderData = res
|
||||
const urls = res.contract_file_url
|
||||
const paths = res.contract_file
|
||||
const list: Array<{ name: string; url: string }> = []
|
||||
const nameFromPath = (p: string) => extractFileName(p)
|
||||
urls.forEach((u: string, i: number) => {
|
||||
const p = paths[i] || ''
|
||||
list.push({ name: p ? nameFromPath(p) : extractFileName(u), url: u })
|
||||
})
|
||||
orderData.contract_file_url = list
|
||||
orderDialog.orderData = orderData
|
||||
orderDialog.type = row.flow_type
|
||||
orderDialog.title = '合塑实业-汽车事业部-采购单(采购)'
|
||||
console.log(res)
|
||||
switch (row.flow_type) {
|
||||
case 'stock_orders': {
|
||||
const orderData: OrderData = res
|
||||
orderDialog.orderData = orderData
|
||||
orderDialog.type = row.flow_type
|
||||
orderDialog.title = '合塑实业-汽车事业部-备货单(客户下定)'
|
||||
break
|
||||
}
|
||||
case 'stock_orders_nullify': {
|
||||
const orderData: OrderData = res
|
||||
orderDialog.orderData = orderData
|
||||
orderDialog.type = row.flow_type
|
||||
orderDialog.title = '备货单-作废'
|
||||
break
|
||||
}
|
||||
case 'purchase_orders': {
|
||||
const orderData: OrderData = res
|
||||
const urls = res.contract_file_url
|
||||
const paths = res.contract_file
|
||||
const list: Array<{ name: string; url: string }> = []
|
||||
const nameFromPath = (p: string) => extractFileName(p)
|
||||
urls.forEach((u: string, i: number) => {
|
||||
const p = paths[i] || ''
|
||||
list.push({ name: p ? nameFromPath(p) : extractFileName(u), url: u })
|
||||
})
|
||||
orderData.contract_file_url = list
|
||||
orderDialog.orderData = orderData
|
||||
orderDialog.type = row.flow_type
|
||||
orderDialog.title = '合塑实业-汽车事业部-采购单(采购)'
|
||||
break
|
||||
}
|
||||
}
|
||||
approvalDialogVisible.value = true
|
||||
}
|
||||
|
||||
@@ -98,13 +98,23 @@
|
||||
<div class="form-card" v-if="status == 'pending'">
|
||||
<!-- 底部信息 -->
|
||||
<div class="submit-wrap">
|
||||
<ElButton type="primary" plain size="large" class="submit-btn" @click="handleSubmit('reject')"
|
||||
<ElButton
|
||||
type="primary"
|
||||
plain
|
||||
size="large"
|
||||
class="submit-btn"
|
||||
@click="handleSubmit('reject')"
|
||||
>驳回</ElButton
|
||||
>
|
||||
<ElButton type="primary" size="large" class="submit-btn" @click="handleSubmit('approve')"
|
||||
>同意</ElButton
|
||||
>
|
||||
<ElButton type="warning" size="large" class="submit-btn" @click="handleTransfer()"
|
||||
<ElButton
|
||||
v-if="flow_type == 'purchase_orders'"
|
||||
type="warning"
|
||||
size="large"
|
||||
class="submit-btn"
|
||||
@click="handleTransfer()"
|
||||
>转交</ElButton
|
||||
>
|
||||
</div>
|
||||
|
||||
@@ -32,7 +32,13 @@
|
||||
@current-change="handleCurrentChange"
|
||||
>
|
||||
<template #default>
|
||||
<ElTableColumn label="审批单名称" prop="flows_name" min-width="140" />
|
||||
<ElTableColumn label="审批单名称" prop="flows_name" min-width="140">
|
||||
<template #default="scope">
|
||||
<span :style="{ color: getFlowTypeColor(scope.row.flow_type) }">
|
||||
{{ scope.row.flows_name }}
|
||||
</span>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="审批说明" prop="biz_desc" min-width="160" />
|
||||
<ElTableColumn label="审批结果" prop="action_desc" min-width="160" />
|
||||
<ElTableColumn label="提交人" prop="initiator_name" min-width="120">
|
||||
@@ -158,7 +164,25 @@
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
|
||||
// 根据 flow_type 获取审批单名称的字体颜色
|
||||
const getFlowTypeColor = (flowType: string): string => {
|
||||
switch (flowType) {
|
||||
case 'stock_orders':
|
||||
return '#409EFF' // 蓝色 - 备货单
|
||||
case 'stock_orders_nullify':
|
||||
return '#F56C6C' // 红色 - 备货单作废
|
||||
case 'purchase_orders':
|
||||
return '#67C23A' // 绿色 - 采购单
|
||||
case 'purchase_orders_nullify':
|
||||
return '#E6A23C' // 橙色 - 采购单作废
|
||||
case 'return_orders':
|
||||
return '#909399' // 灰色 - 退货单
|
||||
case 'refund_orders':
|
||||
return '#9C27B0' // 紫色 - 退款单
|
||||
default:
|
||||
return '#303133' // 默认黑色
|
||||
}
|
||||
}
|
||||
watch([keyword], () => {
|
||||
pagination.currentPage = 1
|
||||
// 不自动请求,保持与截图行为一致,点击搜索或回车触发
|
||||
|
||||
@@ -221,7 +221,7 @@
|
||||
getList()
|
||||
})
|
||||
function statusClass(status: any) {
|
||||
if (status === 'rejected') return 'status-lost'
|
||||
if (status === 'rejected' || status === 'voided') return 'status-lost'
|
||||
if (status === 'approved') return 'status-following'
|
||||
return 'status-dealed'
|
||||
}
|
||||
|
||||
@@ -0,0 +1,281 @@
|
||||
<template>
|
||||
<div class="page-content">
|
||||
<div v-if="!isMobile">
|
||||
<ElRow>
|
||||
<div style="width: 12px"></div>
|
||||
<ElCol :xs="24" :sm="12" :lg="6" class="el-col2">
|
||||
<ElButton @click="showDialog('add')" v-ripple v-if="hasAuth('group@add')">新增</ElButton>
|
||||
</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"
|
||||
>
|
||||
<template #default>
|
||||
<ElTableColumn label="分组名称" prop="title" width="160px" />
|
||||
<ElTableColumn label="负责人" prop="id" width="120px">
|
||||
<template #default="scope">
|
||||
<ElTag>{{ scope.row.leader_name }}</ElTag>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="分组成员" prop="id">
|
||||
<template #default="scope">
|
||||
<span v-for="item in scope.row.user" :key="item.id" style="margin-right: 10px;"> {{ item.name }}</span>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="创建时间" prop="created_at" width="160px" />
|
||||
<ElTableColumn fixed="right" label="操作" width="110px">
|
||||
<template #default="scope">
|
||||
<ElRow>
|
||||
<el-button
|
||||
link
|
||||
@click="showDialog('edit', scope.row)"
|
||||
v-if="hasAuth('group@update')"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
link
|
||||
@click="deleteRole(scope.row)"
|
||||
v-if="hasAuth('group@destroy')"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</ElRow>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
</template>
|
||||
</ArtTable>
|
||||
<ElDialog
|
||||
v-model="dialogVisible"
|
||||
:title="dialogType === 'add' ? '新增' : '编辑'"
|
||||
width="40%"
|
||||
align-center
|
||||
>
|
||||
<ElForm ref="formRef" :model="form" :rules="rules" label-width="80px" class="form">
|
||||
<ElFormItem label="分组名称" prop="title">
|
||||
<ElInput v-model="form.title" />
|
||||
</ElFormItem>
|
||||
<ElFormItem label="分组成员" prop="user_ids">
|
||||
<el-select
|
||||
v-model="form.user_ids"
|
||||
multiple
|
||||
filterable
|
||||
placeholder="请选择分组成员"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in companyUserList"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="负责人" prop="leader_id">
|
||||
<el-select
|
||||
v-model="form.leader_id"
|
||||
filterable
|
||||
placeholder="请选择负责人"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in companyUserList"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="业务类型" prop="scenario_code">
|
||||
<ElInput v-model="form.scenario_code" />
|
||||
</ElFormItem>
|
||||
<ElFormItem label="排序" prop="sort">
|
||||
<ElInputNumber v-model="form.sort" />
|
||||
</ElFormItem>
|
||||
<ElFormItem label="状态">
|
||||
<ElSwitch v-model="form.status" :active-value="1" :inactive-value="0" />
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<ElButton @click="dialogVisible = false">取消</ElButton>
|
||||
<ElButton type="primary" @click="handleSubmit(formRef)">提交</ElButton>
|
||||
</div>
|
||||
</template>
|
||||
</ElDialog>
|
||||
</div>
|
||||
<div v-else class="mobile-hide-tip">
|
||||
<p>该页面暂不支持移动端访问</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
import { userConService } from '@/api/admin/user'
|
||||
import { useAuth } from '@/composables/useAuth'
|
||||
import { manageService } from '@/api/adminApi'
|
||||
const { hasAuth } = useAuth()
|
||||
defineOptions({ name: 'Company' })
|
||||
const loading = ref(false)
|
||||
const dialogVisible = ref(false)
|
||||
const formRef = ref<FormInstance>()
|
||||
const { width } = useWindowSize()
|
||||
const isMobile = computed(() => width.value < 500)
|
||||
const rules = reactive<FormRules>({
|
||||
title: [{ required: true, message: '请输入分组名称', trigger: 'blur' }],
|
||||
user_ids: [{ required: true, message: '请选择分组成员', trigger: 'blur' }]
|
||||
// tenant_key: [{ required: true, message: '请输入飞书标识', trigger: 'blur' }]
|
||||
})
|
||||
const companyUserList = ref<any>()
|
||||
// 获取用户列表
|
||||
const getCompanyUserList = async () => {
|
||||
const response = await manageService.presetusers()
|
||||
companyUserList.value = response || []
|
||||
}
|
||||
const form = reactive<any>({
|
||||
title: '',
|
||||
user_ids: [],
|
||||
leader_id: '',
|
||||
scenario_code: '',
|
||||
sort: 0,
|
||||
status: 1
|
||||
})
|
||||
const pagination = reactive({
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
total: 0
|
||||
})
|
||||
// 表格数据
|
||||
const tableData = ref<any[]>([])
|
||||
|
||||
// 表格实例引用
|
||||
const tableRef = ref()
|
||||
|
||||
onMounted(() => {
|
||||
getWorkList()
|
||||
})
|
||||
// 处理表格分页变化
|
||||
const handleSizeChange = (newPageSize: number) => {
|
||||
pagination.pageSize = newPageSize
|
||||
getWorkList()
|
||||
}
|
||||
const handleCurrentChange = (newCurrentPage: number) => {
|
||||
pagination.currentPage = newCurrentPage
|
||||
getWorkList()
|
||||
}
|
||||
// 获取列表数据
|
||||
const getWorkList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const { currentPage, pageSize } = pagination
|
||||
|
||||
const { records, current, size, total } = await userConService.getGroupList({
|
||||
current: currentPage,
|
||||
size: pageSize
|
||||
})
|
||||
tableData.value = records.map((item: any) => ({
|
||||
...item
|
||||
}))
|
||||
Object.assign(pagination, { currentPage: current, pageSize: size, total })
|
||||
} catch (error) {
|
||||
console.error('获取列表失败:', error)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const dialogType = ref('add')
|
||||
|
||||
const showDialog = (type: string, row?: any) => {
|
||||
getCompanyUserList()
|
||||
dialogVisible.value = true
|
||||
dialogType.value = type
|
||||
|
||||
if (type === 'edit' && row) {
|
||||
form.title = row.title
|
||||
form.id = row.id
|
||||
form.leader_id = row.leader_id
|
||||
form.scenario_code = row.scenario_code
|
||||
form.user_ids = row.user.map((item: any) => item.id)
|
||||
form.sort = row.sort
|
||||
console.log(row.user_ids)
|
||||
console.log(form.user_ids)
|
||||
form.status = row.status
|
||||
// form.tenant_key = row.tenant_key
|
||||
} else {
|
||||
form.title = ''
|
||||
form.id = ''
|
||||
form.scenario_code = ''
|
||||
form.sort = 0
|
||||
form.leader_id = ''
|
||||
form.user_ids = []
|
||||
form.status = 1
|
||||
// form.tenant_key = ''
|
||||
}
|
||||
}
|
||||
const deleteRole = (row?: any) => {
|
||||
ElMessageBox.confirm('确定删除吗?', '删除确认', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'error'
|
||||
}).then(async () => {
|
||||
const { message } = await userConService.deleteGroup({
|
||||
id: row.id
|
||||
})
|
||||
ElMessage.success(message)
|
||||
getWorkList()
|
||||
})
|
||||
}
|
||||
|
||||
const handleSubmit = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
if (dialogType.value === 'add') {
|
||||
const { message } = await userConService.addGroup({
|
||||
title: form.title,
|
||||
user_ids: form.user_ids,
|
||||
leader_id: form.leader_id,
|
||||
sort: form.sort,
|
||||
scenario_code: form.scenario_code,
|
||||
status: form.status
|
||||
})
|
||||
ElMessage.success(message)
|
||||
dialogVisible.value = false
|
||||
getWorkList()
|
||||
} else {
|
||||
const { message } = await userConService.editGroup(form)
|
||||
ElMessage.success(message)
|
||||
dialogVisible.value = false
|
||||
getWorkList()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.el-table__empty-block) {
|
||||
height: 100% !important;
|
||||
min-height: 200px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.page-content {
|
||||
.svg-icon {
|
||||
width: 1.8em;
|
||||
height: 1.8em;
|
||||
overflow: hidden;
|
||||
vertical-align: -8px;
|
||||
fill: currentcolor;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
+858
-181
File diff suppressed because it is too large
Load Diff
@@ -3,25 +3,46 @@
|
||||
<!-- 搜索工具栏 -->
|
||||
<div class="search-toolbar boxshadow">
|
||||
<div class="toolbar-left">
|
||||
<ElButton type="primary" v-ripple @click="handleAdd">新增车辆</ElButton>
|
||||
</div>
|
||||
<div class="toolbar-right">
|
||||
<el-input
|
||||
v-model="searchForm.keyword"
|
||||
placeholder="请输入关键字"
|
||||
clearable
|
||||
style="width: 300px"
|
||||
@keyup.enter="getList"
|
||||
>
|
||||
<template #suffix>
|
||||
<el-icon class="clickable" @click="getList"><Search /></el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
<el-form-item label="关键字:" label-width="70px">
|
||||
<el-input
|
||||
v-model="searchForm.keyword"
|
||||
placeholder="请输入关键字"
|
||||
clearable
|
||||
@keyup.enter="getList"
|
||||
>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="品牌:" label-width="60px">
|
||||
<ElSelect v-model="searchForm.brand_id" filterable placeholder="请选择品牌">
|
||||
<ElOption label="全部" value="" />
|
||||
<ElOption
|
||||
v-for="item in brandOptions"
|
||||
:key="item.brand_code || item.code"
|
||||
:label="item.brand_name || item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
</ElSelect>
|
||||
</el-form-item>
|
||||
<el-form-item label="车系:" label-width="60px">
|
||||
<ElSelect v-model="searchForm.series_id" filterable placeholder="请选择车系">
|
||||
<ElOption label="全部" value="" />
|
||||
<ElOption
|
||||
v-for="s in seriesOptions"
|
||||
:key="s.series_code || s.code"
|
||||
:label="s.series_name || s.name"
|
||||
:value="s.id"
|
||||
/>
|
||||
</ElSelect>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="getList" v-ripple>搜索</el-button>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 数据表格 -->
|
||||
<div class="table-container boxshadow">
|
||||
<ElButton type="primary" v-ripple @click="handleAdd">新增车辆</ElButton>
|
||||
<ArtTable
|
||||
ref="tableRef"
|
||||
row-key="id"
|
||||
@@ -34,12 +55,12 @@
|
||||
@current-change="handleCurrentChange"
|
||||
>
|
||||
<template #default>
|
||||
<el-table-column prop="id" label="编码" min-width="170" show-overflow-tooltip align="center">
|
||||
<!-- <el-table-column prop="id" label="编码" min-width="170" show-overflow-tooltip align="center">
|
||||
<template #default="scope">
|
||||
<div class="ellipsis" :title="scope.row.fingerprint">指纹:{{ scope.row.fingerprint }}</div>
|
||||
<div class="ellipsis" :title="scope.row.sku_code">sku_code:{{ scope.row.sku_code }}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table-column> -->
|
||||
<ElTableColumn label="车型" prop="model_name" min-width="150" align="center">
|
||||
<template #default="scope">
|
||||
<span class="primary">{{ scope.row.model_name }}</span>
|
||||
@@ -48,7 +69,7 @@
|
||||
<ElTableColumn label="品牌" prop="brand_name" min-width="100" align="center" />
|
||||
<ElTableColumn label="车系" prop="series_name" min-width="120" align="center" />
|
||||
<ElTableColumn label="年款" prop="model_year" align="center" />
|
||||
<ElTableColumn label="版型" prop="trim" align="center" />
|
||||
<!-- <ElTableColumn label="版型" prop="trim" align="center" />
|
||||
<ElTableColumn label="配置等级" prop="trim_level" align="center" />
|
||||
<ElTableColumn label="排量" prop="engine_disp_l" align="center">
|
||||
<template #default="scope">
|
||||
@@ -64,7 +85,7 @@
|
||||
<template #default="scope">
|
||||
{{ scope.row.range_km ? scope.row.range_km + 'km' : '-' }}
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
</ElTableColumn> -->
|
||||
<ElTableColumn label="指导价(CNY)" prop="msrp" align="center" min-width="100">
|
||||
<template #default="scope">
|
||||
{{ scope.row.msrp ? scope.row.msrp : '-' }}
|
||||
@@ -93,7 +114,6 @@
|
||||
v-model="dialogVisible"
|
||||
:title="dialogTitle"
|
||||
width="1200px"
|
||||
top="40px"
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
>
|
||||
@@ -117,7 +137,7 @@
|
||||
:value="item.brand_name || item.name"
|
||||
/>
|
||||
</ElSelect>
|
||||
<ElInput v-model="formData.brand_code" placeholder="品牌编码" readonly disabled />
|
||||
<!-- <ElInput v-model="formData.brand_code" placeholder="品牌编码" readonly disabled /> -->
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
<ElCol :span="8">
|
||||
@@ -130,17 +150,74 @@
|
||||
:value="s.series_name || s.name"
|
||||
/>
|
||||
</ElSelect>
|
||||
<ElInput v-model="formData.series_code" placeholder="车系编码" readonly disabled />
|
||||
<!-- <ElInput v-model="formData.series_code" placeholder="车系编码" readonly disabled /> -->
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
<ElCol :span="8">
|
||||
<ElFormItem label="车型/配置名称" prop="model_name">
|
||||
<div style="display: flex; align-items: center; gap: 8px">
|
||||
<!-- 情况1: 有关联车型数据时显示下拉选择和采集按钮 -->
|
||||
<template v-if="modelsOptions.length > 0">
|
||||
<ElSelect
|
||||
v-model="formData.model_name"
|
||||
filterable
|
||||
placeholder="请选择车型名称"
|
||||
style="flex: 1"
|
||||
:disabled="!formData.series_code"
|
||||
allow-create
|
||||
default-first-option
|
||||
@change="handleModelSelectChange"
|
||||
>
|
||||
<ElOption
|
||||
v-for="model in modelsOptions"
|
||||
:key="model.model_code || model.code"
|
||||
:label="model.model_name || model.name"
|
||||
:value="model.model_name || model.name"
|
||||
/>
|
||||
</ElSelect>
|
||||
<ElButton type="primary" size="small" @click="showCollectModelsDialog">
|
||||
采集车型
|
||||
</ElButton>
|
||||
</template>
|
||||
|
||||
<!-- 情况2: 没有车型数据时显示采集按钮 -->
|
||||
<template v-else-if="formData.series_code">
|
||||
<ElInput
|
||||
v-model="formData.model_name"
|
||||
placeholder="请输入车型名称"
|
||||
style="flex: 1"
|
||||
@input="handleModelInputChange"
|
||||
/>
|
||||
<ElButton type="primary" size="small" @click="showCollectModelsDialog">
|
||||
采集车型
|
||||
</ElButton>
|
||||
</template>
|
||||
<!-- 情况3: 没有车系时直接输入 -->
|
||||
<ElInput
|
||||
v-else
|
||||
v-model="formData.model_name"
|
||||
placeholder="请输入车型名称"
|
||||
style="flex: 1"
|
||||
@input="handleModelInputChange"
|
||||
/>
|
||||
</div>
|
||||
<!-- <ElInput
|
||||
v-model="formData.model_code"
|
||||
placeholder="车型编码"
|
||||
readonly
|
||||
disabled
|
||||
style="margin-top: 8px"
|
||||
/> -->
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
<!-- <ElCol :span="8">
|
||||
<ElFormItem label="排量(L)" prop="engine_disp_l">
|
||||
<ElInput v-model="formData.engine_disp_l" placeholder="请输入排量" />
|
||||
<el-text class="mx-1" type="warning">如:1.5;纯电为空</el-text>
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
</ElCol> -->
|
||||
</ElRow>
|
||||
<ElRow :gutter="16">
|
||||
<!-- <ElRow :gutter="16">
|
||||
<ElCol :span="8">
|
||||
<ElFormItem label="版型/细分配置" prop="trim">
|
||||
<ElSelect
|
||||
@@ -208,14 +285,9 @@
|
||||
<ElInput v-model="formData.edition_code" placeholder="版本编码" readonly disabled />
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
<ElRow :gutter="16">
|
||||
<ElCol :span="8">
|
||||
<ElFormItem label="车型/配置名称" prop="model_name">
|
||||
<ElInput v-model="formData.model_name" placeholder="请输入车型名称" />
|
||||
<el-text class="mx-1" type="warning">如:伊兰特,海狮07(不填则自动生成)</el-text>
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
</ElRow> -->
|
||||
<!-- <ElRow :gutter="16">
|
||||
|
||||
<ElCol :span="8">
|
||||
<ElFormItem label="车型/配置代码" prop="model_code">
|
||||
<ElInput v-model="formData.model_code" placeholder="请输入车型代码" />
|
||||
@@ -228,7 +300,7 @@
|
||||
<el-text class="mx-1" type="warning">如:EV多为1</el-text>
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
</ElRow> -->
|
||||
<ElRow :gutter="16">
|
||||
<ElCol :span="8">
|
||||
<ElFormItem label="官方指导价" prop="msrp">
|
||||
@@ -240,13 +312,13 @@
|
||||
<ElInput v-model="formData.model_year" placeholder="YYYY" />
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
<ElCol :span="8">
|
||||
<!-- <ElCol :span="8">
|
||||
<ElFormItem label="最大功率(kW)" prop="engine_power_kw">
|
||||
<ElInput v-model="formData.engine_power_kw" placeholder="请输入最大功率" />
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
</ElCol> -->
|
||||
</ElRow>
|
||||
<ElRow :gutter="16">
|
||||
<!-- <ElRow :gutter="16">
|
||||
<ElCol :span="8">
|
||||
<ElFormItem label="电池容量(kWh)" prop="battery_kwh">
|
||||
<ElInput v-model="formData.battery_kwh" placeholder="请输入电池容量" />
|
||||
@@ -262,8 +334,8 @@
|
||||
<ElInput v-model="formData.doors" placeholder="请输入门数" />
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
<ElRow :gutter="16">
|
||||
</ElRow> -->
|
||||
<!-- <ElRow :gutter="16">
|
||||
<ElCol :span="8">
|
||||
<ElFormItem label="是否涡轮" prop="engine_turbo">
|
||||
<ElSelect v-model="formData.engine_turbo" placeholder="请选择">
|
||||
@@ -282,8 +354,8 @@
|
||||
<ElInput v-model="formData.seats" placeholder="请输入座位数" />
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
<ElRow :gutter="16">
|
||||
</ElRow> -->
|
||||
<!-- <ElRow :gutter="16"> -->
|
||||
<!-- <ElCol :span="8">
|
||||
<ElFormItem label="原产国" prop="origin_country">
|
||||
<ElSelect v-model="formData.origin_country" placeholder="请选择原产国" filterable>
|
||||
@@ -304,7 +376,7 @@
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
</ElCol> -->
|
||||
<ElCol :span="8">
|
||||
<!-- <ElCol :span="8">
|
||||
<ElFormItem label="外观颜色" prop="color_exterior_text">
|
||||
<ElSelect
|
||||
v-model="formData.color_exterior_text"
|
||||
@@ -327,8 +399,8 @@
|
||||
disabled
|
||||
/>
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
<ElCol :span="8">
|
||||
</ElCol> -->
|
||||
<!-- <ElCol :span="8">
|
||||
<ElFormItem label="内饰颜色" prop="color_interior_text">
|
||||
<ElSelect
|
||||
v-model="formData.color_interior_text"
|
||||
@@ -370,10 +442,10 @@
|
||||
disabled
|
||||
/>
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
</ElCol> -->
|
||||
<!-- </ElRow> -->
|
||||
<!-- <el-divider /> -->
|
||||
<ElRow :gutter="16">
|
||||
<!-- <ElRow :gutter="16">
|
||||
<ElCol :span="8">
|
||||
<ElFormItem label="驱动类型" prop="drive_type">
|
||||
<ElSelect v-model="formData.drive_type" placeholder="请选择驱动类型" clearable>
|
||||
@@ -494,7 +566,7 @@
|
||||
/>
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
</ElRow> -->
|
||||
<ElRow :gutter="16">
|
||||
<ElCol :span="8">
|
||||
<ElFormItem label="状态" prop="is_active">
|
||||
@@ -852,18 +924,83 @@
|
||||
</div>
|
||||
</div>
|
||||
</ElDialog>
|
||||
|
||||
<!-- 车型采集弹出层 -->
|
||||
<ElDialog
|
||||
v-model="collectModelsDialogVisible"
|
||||
:title="`${currentCollectSeries?.series_name} - 车型采集`"
|
||||
width="900px"
|
||||
align-center
|
||||
>
|
||||
<div class="collect-models-container">
|
||||
<div class="collect-models-header">
|
||||
<div
|
||||
style="
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 14px;
|
||||
"
|
||||
>
|
||||
请选择一个车型
|
||||
</div>
|
||||
</div>
|
||||
<div class="collect-models-content">
|
||||
<div v-if="collectModelsLoading" class="loading-container">
|
||||
<ElLoading text="正在采集车型数据..." />
|
||||
</div>
|
||||
<div v-else-if="collectModelsData.length === 0" class="empty-container">
|
||||
<ElEmpty description="暂无车型数据" />
|
||||
</div>
|
||||
<div v-else class="models-table-container">
|
||||
<ElTable :data="collectModelsData" style="width: 100%" max-height="400">
|
||||
<ElTableColumn width="60" align="center">
|
||||
<template #default="scope">
|
||||
<ElRadio
|
||||
:model-value="selectedModelId"
|
||||
:value="scope.row.car_id || scope.row.title"
|
||||
@change="selectModel(scope.row.car_id || scope.row.title)"
|
||||
/>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn prop="brand_name" label="品牌" width="100" />
|
||||
<ElTableColumn prop="series_name" label="车系" width="120" />
|
||||
<ElTableColumn prop="title" label="车型名称" min-width="200" show-overflow-tooltip />
|
||||
<ElTableColumn prop="year" label="年款" width="80" />
|
||||
<ElTableColumn prop="msrp" label="指导价" width="120">
|
||||
<template #default="scope">
|
||||
<span v-if="scope.row.msrp" class="price-text">
|
||||
¥{{ formatPrice(scope.row.msrp) }}
|
||||
</span>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
</ElTable>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<ElButton @click="collectModelsDialogVisible = false">取消</ElButton>
|
||||
<ElButton type="primary" @click="saveCollectModels" :disabled="!selectedModelId">
|
||||
保存车型
|
||||
</ElButton>
|
||||
</div>
|
||||
</template>
|
||||
</ElDialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { Search } from '@element-plus/icons-vue'
|
||||
import { vehicleService } from '@/api/vehicleApi'
|
||||
import { commonService } from '@/api/commonApi'
|
||||
defineOptions({ name: 'AdminVehicleList' })
|
||||
// 搜索表单
|
||||
const searchForm = reactive({
|
||||
keyword: ''
|
||||
keyword: '',
|
||||
brand_id: '',
|
||||
series_id: ''
|
||||
})
|
||||
|
||||
// 分页
|
||||
@@ -947,6 +1084,16 @@
|
||||
// 车系选项
|
||||
const seriesOptions = ref<Array<any>>([])
|
||||
|
||||
// 车型选项
|
||||
const modelsOptions = ref<Array<any>>([])
|
||||
|
||||
// 车型采集弹出层相关
|
||||
const collectModelsDialogVisible = ref(false)
|
||||
const currentCollectSeries = ref<any | null>(null)
|
||||
const collectModelsLoading = ref(false)
|
||||
const collectModelsData = ref<Array<any>>([])
|
||||
const selectedModelId = ref<string>('')
|
||||
|
||||
// 外观颜色
|
||||
const wcolorOptions = ref<Array<any>>([])
|
||||
// 内饰颜色
|
||||
@@ -992,6 +1139,30 @@
|
||||
ElMessage.error('获取车系列表失败')
|
||||
}
|
||||
}
|
||||
|
||||
// 获取车型列表
|
||||
const loadModelsOptions = async (seriesCode?: string) => {
|
||||
if (!seriesCode) {
|
||||
modelsOptions.value = []
|
||||
return
|
||||
}
|
||||
try {
|
||||
const matched = seriesOptions.value.find((s: any) => s.series_code === seriesCode)
|
||||
const seriesId = matched ? matched.id : ''
|
||||
if (!seriesId) {
|
||||
modelsOptions.value = []
|
||||
return
|
||||
}
|
||||
const response = await commonService.getModelsSelect({
|
||||
series_id: seriesId
|
||||
})
|
||||
modelsOptions.value = response
|
||||
} catch (error) {
|
||||
console.error('获取车型列表失败:', error)
|
||||
ElMessage.error('获取车型列表失败')
|
||||
}
|
||||
}
|
||||
|
||||
//颜色下拉列表
|
||||
const loadColorOptions = async () => {
|
||||
try {
|
||||
@@ -1036,7 +1207,9 @@
|
||||
const params = {
|
||||
current: currentPage,
|
||||
size: pageSize,
|
||||
keyword: searchForm.keyword
|
||||
keyword: searchForm.keyword,
|
||||
brand_id: searchForm.brand_id,
|
||||
series_id: searchForm.series_id
|
||||
}
|
||||
|
||||
const { records, current, size, total } = await vehicleService.getVehicleList(params)
|
||||
@@ -1062,22 +1235,122 @@
|
||||
}
|
||||
|
||||
// 新增车辆
|
||||
const handleAdd = () => {
|
||||
loadBrandOptions()
|
||||
loadColorOptions()
|
||||
loadDictOptions()
|
||||
const handleAdd = async () => {
|
||||
dialogTitle.value = '新增车辆'
|
||||
resetForm()
|
||||
await loadBrandOptions()
|
||||
loadColorOptions()
|
||||
loadDictOptions()
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
// 编辑车辆
|
||||
const handleEdit = (row: any) => {
|
||||
loadBrandOptions()
|
||||
const handleEdit = async (row: any) => {
|
||||
loadColorOptions()
|
||||
loadDictOptions()
|
||||
dialogTitle.value = '编辑车辆'
|
||||
|
||||
// 先清空表单和选项数据,避免监听器干扰
|
||||
resetForm()
|
||||
|
||||
// 使用 nextTick 确保 DOM 更新后再设置数据
|
||||
await nextTick()
|
||||
|
||||
// 设置表单数据
|
||||
Object.assign(formData, row)
|
||||
|
||||
// 立即检查车系名称是否被清空并重新设置
|
||||
setTimeout(() => {
|
||||
if (!formData.series_name) {
|
||||
formData.series_name = row.series_name
|
||||
}
|
||||
}, 1)
|
||||
|
||||
// 先加载品牌选项
|
||||
await loadBrandOptions()
|
||||
|
||||
// 如果品牌已选择,加载车系选项
|
||||
if (row.brand_code) {
|
||||
await loadSeriesOptions(row.brand_code)
|
||||
} else if (row.brand_name) {
|
||||
// 如果只有品牌名称没有品牌代码,尝试匹配品牌代码
|
||||
const matchedBrand = brandOptions.value.find(
|
||||
(b: any) => b.brand_name === row.brand_name || b.name === row.brand_name
|
||||
)
|
||||
if (matchedBrand && matchedBrand.brand_code) {
|
||||
await loadSeriesOptions(matchedBrand.brand_code)
|
||||
}
|
||||
}
|
||||
|
||||
// 如果车系已选择,加载车型选项
|
||||
if (row.series_code) {
|
||||
// 先尝试用原始的车系代码加载
|
||||
await loadModelsOptions(row.series_code)
|
||||
|
||||
// 如果车型选项为空,尝试用车系名称匹配的车系代码
|
||||
if (modelsOptions.value.length === 0 && row.series_name) {
|
||||
const matchedSeries = seriesOptions.value.find(
|
||||
(s: any) => s.series_name === row.series_name || s.name === row.series_name
|
||||
)
|
||||
if (matchedSeries && matchedSeries.series_code) {
|
||||
await loadModelsOptions(matchedSeries.series_code)
|
||||
}
|
||||
}
|
||||
} else if (row.series_name) {
|
||||
// 如果只有车系名称没有车系代码,尝试匹配车系代码
|
||||
const matchedSeries = seriesOptions.value.find(
|
||||
(s: any) => s.series_name === row.series_name || s.name === row.series_name
|
||||
)
|
||||
if (matchedSeries && matchedSeries.series_code) {
|
||||
await loadModelsOptions(matchedSeries.series_code)
|
||||
}
|
||||
}
|
||||
|
||||
// 如果仍然没有车型选项,但有车系信息,尝试再次加载
|
||||
if (modelsOptions.value.length === 0 && (row.series_code || row.series_name)) {
|
||||
// 使用当前表单中的车系代码重新加载
|
||||
if (formData.series_code) {
|
||||
await loadModelsOptions(formData.series_code)
|
||||
}
|
||||
}
|
||||
|
||||
// 确保车系代码与车系名称匹配(在选项数据加载完成后)
|
||||
// 如果车系名称被清空,重新设置
|
||||
if (!formData.series_name && row.series_name) {
|
||||
formData.series_name = row.series_name
|
||||
}
|
||||
|
||||
if (formData.series_name && seriesOptions.value.length > 0) {
|
||||
const matchedSeries = seriesOptions.value.find(
|
||||
(s: any) => s.series_name === formData.series_name || s.name === formData.series_name
|
||||
)
|
||||
|
||||
if (matchedSeries && matchedSeries.series_code) {
|
||||
formData.series_code = matchedSeries.series_code
|
||||
}
|
||||
}
|
||||
|
||||
// 确保车型信息正确回显(在选项数据加载完成后)
|
||||
if (formData.model_name && modelsOptions.value.length > 0) {
|
||||
const matchedModel = modelsOptions.value.find(
|
||||
(m: any) => m.model_name === formData.model_name || m.name === formData.model_name
|
||||
)
|
||||
if (matchedModel) {
|
||||
// 如果找到匹配的车型,更新车型代码、年款和指导价
|
||||
formData.model_code = matchedModel.model_code || matchedModel.code || formData.model_code
|
||||
formData.model_year = matchedModel.model_year || matchedModel.year || formData.model_year
|
||||
formData.msrp = matchedModel.msrp || formData.msrp
|
||||
}
|
||||
}
|
||||
|
||||
// 强制触发一次车系名称监听器(在回显逻辑完成后)
|
||||
const currentSeriesName = formData.series_name
|
||||
if (currentSeriesName) {
|
||||
formData.series_name = ''
|
||||
await nextTick()
|
||||
formData.series_name = currentSeriesName
|
||||
}
|
||||
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
@@ -1128,14 +1401,19 @@
|
||||
const resetForm = () => {
|
||||
Object.keys(formData).forEach((key) => {
|
||||
if (key === 'is_active') {
|
||||
(formData as any)[key] = 1
|
||||
;(formData as any)[key] = 1
|
||||
} else if (key === 'currency') {
|
||||
(formData as any)[key] = 'CNY'
|
||||
;(formData as any)[key] = 'CNY'
|
||||
} else {
|
||||
(formData as any)[key] = ''
|
||||
;(formData as any)[key] = ''
|
||||
}
|
||||
})
|
||||
formRef.value?.clearValidate()
|
||||
|
||||
// 重置选项数据
|
||||
modelsOptions.value = []
|
||||
seriesOptions.value = []
|
||||
brandOptions.value = []
|
||||
}
|
||||
|
||||
// 提交表单
|
||||
@@ -1169,14 +1447,120 @@
|
||||
() => searchForm.keyword,
|
||||
() => {
|
||||
pagination.currentPage = 1
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
// 车型采集相关方法
|
||||
const showCollectModelsDialog = () => {
|
||||
if (!formData.series_code) {
|
||||
ElMessage.warning('请先选择车系')
|
||||
return
|
||||
}
|
||||
|
||||
const matched = seriesOptions.value.find((s: any) => s.series_code === formData.series_code)
|
||||
if (!matched) {
|
||||
ElMessage.warning('未找到对应的车系信息')
|
||||
return
|
||||
}
|
||||
|
||||
currentCollectSeries.value = matched
|
||||
collectModelsDialogVisible.value = true
|
||||
collectModelsData.value = []
|
||||
selectedModelId.value = ''
|
||||
|
||||
// 调用车型采集
|
||||
collectVehicleModels(matched)
|
||||
}
|
||||
|
||||
const collectVehicleModels = async (series: any) => {
|
||||
try {
|
||||
collectModelsLoading.value = true
|
||||
|
||||
// 调用后端接口获取车型数据
|
||||
const response = await vehicleService.collectVehicleModels({ brand_series_id: series.id })
|
||||
|
||||
// 后端返回的数据格式为数组
|
||||
collectModelsData.value = response || []
|
||||
selectedModelId.value = ''
|
||||
|
||||
ElMessage.success(`成功采集到 ${collectModelsData.value.length} 个车型`)
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error.message || '车型采集失败')
|
||||
} finally {
|
||||
collectModelsLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 选择车型
|
||||
const selectModel = (modelId: string) => {
|
||||
selectedModelId.value = modelId
|
||||
}
|
||||
|
||||
const saveCollectModels = async () => {
|
||||
if (!currentCollectSeries.value || !selectedModelId.value) {
|
||||
ElMessage.warning('请选择一个车型')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
// 找到选中的车型数据
|
||||
const selectedModel = collectModelsData.value.find(
|
||||
(model: any) => (model.car_id || model.title) === selectedModelId.value
|
||||
)
|
||||
|
||||
if (!selectedModel) {
|
||||
ElMessage.warning('未找到选中的车型数据')
|
||||
return
|
||||
}
|
||||
|
||||
await vehicleService.saveVehicleModels({
|
||||
series_id: currentCollectSeries.value.id,
|
||||
models: [selectedModel]
|
||||
})
|
||||
|
||||
ElMessage.success('成功保存车型')
|
||||
collectModelsDialogVisible.value = false
|
||||
|
||||
// 将选中的车型信息回显到表单
|
||||
formData.model_name = selectedModel.title || selectedModel.model_name || ''
|
||||
formData.model_code = selectedModel.car_id || selectedModel.model_code || ''
|
||||
formData.model_year = selectedModel.year || ''
|
||||
formData.msrp = selectedModel.msrp || ''
|
||||
// 刷新车型选项
|
||||
await loadModelsOptions(formData.series_code)
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error.message || '保存车型失败')
|
||||
}
|
||||
}
|
||||
|
||||
// 格式化价格显示
|
||||
const formatPrice = (price: number | string) => {
|
||||
if (!price) return '0'
|
||||
const numPrice = typeof price === 'string' ? parseFloat(price) : price
|
||||
return numPrice.toLocaleString()
|
||||
}
|
||||
|
||||
// 页面加载时获取数据
|
||||
onMounted(async () => {
|
||||
loadBrandOptions()
|
||||
await getList()
|
||||
})
|
||||
|
||||
// 品牌名称变更时,联动设置品牌代码
|
||||
watch(
|
||||
() => searchForm.brand_id,
|
||||
(newId) => {
|
||||
const matched = brandOptions.value.find((b: any) => b.id === newId)
|
||||
const brand_code = matched ? matched.brand_code : ''
|
||||
// 品牌变更后,清空车系相关字段并加载对应车系
|
||||
searchForm.series_id = ''
|
||||
if (brand_code) {
|
||||
loadSeriesOptions(brand_code)
|
||||
} else {
|
||||
seriesOptions.value = []
|
||||
}
|
||||
}
|
||||
)
|
||||
watch(
|
||||
() => formData.brand_name,
|
||||
(newName) => {
|
||||
@@ -1196,14 +1580,71 @@
|
||||
// 车系名称变更时,联动设置车系代码
|
||||
watch(
|
||||
() => formData.series_name,
|
||||
(newSeriesName) => {
|
||||
(newSeriesName, oldSeriesName) => {
|
||||
if (!newSeriesName) return
|
||||
|
||||
const matchedSeries = seriesOptions.value.find(
|
||||
(s: any) => s.series_name === newSeriesName || s.name === newSeriesName
|
||||
)
|
||||
formData.series_code = matchedSeries ? matchedSeries.series_code || matchedSeries.code : ''
|
||||
|
||||
if (matchedSeries) {
|
||||
formData.series_code = matchedSeries.series_code || matchedSeries.code || ''
|
||||
|
||||
// 只有在车系真正发生变化时才清空车型相关字段
|
||||
if (oldSeriesName && oldSeriesName !== newSeriesName) {
|
||||
formData.model_name = ''
|
||||
formData.model_code = ''
|
||||
}
|
||||
|
||||
if (formData.series_code) {
|
||||
loadModelsOptions(formData.series_code)
|
||||
} else {
|
||||
modelsOptions.value = []
|
||||
}
|
||||
} else {
|
||||
// 如果找不到匹配的车系,清空车系代码
|
||||
formData.series_code = ''
|
||||
modelsOptions.value = []
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
// 车型名称变更时,联动设置车型代码
|
||||
watch(
|
||||
() => formData.model_name,
|
||||
(newModelName) => {
|
||||
const matchedModel = modelsOptions.value.find(
|
||||
(m: any) => m.model_name === newModelName || m.name === newModelName
|
||||
)
|
||||
formData.model_code = matchedModel ? matchedModel.model_code || matchedModel.code : ''
|
||||
}
|
||||
)
|
||||
|
||||
// 处理车型下拉选择变化
|
||||
const handleModelSelectChange = (value: string) => {
|
||||
// 如果是新创建的值(不在现有选项中),清空车型代码
|
||||
const matchedModel = modelsOptions.value.find(
|
||||
(m: any) => m.model_name === value || m.name === value
|
||||
)
|
||||
if (!matchedModel) {
|
||||
formData.model_code = ''
|
||||
// 清空年款和指导价
|
||||
formData.model_year = ''
|
||||
formData.msrp = ''
|
||||
} else {
|
||||
// 如果找到匹配的车型,更新车型代码、年款和指导价
|
||||
formData.model_code = matchedModel.model_code || matchedModel.code || ''
|
||||
formData.model_year = matchedModel.model_year || matchedModel.year || ''
|
||||
formData.msrp = matchedModel.msrp || ''
|
||||
}
|
||||
}
|
||||
|
||||
// 处理车型直接输入变化
|
||||
const handleModelInputChange = () => {
|
||||
// 直接输入时清空车型代码,让后端自动生成
|
||||
formData.model_code = ''
|
||||
}
|
||||
|
||||
// 外观颜色变更时,联动设置外观颜色代码
|
||||
watch(
|
||||
() => formData.color_exterior_text,
|
||||
@@ -1353,11 +1794,6 @@
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.toolbar-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
@@ -1423,4 +1859,42 @@
|
||||
white-space: nowrap;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.collect-models-container {
|
||||
background: transparent;
|
||||
|
||||
.collect-models-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 15px;
|
||||
border-bottom: 1px solid var(--el-border-color-lighter);
|
||||
}
|
||||
|
||||
.collect-models-content {
|
||||
min-height: 400px;
|
||||
|
||||
.loading-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
.empty-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
.models-table-container {
|
||||
.price-text {
|
||||
color: var(--el-color-danger);
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -36,11 +36,11 @@
|
||||
<div class="form">
|
||||
<!-- 登录方式切换 tab -->
|
||||
<div class="login-type-tabs">
|
||||
<span
|
||||
<!-- <span
|
||||
:class="{ active: activeLoginType === 'password' }"
|
||||
@click="activeLoginType = 'password'"
|
||||
>账号密码登录</span
|
||||
>
|
||||
> -->
|
||||
<span
|
||||
:class="{ active: activeLoginType === 'qrcode' }"
|
||||
@click="activeLoginType = 'qrcode'"
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
<template>
|
||||
<div class="stocking-show-page">
|
||||
<div class="boxshadow">
|
||||
<OrderDetailContent :orderData="orderDialog.orderData" />
|
||||
<div v-if="flow_type == 'stock_orders' || flow_type == 'stock_orders_nullify'">
|
||||
<OrderDetailContent :orderData="orderDialog.orderData" />
|
||||
</div>
|
||||
<div v-if="flow_type == 'purchase_orders'">
|
||||
<ProcurementDetailView :detailData="orderDialog.orderData" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- 上传合同 -->
|
||||
<div class="boxshadow">
|
||||
<div class="form-title">
|
||||
<span class="border"></span>
|
||||
<span class="title">合同</span>
|
||||
<span class="title">上传合同</span>
|
||||
</div>
|
||||
<div class="file-preview">
|
||||
<div v-if="contractFile" class="contract-file">
|
||||
@@ -99,6 +104,9 @@
|
||||
<ElButton type="primary" size="large" class="submit-btn" @click="handleSubmit('approve')"
|
||||
>同意</ElButton
|
||||
>
|
||||
<!-- <ElButton type="warning" size="large" class="submit-btn" @click="handleTransfer()"
|
||||
>转交</ElButton
|
||||
> -->
|
||||
</div>
|
||||
</div>
|
||||
<!-- PDF 预览弹窗 -->
|
||||
@@ -122,37 +130,105 @@
|
||||
/>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<!-- 转交弹出框 -->
|
||||
<el-dialog v-model="zjDialogVisible" title="转交" width="450px">
|
||||
<el-form :model="zjForm" label-width="80px">
|
||||
<el-form-item label="转交人" prop="to_uid">
|
||||
<el-select
|
||||
v-model="zjForm.to_uid"
|
||||
filterable
|
||||
placeholder="请选择转交人"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in userOptions"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<ElFormItem label="转交原因" prop="reason">
|
||||
<ElInput v-model="zjForm.reason" type="textarea" :rows="3" placeholder="请输入转交原因" />
|
||||
</ElFormItem>
|
||||
<div style="display: flex; justify-content: space-around; margin-top: 24px">
|
||||
<el-button @click="zjDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="handlezjSubmit">确定</el-button>
|
||||
</div>
|
||||
</el-form>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { CrmService } from '@/api/crmApi'
|
||||
import { commonService } from '@/api/commonApi'
|
||||
// import { manageService } from '@/api/adminApi'
|
||||
import { manageService } from '@/api/adminApi'
|
||||
import { CrmService } from '@/api/crmApi'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { usePdfPreview } from '@/composables/usePdfPreview'
|
||||
import { router } from '@/router'
|
||||
import OrderDetailContent from '@/components/custom/OrderDetailContent.vue'
|
||||
import ProcurementDetailView from '@/components/custom/ProcurementDetailView.vue'
|
||||
import type { OrderData } from '@/composables/useOrderDetailDialog'
|
||||
|
||||
defineOptions({ name: 'CrmApprovalDetail' }) // 定义组件名称,用于 KeepAlive 缓存控制
|
||||
|
||||
import { router } from '@/router'
|
||||
defineOptions({ name: 'CrmApprovalDetail' })
|
||||
const { pdf, frameRef, download } = usePdfPreview()
|
||||
const zjDialogVisible = ref(false)
|
||||
const route = useRoute()
|
||||
const approval_id = route.query.id
|
||||
const stocking_id = route.query.biz_id
|
||||
const instance_id = route.query.instance_id
|
||||
const step_index = route.query.step_index
|
||||
const status = route.query.status
|
||||
const flow_type = route.query.flow_type
|
||||
const approvalNotes = ref('')
|
||||
const orderDialog = reactive({
|
||||
orderData: {} as OrderData
|
||||
})
|
||||
// const approval_id = route.query.id
|
||||
const status = route.query.status
|
||||
const stocking_id = route.query.biz_id
|
||||
const instance_id = route.query.instance_id
|
||||
const stockingInfo = ref<any>({})
|
||||
const approvalNotes = ref('')
|
||||
const approvalAction = ref('')
|
||||
const userOptions = ref<any>()
|
||||
const getuserList = async () => {
|
||||
// 这里调用获取用户列表的接口
|
||||
const res = await manageService.presetusers()
|
||||
userOptions.value = res || []
|
||||
// return res || []
|
||||
}
|
||||
const zjForm = reactive({
|
||||
to_uid: '',
|
||||
instance_id: '',
|
||||
step_index: '',
|
||||
reason: ''
|
||||
})
|
||||
// 转交操作
|
||||
const handleTransfer = async () => {
|
||||
getuserList()
|
||||
zjDialogVisible.value = true
|
||||
}
|
||||
const handlezjSubmit = async () => {
|
||||
if (!zjForm.to_uid) {
|
||||
ElMessage.warning('请选择转交人')
|
||||
return
|
||||
}
|
||||
if (!zjForm.reason) {
|
||||
ElMessage.warning('请输入转交原因')
|
||||
return
|
||||
}
|
||||
try {
|
||||
const zjForms = {
|
||||
to_uid: zjForm.to_uid,
|
||||
instance_id: instance_id,
|
||||
step_index: step_index,
|
||||
reason: zjForm.reason
|
||||
}
|
||||
const { message } = await CrmService.stockapprovaltransfer(zjForms)
|
||||
ElMessage.success(message)
|
||||
router.push('/admin/approval/stocking_auth')
|
||||
} catch (error) {
|
||||
console.error('转交失败:', error)
|
||||
// ElMessage.error('转交失败')
|
||||
}
|
||||
}
|
||||
const handleSubmit = async (approvalAction: string) => {
|
||||
try {
|
||||
// 这里调用审批API
|
||||
const { message } = await CrmService.stockapprovalpass({
|
||||
instance_id: instance_id,
|
||||
action: approvalAction,
|
||||
@@ -174,14 +250,38 @@
|
||||
|
||||
// 合同文件
|
||||
const contractFile = ref<{ name: string; url: string; file_name: string } | null>(null)
|
||||
|
||||
|
||||
function extractFileName(pathOrUrl: string | undefined | null): string {
|
||||
if (!pathOrUrl) return '合同文件'
|
||||
try {
|
||||
const clean = String(pathOrUrl)
|
||||
const afterSlash = clean.split(/[\\/]/).pop() || '合同文件'
|
||||
return decodeURIComponent(afterSlash)
|
||||
} catch {
|
||||
return '合同文件'
|
||||
}
|
||||
}
|
||||
//获取详情并映射到回显模型
|
||||
const getdetilfn = async () => {
|
||||
const res = await CrmService.checkStock({ id: stocking_id })
|
||||
const orderData: OrderData = res
|
||||
orderDialog.orderData = orderData
|
||||
stockingInfo.value = res
|
||||
const res = await CrmService.stockapprovaldetailborder({
|
||||
id: approval_id,
|
||||
flow_type: flow_type
|
||||
})
|
||||
if (flow_type == 'stock_orders') {
|
||||
const orderData: OrderData = res
|
||||
orderDialog.orderData = orderData
|
||||
} else if (flow_type == 'purchase_orders') {
|
||||
const orderData: OrderData = res
|
||||
const urls = res.contract_file_url
|
||||
const paths = res.contract_file
|
||||
const list: Array<{ name: string; url: string }> = []
|
||||
const nameFromPath = (p: string) => extractFileName(p)
|
||||
urls.forEach((u: string, i: number) => {
|
||||
const p = paths[i] || ''
|
||||
list.push({ name: p ? nameFromPath(p) : extractFileName(u), url: u })
|
||||
})
|
||||
orderData.contract_file_url = list
|
||||
orderDialog.orderData = orderData
|
||||
}
|
||||
// 合同文件
|
||||
if (res?.contract) {
|
||||
contractFile.value = {
|
||||
@@ -447,8 +547,10 @@
|
||||
}
|
||||
.submit-wrap {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
justify-content: space-around;
|
||||
padding: 24px 0 8px;
|
||||
width: 500px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
/* 审批记录 时间线 */
|
||||
@@ -538,17 +640,4 @@
|
||||
color: #999;
|
||||
padding: 8px 0 4px 4px;
|
||||
}
|
||||
.submit-wrap {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
padding: 24px 0 8px;
|
||||
width: 500px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
width: 100px;
|
||||
height: 44px;
|
||||
font-size: 16px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
<template>
|
||||
<div class="stocking-page">
|
||||
<div class="stocking-toolbar boxshadow">
|
||||
<!-- <div class="toolbar-left">
|
||||
<ElButton type="primary" v-ripple @click="handleCreate">创建备货单</ElButton>
|
||||
</div> -->
|
||||
<div class="toolbar-right">
|
||||
<el-input
|
||||
v-model="keyword"
|
||||
@@ -32,14 +29,25 @@
|
||||
@current-change="handleCurrentChange"
|
||||
>
|
||||
<template #default>
|
||||
<ElTableColumn label="审批单名称" prop="flows_name" min-width="140" />
|
||||
<ElTableColumn label="审批单名称" prop="flows_name" min-width="140">
|
||||
<template #default="scope">
|
||||
<span :style="{ color: getFlowTypeColor(scope.row.flow_type) }">
|
||||
{{ scope.row.flows_name }}
|
||||
</span>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="审批说明" prop="biz_desc" min-width="160" />
|
||||
<ElTableColumn label="审批结果" prop="action_desc" min-width="160" />
|
||||
<ElTableColumn label="提交人" prop="initiator_name" min-width="120" />
|
||||
<ElTableColumn label="提交人" prop="initiator_name" min-width="120">
|
||||
<template #default="scope">
|
||||
<div style="display: flex; align-items: center">
|
||||
<img class="avatar" :src="scope.row.initiator_avatar" alt="" />
|
||||
<p>{{ scope.row.initiator_name }}</p>
|
||||
</div>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="审核人" prop="approver_name" min-width="120" />
|
||||
<ElTableColumn label="审批时间" prop="approved_at" min-width="120" />
|
||||
<ElTableColumn label="状态" prop="status_desc" min-width="120" />
|
||||
<ElTableColumn label="创建时间" prop="created_at" min-width="120" />
|
||||
<ElTableColumn label="提交时间" prop="created_at" min-width="120" />
|
||||
<ElTableColumn label="操作" fixed="right" min-width="140" align="center">
|
||||
<template #default="scope">
|
||||
<ElRow :gutter="10" justify="center">
|
||||
@@ -69,7 +77,7 @@
|
||||
<!-- 快速审批弹出框 -->
|
||||
<el-dialog
|
||||
v-model="approvalDialogVisible"
|
||||
title="合塑实业-汽车事业部-备货单(客户下定)"
|
||||
:title="orderDialog.title"
|
||||
width="80%"
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
@@ -77,18 +85,25 @@
|
||||
>
|
||||
<div class="approval-content">
|
||||
<!-- 头部信息 -->
|
||||
<OrderDetailContent :orderData="orderDialog.orderData" />
|
||||
<div
|
||||
v-if="orderDialog.type == 'stock_orders' || orderDialog.type == 'stock_orders_nullify'"
|
||||
>
|
||||
<OrderDetailContent :orderData="orderDialog.orderData" />
|
||||
</div>
|
||||
<div v-if="orderDialog.type == 'purchase_orders'">
|
||||
<ProcurementDetailView :detailData="orderDialog.orderData" />
|
||||
</div>
|
||||
<!-- 审批备注 -->
|
||||
<div class="info-section">
|
||||
<ElRow :gutter="12">
|
||||
<ElCol :span="24">
|
||||
<el-input
|
||||
v-model="approvalNotes"
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
placeholder="备注"
|
||||
class="approval-notes"
|
||||
/>
|
||||
v-model="approvalNotes"
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
placeholder="备注"
|
||||
class="approval-notes"
|
||||
/>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
</div>
|
||||
@@ -96,39 +111,107 @@
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<ElButton type="primary" plain size="large" class="submit-btn" @click="handleApprove('reject')"
|
||||
<ElButton
|
||||
type="primary"
|
||||
plain
|
||||
size="large"
|
||||
class="submit-btn"
|
||||
@click="handleApprove('reject')"
|
||||
>驳回</ElButton
|
||||
>
|
||||
<ElButton type="primary" size="large" class="submit-btn" @click="handleApprove('approve')"
|
||||
>同意</ElButton
|
||||
>
|
||||
<!-- <ElButton type="warning" size="large" class="submit-btn" @click="handleTransfer()"
|
||||
>转交</ElButton
|
||||
> -->
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<!-- 转交弹出框 -->
|
||||
<el-dialog v-model="zjDialogVisible" title="转交" width="450px">
|
||||
<el-form :model="zjForm" label-width="80px">
|
||||
<el-form-item label="转交人" prop="to_uid">
|
||||
<el-select
|
||||
v-model="zjForm.to_uid"
|
||||
filterable
|
||||
placeholder="请选择转交人"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in userOptions"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<ElFormItem label="转交原因" prop="reason">
|
||||
<ElInput v-model="zjForm.reason" type="textarea" :rows="3" placeholder="请输入转交原因" />
|
||||
</ElFormItem>
|
||||
<div style="display: flex; justify-content: space-around; margin-top: 24px">
|
||||
<el-button @click="zjDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="handlezjSubmit">确定</el-button>
|
||||
</div>
|
||||
</el-form>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { router } from '@/router'
|
||||
// import { manageService } from '@/api/adminApi'
|
||||
import { manageService } from '@/api/adminApi'
|
||||
import { useAuth } from '@/composables/useAuth'
|
||||
import { CrmService } from '@/api/crmApi'
|
||||
import { commonService } from '@/api/commonApi'
|
||||
import OrderDetailContent from '@/components/custom/OrderDetailContent.vue'
|
||||
import type { OrderData } from '@/composables/useOrderDetailDialog'
|
||||
|
||||
defineOptions({ name: 'CrmStockingAuth' }) // 定义组件名称,用于 KeepAlive 缓存控制
|
||||
|
||||
const { hasAuth } = useAuth()
|
||||
import ProcurementDetailView from '@/components/custom/ProcurementDetailView.vue'
|
||||
defineOptions({ name: 'CrmStockingAuth' })
|
||||
const zjDialogVisible = ref(false)
|
||||
const orderDialog = reactive({
|
||||
orderData: {} as OrderData
|
||||
orderData: {} as OrderData,
|
||||
title: '',
|
||||
type: ''
|
||||
})
|
||||
const { hasAuth } = useAuth()
|
||||
const loading = ref(false)
|
||||
const keyword = ref('')
|
||||
const tableRef = ref()
|
||||
const tableData = ref<any[]>([])
|
||||
const pagination = reactive({ currentPage: 1, pageSize: 10, total: 0 })
|
||||
const userOptions = ref<any>()
|
||||
const getuserList = async () => {
|
||||
// 这里调用获取用户列表的接口
|
||||
const res = await manageService.presetusers()
|
||||
userOptions.value = res || []
|
||||
// return res || []
|
||||
}
|
||||
|
||||
// 根据 flow_type 获取审批单名称的字体颜色
|
||||
const getFlowTypeColor = (flowType: string): string => {
|
||||
switch (flowType) {
|
||||
case 'stock_orders':
|
||||
return '#409EFF' // 蓝色 - 备货单
|
||||
case 'stock_orders_nullify':
|
||||
return '#F56C6C' // 红色 - 备货单作废
|
||||
case 'purchase_orders':
|
||||
return '#67C23A' // 绿色 - 采购单
|
||||
case 'purchase_orders_nullify':
|
||||
return '#E6A23C' // 橙色 - 采购单作废
|
||||
case 'return_orders':
|
||||
return '#909399' // 灰色 - 退货单
|
||||
case 'refund_orders':
|
||||
return '#9C27B0' // 紫色 - 退款单
|
||||
default:
|
||||
return '#303133' // 默认黑色
|
||||
}
|
||||
}
|
||||
const zjForm = reactive({
|
||||
to_uid: '',
|
||||
instance_id: '',
|
||||
step_index: '',
|
||||
reason: ''
|
||||
})
|
||||
// 审批弹出框相关
|
||||
const approvalDialogVisible = ref(false)
|
||||
|
||||
@@ -174,21 +257,68 @@
|
||||
const handleView = (row: any) => {
|
||||
router.push({
|
||||
path: '/crmpc/approval/auth_detil',
|
||||
query: { id: row.id, biz_id: row.biz_id, instance_id: row.instance_id, status: row.status } // 可带客户名等参数
|
||||
query: {
|
||||
id: row.id,
|
||||
biz_id: row.biz_id,
|
||||
instance_id: row.instance_id,
|
||||
status: row.status,
|
||||
flow_type: row.flow_type,
|
||||
step_index: row.step_index
|
||||
} // 可带客户名等参数
|
||||
})
|
||||
}
|
||||
const getDictItemSelectlist = ref<any[]>([])
|
||||
const getDictItemSelect = async () => {
|
||||
const res = await commonService.getDictItemSelect({ dict_id: '18' })
|
||||
getDictItemSelectlist.value = res
|
||||
}
|
||||
const instance_id = ref('')
|
||||
function extractFileName(pathOrUrl: string | undefined | null): string {
|
||||
if (!pathOrUrl) return '合同文件'
|
||||
try {
|
||||
const clean = String(pathOrUrl)
|
||||
const afterSlash = clean.split(/[\\/]/).pop() || '合同文件'
|
||||
return decodeURIComponent(afterSlash)
|
||||
} catch {
|
||||
return '合同文件'
|
||||
}
|
||||
}
|
||||
const activerow = ref()
|
||||
const handleCreateRepair = async (row: any) => {
|
||||
// getDictItemSelect()
|
||||
activerow.value = row
|
||||
instance_id.value = row.instance_id
|
||||
const res = await CrmService.checkStock({ id: row.biz_id })
|
||||
const orderData: OrderData = res
|
||||
orderDialog.orderData = orderData
|
||||
const res = await CrmService.stockapprovaldetailborder({
|
||||
id: row.id,
|
||||
flow_type: row.flow_type
|
||||
})
|
||||
console.log(res)
|
||||
switch (row.flow_type) {
|
||||
case 'stock_orders': {
|
||||
const orderData: OrderData = res
|
||||
orderDialog.orderData = orderData
|
||||
orderDialog.type = row.flow_type
|
||||
orderDialog.title = '合塑实业-汽车事业部-备货单(客户下定)'
|
||||
break
|
||||
}
|
||||
case 'stock_orders_nullify': {
|
||||
const orderData: OrderData = res
|
||||
orderDialog.orderData = orderData
|
||||
orderDialog.type = row.flow_type
|
||||
orderDialog.title = '备货单-作废'
|
||||
break
|
||||
}
|
||||
case 'purchase_orders': {
|
||||
const orderData: OrderData = res
|
||||
const urls = res.contract_file_url
|
||||
const paths = res.contract_file
|
||||
const list: Array<{ name: string; url: string }> = []
|
||||
const nameFromPath = (p: string) => extractFileName(p)
|
||||
urls.forEach((u: string, i: number) => {
|
||||
const p = paths[i] || ''
|
||||
list.push({ name: p ? nameFromPath(p) : extractFileName(u), url: u })
|
||||
})
|
||||
orderData.contract_file_url = list
|
||||
orderDialog.orderData = orderData
|
||||
orderDialog.type = row.flow_type
|
||||
orderDialog.title = '合塑实业-汽车事业部-采购单(采购)'
|
||||
break
|
||||
}
|
||||
}
|
||||
approvalDialogVisible.value = true
|
||||
}
|
||||
|
||||
@@ -213,15 +343,46 @@
|
||||
ElMessage.error('审批失败')
|
||||
}
|
||||
}
|
||||
// 转交操作
|
||||
const handleTransfer = async () => {
|
||||
zjForm.instance_id = activerow.value.instance_id
|
||||
zjForm.step_index = activerow.value.step_index
|
||||
getuserList()
|
||||
zjDialogVisible.value = true
|
||||
}
|
||||
const handlezjSubmit = async () => {
|
||||
if (!zjForm.to_uid) {
|
||||
ElMessage.warning('请选择转交人')
|
||||
return
|
||||
}
|
||||
if (!zjForm.reason) {
|
||||
ElMessage.warning('请输入转交原因')
|
||||
return
|
||||
}
|
||||
try {
|
||||
const { message } = await CrmService.stockapprovaltransfer(zjForm)
|
||||
ElMessage.success(message)
|
||||
zjDialogVisible.value = false
|
||||
approvalDialogVisible.value = false
|
||||
approvalNotes.value = ''
|
||||
zjForm.to_uid = ''
|
||||
zjForm.reason = ''
|
||||
instance_id.value = ''
|
||||
// 刷新列表
|
||||
getList()
|
||||
} catch (error) {
|
||||
console.error('转交失败:', error)
|
||||
// ElMessage.error('转交失败')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
:deep(.el-table__empty-block) {
|
||||
height: 100% !important;
|
||||
min-height: 200px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.avatar {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 6px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.stocking-page {
|
||||
background: var(--art-main-bg-color);
|
||||
@@ -253,22 +414,22 @@
|
||||
.approval-content {
|
||||
max-height: 70vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
align-items: center;
|
||||
margin-bottom: 12px;
|
||||
color: #333333;
|
||||
.mr40 { margin-right: 40px; }
|
||||
}
|
||||
|
||||
.info-section {
|
||||
margin-bottom: 24px;
|
||||
margin: 24px 0px;
|
||||
|
||||
.section-title {
|
||||
display: flex;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
|
||||
@@ -316,20 +477,20 @@
|
||||
margin-bottom: 12px;
|
||||
|
||||
.detail-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
label {
|
||||
margin-bottom: 6px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
font-weight: 500;
|
||||
color: #606266;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.summary-section {
|
||||
display: flex;
|
||||
@@ -359,10 +520,10 @@
|
||||
}
|
||||
|
||||
.dialog-footer {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
width: 500px;
|
||||
margin: auto;
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
|
||||
.reject-btn {
|
||||
|
||||
@@ -32,7 +32,13 @@
|
||||
@current-change="handleCurrentChange"
|
||||
>
|
||||
<template #default>
|
||||
<ElTableColumn label="审批单名称" prop="flows_name" min-width="140" />
|
||||
<ElTableColumn label="审批单名称" prop="flows_name" min-width="140">
|
||||
<template #default="scope">
|
||||
<span :style="{ color: getFlowTypeColor(scope.row.flow_type) }">
|
||||
{{ scope.row.flows_name }}
|
||||
</span>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="审批说明" prop="biz_desc" min-width="160" />
|
||||
<ElTableColumn label="审批结果" prop="action_desc" min-width="160" />
|
||||
<ElTableColumn label="提交人" prop="initiator_name" min-width="120" />
|
||||
@@ -83,12 +89,12 @@
|
||||
<ElRow :gutter="12">
|
||||
<ElCol :span="24">
|
||||
<el-input
|
||||
v-model="approvalNotes"
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
placeholder="备注"
|
||||
class="approval-notes"
|
||||
/>
|
||||
v-model="approvalNotes"
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
placeholder="备注"
|
||||
class="approval-notes"
|
||||
/>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
</div>
|
||||
@@ -96,7 +102,12 @@
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<ElButton type="primary" plain size="large" class="submit-btn" @click="handleApprove('reject')"
|
||||
<ElButton
|
||||
type="primary"
|
||||
plain
|
||||
size="large"
|
||||
class="submit-btn"
|
||||
@click="handleApprove('reject')"
|
||||
>驳回</ElButton
|
||||
>
|
||||
<ElButton type="primary" size="large" class="submit-btn" @click="handleApprove('approve')"
|
||||
@@ -136,7 +147,25 @@
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
|
||||
// 根据 flow_type 获取审批单名称的字体颜色
|
||||
const getFlowTypeColor = (flowType: string): string => {
|
||||
switch (flowType) {
|
||||
case 'stock_orders':
|
||||
return '#409EFF' // 蓝色 - 备货单
|
||||
case 'stock_orders_nullify':
|
||||
return '#F56C6C' // 红色 - 备货单作废
|
||||
case 'purchase_orders':
|
||||
return '#67C23A' // 绿色 - 采购单
|
||||
case 'purchase_orders_nullify':
|
||||
return '#E6A23C' // 橙色 - 采购单作废
|
||||
case 'return_orders':
|
||||
return '#909399' // 灰色 - 退货单
|
||||
case 'refund_orders':
|
||||
return '#9C27B0' // 紫色 - 退款单
|
||||
default:
|
||||
return '#303133' // 默认黑色
|
||||
}
|
||||
}
|
||||
watch([keyword], () => {
|
||||
pagination.currentPage = 1
|
||||
// 不自动请求,保持与截图行为一致,点击搜索或回车触发
|
||||
|
||||
@@ -9,15 +9,15 @@
|
||||
<span class="border"></span>
|
||||
<span class="title">基础信息</span>
|
||||
</div>
|
||||
<ElForm :model="baseForm" class="base-form" label-position="right">
|
||||
<ElForm :model="baseForm" :rules="baseFormRules" ref="baseFormRef" class="base-form" label-position="right">
|
||||
<ElRow :gutter="12">
|
||||
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
|
||||
<ElFormItem label="阿里订单号/PI号" label-width="114px">
|
||||
<ElFormItem label="阿里订单号/PI号" label-width="124px" prop="pi_no" required>
|
||||
<ElInput v-model="baseForm.pi_no" placeholder="请输入阿里订单号/PI号" />
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
|
||||
<ElFormItem label="日期" label-width="114px">
|
||||
<ElFormItem label="日期" label-width="114px" prop="biz_date" required>
|
||||
<ElDatePicker
|
||||
v-model="baseForm.biz_date"
|
||||
type="date"
|
||||
@@ -29,16 +29,12 @@
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
|
||||
<ElFormItem label="清关目的国" label-width="114px">
|
||||
<ElFormItem label="清关目的国" label-width="114px" prop="clearance_country_id" required>
|
||||
<el-select
|
||||
v-model="baseForm.clearance_country_id"
|
||||
filterable
|
||||
placeholder="请选择国家"
|
||||
@change="
|
||||
(val) =>
|
||||
(baseForm.clearance_country_snapshot =
|
||||
(countryOptions || []).find((x: any) => x.id === val)?.cn_addrs || '')
|
||||
"
|
||||
@change="(val) => (baseForm.clearance_country_snapshot = (countryOptions || []).find((x: any) => x.id === val)?.cn_addrs || '')"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in countryOptions"
|
||||
@@ -51,7 +47,7 @@
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
|
||||
<ElFormItem label="客户名称" label-width="114px">
|
||||
<ElFormItem label="客户名称" label-width="114px" prop="customer_id" required>
|
||||
<ElInput
|
||||
v-model="baseForm.customer_snapshot"
|
||||
placeholder="请选择客户"
|
||||
@@ -73,11 +69,7 @@
|
||||
filterable
|
||||
disabled
|
||||
placeholder="请选择客户渠道"
|
||||
@change="
|
||||
(val) =>
|
||||
(baseForm.customer_source_snapshot =
|
||||
(channelOptions || []).find((x: any) => x.id === val)?.name || '')
|
||||
"
|
||||
@change="(val) => (baseForm.customer_source_snapshot = (channelOptions || []).find((x: any) => x.id === val)?.name || '')"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in channelOptions"
|
||||
@@ -98,11 +90,7 @@
|
||||
filterable
|
||||
placeholder="请选择国家"
|
||||
disabled
|
||||
@change="
|
||||
(val) =>
|
||||
(baseForm.customer_country_snapshot =
|
||||
(countryOptions || []).find((x: any) => x.id === val)?.cn_addrs || '')
|
||||
"
|
||||
@change="(val) => (baseForm.customer_country_snapshot = (countryOptions || []).find((x: any) => x.id === val)?.cn_addrs || '')"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in countryOptions"
|
||||
@@ -124,11 +112,11 @@
|
||||
<span class="border"></span>
|
||||
<span class="title">订单详情</span>
|
||||
</div>
|
||||
<ElForm class="items-wrap" label-position="right">
|
||||
<ElForm :model="formModel" :rules="itemRules" ref="itemsFormRef" class="items-wrap" label-position="right">
|
||||
<div class="item-row form-box" v-for="(item, idx) in items" :key="idx">
|
||||
<ElRow :gutter="12" class="item-row-wrap">
|
||||
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
|
||||
<ElFormItem label="车型" label-width="85px">
|
||||
<ElFormItem label="车型" label-width="85px" :prop="`items.${idx}.sku_id`" required>
|
||||
<ElInput
|
||||
v-model="item.model_snapshot"
|
||||
placeholder="请选择车型"
|
||||
@@ -143,17 +131,12 @@
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
|
||||
<ElFormItem label="颜色/外饰" label-width="85px">
|
||||
<ElFormItem label="颜色/外饰" label-width="85px" :prop="`items.${idx}.color_exterior_snapshot`" required>
|
||||
<el-select
|
||||
v-model="item.color_exterior_snapshot"
|
||||
filterable
|
||||
placeholder="请选择内容"
|
||||
@change="
|
||||
(val) =>
|
||||
(item.color_exterior_code =
|
||||
(wcolorOptions || []).find((x: any) => x.color_text === val)?.color_code ||
|
||||
'')
|
||||
"
|
||||
@change="(val) => (item.color_exterior_code = (wcolorOptions || []).find((x: any) => x.color_text === val)?.color_code || '')"
|
||||
>
|
||||
<ElOption
|
||||
v-for="item in wcolorOptions"
|
||||
@@ -166,7 +149,7 @@
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
|
||||
<ElFormItem label="颜色/内饰" label-width="85px">
|
||||
<ElFormItem label="颜色/内饰" label-width="85px" :prop="`items.${idx}.color_interior_snapshot`" required>
|
||||
<el-select
|
||||
v-model="item.color_interior_snapshot"
|
||||
filterable
|
||||
@@ -195,12 +178,7 @@
|
||||
filterable
|
||||
multiple
|
||||
placeholder="请选择内容"
|
||||
@change="
|
||||
(vals) =>
|
||||
(item.modify_list = (modificationOptions || [])
|
||||
.filter((x: any) => (vals || []).includes(x.id))
|
||||
.map((x: any) => ({ option_id: x.id, option_name_snapshot: x.name })))
|
||||
"
|
||||
@change="(vals) => (item.modify_list = (modificationOptions || []).filter((x: any) => (vals || []).includes(x.id)).map((x: any) => ({ option_id: x.id, option_name_snapshot: x.name })))"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in modificationOptions"
|
||||
@@ -219,10 +197,10 @@
|
||||
</ElRow>
|
||||
<ElRow :gutter="12" class="item-row-wrap">
|
||||
<ElCol :sm="12" :md="12" :lg="6" :xl="4" v-if="(item.mileage ?? 0) <= 0">
|
||||
<ElFormItem label="官方指导价" label-width="85px">
|
||||
<ElFormItem label="官方指导价" label-width="95px" :prop="`items.${idx}.official_price`" required>
|
||||
<ElInput v-model="item.official_price" placeholder="请输入官方指导价">
|
||||
<template #suffix>
|
||||
<ElSelect v-model="item.guide_price_currency" style="width: 64px; border: none">
|
||||
<ElSelect v-model="item.guide_price_currency" style="width: 74px; border: none">
|
||||
<ElOption
|
||||
v-for="c in currencyOptions"
|
||||
:key="c.currency_code"
|
||||
@@ -236,10 +214,10 @@
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
<ElCol :sm="12" :md="12" :lg="6" :xl="4" v-else>
|
||||
<ElFormItem label="二手挂牌价" label-width="85px">
|
||||
<ElFormItem label="二手挂牌价" label-width="95px" :prop="`items.${idx}.listing_price`" required>
|
||||
<ElInput v-model="item.listing_price" placeholder="请输入二手挂牌价">
|
||||
<template #suffix>
|
||||
<ElSelect v-model="item.listing_price_currency" style="width: 64px">
|
||||
<ElSelect v-model="item.listing_price_currency" style="width: 74px">
|
||||
<ElOption
|
||||
v-for="c in currencyOptions"
|
||||
:key="c.currency_code"
|
||||
@@ -256,7 +234,7 @@
|
||||
<ElFormItem label="优惠" label-width="85px">
|
||||
<ElInput v-model="item.discount_price" placeholder="请输入优惠额">
|
||||
<template #suffix>
|
||||
<ElSelect v-model="item.discount_currency" style="width: 64px">
|
||||
<ElSelect v-model="item.discount_currency" style="width: 74px">
|
||||
<ElOption
|
||||
v-for="c in currencyOptions"
|
||||
:key="c.currency_code"
|
||||
@@ -273,7 +251,7 @@
|
||||
<ElFormItem label="优惠" label-width="85px">
|
||||
<ElInput v-model="item.reduction_price" placeholder="请输入优惠">
|
||||
<template #suffix>
|
||||
<ElSelect v-model="item.reduction_price_currency" style="width: 64px">
|
||||
<ElSelect v-model="item.reduction_price_currency" style="width: 74px">
|
||||
<ElOption
|
||||
v-for="c in currencyOptions"
|
||||
:key="c.currency_code"
|
||||
@@ -292,7 +270,7 @@
|
||||
<ElFormItem label="手续费" label-width="85px">
|
||||
<ElInput v-model="item.handling_fee" placeholder="请输入手续费">
|
||||
<template #suffix>
|
||||
<ElSelect v-model="item.helper_fee_currency" style="width: 64px">
|
||||
<ElSelect v-model="item.helper_fee_currency" style="width: 74px">
|
||||
<ElOption
|
||||
v-for="c in currencyOptions"
|
||||
:key="c.currency_code"
|
||||
@@ -309,7 +287,7 @@
|
||||
<ElFormItem label="改装费" label-width="85px">
|
||||
<ElInput v-model="item.retrofit_fee" placeholder="请输入改装费">
|
||||
<template #suffix>
|
||||
<ElSelect v-model="item.modify_fee_currency" style="width: 64px">
|
||||
<ElSelect v-model="item.modify_fee_currency" style="width: 74px">
|
||||
<ElOption
|
||||
v-for="c in currencyOptions"
|
||||
:key="c.currency_code"
|
||||
@@ -326,7 +304,7 @@
|
||||
<ElFormItem label="FOB段运费" label-width="85px">
|
||||
<ElInput v-model="item.fob_transport_fee" placeholder="请输入FOB段运费">
|
||||
<template #suffix>
|
||||
<ElSelect v-model="item.fob_fee_currency" style="width: 64px">
|
||||
<ElSelect v-model="item.fob_fee_currency" style="width: 74px">
|
||||
<ElOption
|
||||
v-for="c in currencyOptions"
|
||||
:key="c.currency_code"
|
||||
@@ -343,7 +321,7 @@
|
||||
<ElFormItem label="港杂费" label-width="85px">
|
||||
<ElInput v-model="item.port_misc_fee" placeholder="请输入港杂费">
|
||||
<template #suffix>
|
||||
<ElSelect v-model="item.port_fee_currency" style="width: 64px">
|
||||
<ElSelect v-model="item.port_fee_currency" style="width: 74px">
|
||||
<ElOption
|
||||
v-for="c in currencyOptions"
|
||||
:key="c.currency_code"
|
||||
@@ -360,7 +338,7 @@
|
||||
<ElFormItem label="海运费" label-width="85px">
|
||||
<ElInput v-model="item.ocean_freight" placeholder="请输入海运费">
|
||||
<template #suffix>
|
||||
<ElSelect v-model="item.ocean_freight_currency" style="width: 64px">
|
||||
<ElSelect v-model="item.ocean_freight_currency" style="width: 74px">
|
||||
<ElOption
|
||||
v-for="c in currencyOptions"
|
||||
:key="c.currency_code"
|
||||
@@ -377,7 +355,7 @@
|
||||
<ElFormItem label="佣金" label-width="85px">
|
||||
<ElInput v-model="item.commission" placeholder="请输入佣金">
|
||||
<template #suffix>
|
||||
<ElSelect v-model="item.commission_currency" style="width: 64px">
|
||||
<ElSelect v-model="item.commission_currency" style="width: 74px">
|
||||
<ElOption
|
||||
v-for="c in currencyOptions"
|
||||
:key="c.currency_code"
|
||||
@@ -394,7 +372,7 @@
|
||||
<ElFormItem label="购置税" label-width="85px">
|
||||
<ElInput v-model="item.purchase_tax" placeholder="请输入购置税">
|
||||
<template #suffix>
|
||||
<ElSelect v-model="item.purchase_tax_currency" style="width: 64px">
|
||||
<ElSelect v-model="item.purchase_tax_currency" style="width: 74px">
|
||||
<ElOption
|
||||
v-for="c in currencyOptions"
|
||||
:key="c.currency_code"
|
||||
@@ -411,7 +389,7 @@
|
||||
<ElFormItem label="预估退税额" label-width="85px">
|
||||
<ElInput v-model="item.tax_refund" placeholder="请输入预估退税额">
|
||||
<template #suffix>
|
||||
<ElSelect v-model="item.tax_refund_currency" style="width: 64px">
|
||||
<ElSelect v-model="item.tax_refund_currency" style="width: 74px">
|
||||
<ElOption
|
||||
v-for="c in currencyOptions"
|
||||
:key="c.currency_code"
|
||||
@@ -427,10 +405,10 @@
|
||||
</ElRow>
|
||||
<ElRow :gutter="12" class="item-row-wrap">
|
||||
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
|
||||
<ElFormItem label="成交价" label-width="85px">
|
||||
<ElFormItem label="成交价" label-width="85px" :prop="`items.${idx}.unit_price`" required>
|
||||
<ElInput v-model="item.unit_price" placeholder="请输入成交价">
|
||||
<template #suffix>
|
||||
<ElSelect v-model="item.unit_price_currency" style="width: 64px">
|
||||
<ElSelect v-model="item.unit_price_currency" style="width: 74px">
|
||||
<ElOption
|
||||
v-for="c in currencyOptions"
|
||||
:key="c.currency_code"
|
||||
@@ -447,7 +425,7 @@
|
||||
<ElFormItem label="总价" label-width="85px">
|
||||
<ElInput v-model="item.total_price" placeholder="请输入总价">
|
||||
<template #suffix>
|
||||
<ElSelect v-model="item.total_price_currency" style="width: 64px">
|
||||
<ElSelect v-model="item.total_price_currency" style="width: 74px">
|
||||
<ElOption
|
||||
v-for="c in currencyOptions"
|
||||
:key="c.currency_code"
|
||||
@@ -517,7 +495,7 @@
|
||||
<ElFormItem label="合计到岸价" label-width="105px">
|
||||
<ElInput v-model="baseForm.total_cif" placeholder="请输入合计到岸价">
|
||||
<template #suffix>
|
||||
<ElSelect v-model="baseForm.total_cif_currency" style="width: 64px">
|
||||
<ElSelect v-model="baseForm.total_cif_currency" style="width: 74px">
|
||||
<ElOption
|
||||
v-for="c in currencyOptions"
|
||||
:key="c.currency_code"
|
||||
@@ -534,7 +512,7 @@
|
||||
<ElFormItem label="合同汇率" label-width="105px">
|
||||
<ElInput v-model="baseForm.contract_exchange_rate" placeholder="请输入">
|
||||
<template #suffix>
|
||||
<ElSelect v-model="baseForm.settlement_currency" style="width: 64px">
|
||||
<ElSelect v-model="baseForm.settlement_currency" style="width: 74px">
|
||||
<ElOption
|
||||
@change="(val) =>(baseForm.contract_exchange_rate =(currencyOptions || []).find((x: any) => x.currency_code === val)?.exchange_rate || '') "
|
||||
v-for="c in currencyOptions"
|
||||
@@ -555,15 +533,15 @@
|
||||
<span class="border"></span>
|
||||
<span class="title">运输详情</span>
|
||||
</div>
|
||||
<ElForm :model="baseForm" class="foot-form" label-position="right">
|
||||
<ElForm :model="baseForm" :rules="transportFormRules" ref="transportFormRef" class="foot-form" label-position="right">
|
||||
<ElRow :gutter="12">
|
||||
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
|
||||
<ElFormItem label="清关人" label-width="98px">
|
||||
<ElFormItem label="清关人" label-width="98px" prop="declarant_name" required>
|
||||
<ElInput v-model="baseForm.declarant_name" placeholder="请输入清关人" />
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
|
||||
<ElFormItem label="预计发送日" label-width="98px">
|
||||
<ElFormItem label="预计发送日" label-width="98px" prop="eta_ship_date" required>
|
||||
<ElDatePicker
|
||||
v-model="baseForm.eta_ship_date"
|
||||
value-format="YYYY-MM-DD"
|
||||
@@ -575,7 +553,7 @@
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
|
||||
<ElFormItem label="国内收货港" label-width="98px">
|
||||
<ElFormItem label="国内收货港" label-width="98px" prop="domestic_port_id" required>
|
||||
<ElSelect
|
||||
v-model="baseForm.domestic_port_id"
|
||||
placeholder="请选择国内收货商"
|
||||
@@ -596,7 +574,7 @@
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
|
||||
<ElFormItem label="运输方式" label-width="98px">
|
||||
<ElFormItem label="运输方式" label-width="98px" prop="shipping_method_id" required>
|
||||
<ElSelect
|
||||
v-model="baseForm.shipping_method_id"
|
||||
placeholder="请选择运输方式"
|
||||
@@ -618,7 +596,7 @@
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
|
||||
<ElFormItem label="贸易条款" label-width="98px">
|
||||
<ElFormItem label="贸易条款" label-width="98px" prop="trade_term_id" required>
|
||||
<ElSelect
|
||||
v-model="baseForm.trade_term_id"
|
||||
placeholder="请选择"
|
||||
@@ -639,7 +617,7 @@
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
|
||||
<ElFormItem label="清关目的港" label-width="98px">
|
||||
<ElFormItem label="清关目的港" label-width="98px" prop="clearance_port_id" required>
|
||||
<ElSelect
|
||||
v-model="baseForm.clearance_port_id"
|
||||
placeholder="请选择"
|
||||
@@ -660,7 +638,7 @@
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
|
||||
<ElFormItem label="最迟到港时间" label-width="98px">
|
||||
<ElFormItem label="最迟到港时间" label-width="108px" prop="latest_arrival_date" required>
|
||||
<ElDatePicker
|
||||
v-model="baseForm.latest_arrival_date"
|
||||
type="date"
|
||||
@@ -682,15 +660,15 @@
|
||||
<div class="payment-section">
|
||||
<div class="payment-items">
|
||||
<div v-for="(payment, idx) in paymentItems" :key="idx" class="payment-item">
|
||||
<ElForm :model="payment" label-width="55px" class="payment-form" label-position="right">
|
||||
<ElForm :model="payment" :rules="paymentFormRules" :ref="(el) => setPaymentFormRef(el, idx)" label-width="65px" class="payment-form" label-position="right">
|
||||
<ElRow :gutter="12">
|
||||
<ElCol :sm="12" :md="12" :lg="6" :xl="6">
|
||||
<ElFormItem label="汇款人" label-width="55px">
|
||||
<ElFormItem label="汇款人" label-width="65px" prop="remitter_name" required>
|
||||
<ElInput v-model="payment.remitter_name" placeholder="请选择汇款人" />
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
<ElCol :sm="12" :md="12" :lg="4" :xl="4">
|
||||
<ElFormItem label="汇款国" label-width="55px">
|
||||
<ElFormItem label="汇款国" label-width="65px" prop="remit_country_id" required>
|
||||
<el-select
|
||||
v-model="payment.remit_country_id"
|
||||
filterable
|
||||
@@ -712,7 +690,7 @@
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
<ElCol :sm="12" :md="12" :lg="4" :xl="4">
|
||||
<ElFormItem label="日期" label-width="55px">
|
||||
<ElFormItem label="日期" label-width="65px" prop="remit_date" required>
|
||||
<ElDatePicker
|
||||
v-model="payment.remit_date"
|
||||
type="date"
|
||||
@@ -724,7 +702,7 @@
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
<ElCol :sm="12" :md="12" :lg="5" :xl="5">
|
||||
<ElFormItem label="账户" label-width="55px">
|
||||
<ElFormItem label="账户" label-width="65px" prop="account_id" required>
|
||||
<ElSelect
|
||||
v-model="payment.account_id"
|
||||
placeholder="请选择账户"
|
||||
@@ -745,12 +723,12 @@
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
<ElCol :sm="12" :md="12" :lg="5" :xl="5">
|
||||
<ElFormItem label="金额" label-width="55px">
|
||||
<ElFormItem label="金额" label-width="65px" prop="amount" required>
|
||||
<ElInput v-model="payment.amount" placeholder="请输入金额">
|
||||
<template #suffix>
|
||||
<ElSelect
|
||||
v-model="payment.currency_code"
|
||||
style="width: 64px"
|
||||
style="width: 74px"
|
||||
@change="
|
||||
(val) => {
|
||||
const found = (currencyOptions || []).find(
|
||||
@@ -826,10 +804,19 @@
|
||||
<div class="form-card">
|
||||
<!-- 底部信息 -->
|
||||
<div class="submit-wrap">
|
||||
<ElButton type="primary" plain size="large" class="submit-btn" @click="handleSubmit(0)"
|
||||
<ElButton
|
||||
type="primary"
|
||||
plain
|
||||
size="large"
|
||||
class="submit-btn"
|
||||
@click="handleSubmitWithValidation(0)"
|
||||
>保存</ElButton
|
||||
>
|
||||
<ElButton type="primary" size="large" class="submit-btn" @click="handleSubmit(1)"
|
||||
<ElButton
|
||||
type="primary"
|
||||
size="large"
|
||||
class="submit-btn"
|
||||
@click="handleSubmitWithValidation(1)"
|
||||
>提交</ElButton
|
||||
>
|
||||
</div>
|
||||
@@ -945,13 +932,75 @@
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
import { CrmService } from '@/api/crmApi'
|
||||
import { useStockingForm } from '@/composables/useStockingForm'
|
||||
import { onMounted } from 'vue'
|
||||
import { onMounted, ref, computed } from 'vue'
|
||||
|
||||
defineOptions({ name: 'CrmStockingAdd' }) // 定义组件名称,用于 KeepAlive 缓存控制
|
||||
|
||||
const { getUserInfo } = useUserStore()
|
||||
const route = useRoute()
|
||||
const stocking_id = route.query.id
|
||||
// 表单引用
|
||||
const baseFormRef = ref()
|
||||
const itemsFormRef = ref()
|
||||
const transportFormRef = ref()
|
||||
const paymentFormRefs = ref<any[]>([])
|
||||
// 表单模型
|
||||
const formModel = computed(() => ({ items: items.value }))
|
||||
|
||||
// 设置收款表单引用
|
||||
const setPaymentFormRef = (el: any, idx: number) => {
|
||||
if (el) {
|
||||
paymentFormRefs.value[idx] = el
|
||||
}
|
||||
}
|
||||
// 基础表单校验规则
|
||||
const baseFormRules = {
|
||||
pi_no: [{ required: true, message: '请输入阿里订单号/PI号', trigger: 'blur' }],
|
||||
biz_date: [{ required: true, message: '请选择日期', trigger: 'change' }],
|
||||
clearance_country_id: [{ required: true, message: '请选择清关目的国', trigger: 'change' }],
|
||||
customer_id: [{ required: true, message: '请选择客户', trigger: 'change' }]
|
||||
}
|
||||
// 订单项校验规则
|
||||
const itemRules = computed(() => {
|
||||
const rules: any = {}
|
||||
items.value.forEach((_, idx) => {
|
||||
rules[`items.${idx}.sku_id`] = [{ required: true, message: '请选择车型', trigger: 'change' }]
|
||||
rules[`items.${idx}.color_exterior_snapshot`] = [
|
||||
{ required: true, message: '请选择颜色/外饰', trigger: 'change' }
|
||||
]
|
||||
rules[`items.${idx}.color_interior_snapshot`] = [
|
||||
{ required: true, message: '请选择颜色/内饰', trigger: 'change' }
|
||||
]
|
||||
rules[`items.${idx}.official_price`] = [
|
||||
{ required: true, message: '请输入官方指导价', trigger: 'blur' }
|
||||
]
|
||||
rules[`items.${idx}.listing_price`] = [
|
||||
{ required: true, message: '请输入二手挂牌价', trigger: 'blur' }
|
||||
]
|
||||
rules[`items.${idx}.unit_price`] = [
|
||||
{ required: true, message: '请输入成交价', trigger: 'blur' }
|
||||
]
|
||||
})
|
||||
return rules
|
||||
})
|
||||
// 运输详情校验规则
|
||||
const transportFormRules = {
|
||||
declarant_name: [{ required: true, message: '请输入清关人', trigger: 'blur' }],
|
||||
eta_ship_date: [{ required: true, message: '请选择预计发送日', trigger: 'change' }],
|
||||
domestic_port_id: [{ required: true, message: '请选择国内收货港', trigger: 'change' }],
|
||||
shipping_method_id: [{ required: true, message: '请选择运输方式', trigger: 'change' }],
|
||||
trade_term_id: [{ required: true, message: '请选择贸易条款', trigger: 'change' }],
|
||||
clearance_port_id: [{ required: true, message: '请选择清关目的港', trigger: 'change' }],
|
||||
latest_arrival_date: [{ required: true, message: '请选择最迟到港时间', trigger: 'change' }]
|
||||
}
|
||||
// 收款信息校验规则
|
||||
const paymentFormRules = {
|
||||
remitter_name: [{ required: true, message: '请输入汇款人', trigger: 'blur' }],
|
||||
remit_country_id: [{ required: true, message: '请选择汇款国', trigger: 'change' }],
|
||||
remit_date: [{ required: true, message: '请选择汇款日期', trigger: 'change' }],
|
||||
account_id: [{ required: true, message: '请选择账户', trigger: 'change' }],
|
||||
amount: [{ required: true, message: '请输入金额', trigger: 'blur' }]
|
||||
}
|
||||
// 使用公共的备货单表单逻辑
|
||||
const {
|
||||
// 数据
|
||||
@@ -1024,6 +1073,29 @@
|
||||
populateFromDetail(res)
|
||||
}
|
||||
|
||||
// 自定义提交方法,添加表单校验
|
||||
const handleSubmitWithValidation = async (isFinal: number) => {
|
||||
try {
|
||||
// 校验基础表单
|
||||
await baseFormRef.value?.validate()
|
||||
// 校验订单项表单
|
||||
await itemsFormRef.value?.validate()
|
||||
// 校验运输详情表单
|
||||
await transportFormRef.value?.validate()
|
||||
// 校验收款信息表单
|
||||
for (let i = 0; i < paymentItems.value.length; i++) {
|
||||
const paymentFormRef = paymentFormRefs.value[i]
|
||||
if (paymentFormRef) {
|
||||
await paymentFormRef.validate()
|
||||
}
|
||||
}
|
||||
// 如果校验通过,调用原始的handleSubmit方法
|
||||
await handleSubmit(isFinal)
|
||||
} catch (error) {
|
||||
console.log('表单校验失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化表单
|
||||
onMounted(async () => {
|
||||
await initializeForm()
|
||||
|
||||
@@ -95,10 +95,11 @@
|
||||
<el-link
|
||||
v-if="
|
||||
scope.row.is_use == 0 &&
|
||||
scope.row.approval_status == 1 &&(scope.row.appro_status?scope.row.appro_status.status=='approved':false)
|
||||
scope.row.approval_status == 2 &&(scope.row.appro_status?scope.row.appro_status.status=='approved':false)
|
||||
"
|
||||
type="primary"
|
||||
underline="never"
|
||||
style="margin-right: 8px"
|
||||
@click="handleCreateRepair(scope.row)"
|
||||
>
|
||||
<i class="iconfont-sys"></i> 创建终版
|
||||
@@ -107,16 +108,54 @@
|
||||
v-if="scope.row.is_use == 1"
|
||||
type="success"
|
||||
underline="never"
|
||||
style="margin-right: 8px"
|
||||
@click="handleView1(scope.row)"
|
||||
>
|
||||
<i class="iconfont-sys"></i> 查看终版
|
||||
</el-link>
|
||||
<el-link type="warning" underline="never" @click="copys(scope.row)">
|
||||
<el-link
|
||||
type="warning"
|
||||
underline="never"
|
||||
@click="copys(scope.row)"
|
||||
style="margin-right: 8px"
|
||||
>
|
||||
<i class="iconfont-sys"></i> 复制
|
||||
</el-link>
|
||||
<el-link type="warning" underline="never" @click="showDialogtimeline(scope.row)">
|
||||
<el-link
|
||||
type="warning"
|
||||
underline="never"
|
||||
@click="showDialogtimeline(scope.row)"
|
||||
style="margin-right: 8px"
|
||||
>
|
||||
<i class="iconfont-sys"></i> 查看审批
|
||||
</el-link>
|
||||
<el-link
|
||||
v-if="scope.row.approval_status == 1"
|
||||
type="primary"
|
||||
underline="never"
|
||||
@click="stockrecallfn(scope.row)"
|
||||
style="margin-right: 8px"
|
||||
>
|
||||
<i class="iconfont-sys"></i> 撤回
|
||||
</el-link>
|
||||
<el-link
|
||||
v-if="scope.row.approval_status == 2"
|
||||
type="danger"
|
||||
underline="never"
|
||||
@click="stockinvalidfn(scope.row)"
|
||||
style="margin-right: 8px"
|
||||
>
|
||||
<i class="iconfont-sys"></i> 作废
|
||||
</el-link>
|
||||
<el-link
|
||||
v-if="scope.row.approval_status == 3 || scope.row.approval_status == 4"
|
||||
type="danger"
|
||||
underline="never"
|
||||
@click="stockdeletefn(scope.row)"
|
||||
style="margin-right: 8px"
|
||||
>
|
||||
<i class="iconfont-sys"></i> 删除
|
||||
</el-link>
|
||||
</ElRow>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
@@ -262,7 +301,7 @@
|
||||
getList()
|
||||
})
|
||||
//审批进度
|
||||
const showDialogtimeline = async (row: any) => {
|
||||
const showDialogtimeline = async (row: any) => {
|
||||
// 审批记录(兼容多种字段)
|
||||
const resstockapprovallogs = await manageService.stockapprovaltimeline({
|
||||
id: row.id,
|
||||
@@ -284,7 +323,7 @@
|
||||
timelineDialogVisible.value = true
|
||||
}
|
||||
function statusClass(status: any) {
|
||||
if (status === 'rejected') return 'status-lost'
|
||||
if (status === 'rejected' || status === 'voided') return 'status-lost'
|
||||
if (status === 'approved') return 'status-following'
|
||||
return 'status-dealed'
|
||||
}
|
||||
@@ -531,6 +570,72 @@
|
||||
})
|
||||
// 添加具体的复制逻辑
|
||||
}
|
||||
//备货单撤回
|
||||
const stockrecallfn = (row: any) => {
|
||||
ElMessageBox.confirm('确定要撤回此备货单吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
})
|
||||
.then(async () => {
|
||||
CrmService.stockrecall({ id: row.id })
|
||||
.then((res) => {
|
||||
ElMessage.success(res?.message || '操作成功')
|
||||
getList()
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
// 你可以在这里调用接口或做后续处理
|
||||
})
|
||||
.catch(() => {
|
||||
// 用户点击了取消
|
||||
})
|
||||
}
|
||||
//备货单作废
|
||||
const stockinvalidfn = (row: any) => {
|
||||
ElMessageBox.confirm('确定要作废此备货单吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
})
|
||||
.then(async () => {
|
||||
CrmService.stockinvalid({ id: row.id })
|
||||
.then((res) => {
|
||||
ElMessage.success(res?.message || '操作成功')
|
||||
getList()
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
// 你可以在这里调用接口或做后续处理
|
||||
})
|
||||
.catch(() => {
|
||||
// 用户点击了取消
|
||||
})
|
||||
}
|
||||
//删除
|
||||
const stockdeletefn = (row: any) => {
|
||||
ElMessageBox.confirm('确定要删除此备货单吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
})
|
||||
.then(async () => {
|
||||
CrmService.stockdelete({ id: row.id })
|
||||
.then((res) => {
|
||||
ElMessage.success(res?.message || '操作成功')
|
||||
getList()
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
// 你可以在这里调用接口或做后续处理
|
||||
})
|
||||
.catch(() => {
|
||||
// 用户点击了取消
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,362 @@
|
||||
<template>
|
||||
<div class="port-page">
|
||||
<div class="port-toolbar boxshadow">
|
||||
<div class="toolbar-left">
|
||||
<ElButton @click="showDialog('add')" v-ripple v-if="hasAuth('port@add')" type="primary"
|
||||
>创建港口</ElButton
|
||||
>
|
||||
</div>
|
||||
<div class="toolbar-right">
|
||||
<el-input
|
||||
v-model="keyword"
|
||||
placeholder="请输入客户名称"
|
||||
clearable
|
||||
style="width: 260px"
|
||||
@keyup.enter="getPortList"
|
||||
>
|
||||
<template #suffix>
|
||||
<el-icon class="clickable" @click="getPortList"><Search /></el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="boxshadow">
|
||||
<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="port_code" min-width="120" /> -->
|
||||
<ElTableColumn label="港口名称" prop="port_name" min-width="150" />
|
||||
<ElTableColumn label="英文名称" prop="port_name_en" min-width="150" />
|
||||
<ElTableColumn label="国家" prop="country_id" min-width="140">
|
||||
<template #default="scope">
|
||||
<span>{{ scope.row.country_name }}</span>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<!-- <ElTableColumn label="纬度" prop="lat" min-width="100" /> -->
|
||||
<!-- <ElTableColumn label="经度" prop="lng" min-width="100" /> -->
|
||||
<ElTableColumn label="排序" prop="sort" width="80" />
|
||||
<ElTableColumn label="状态" prop="status" width="100">
|
||||
<template #default="scope">
|
||||
<ElTag :type="scope.row.status ? 'success' : 'info'">{{
|
||||
scope.row.status ? '启用' : '停用'
|
||||
}}</ElTag>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="创建时间" prop="created_at" min-width="160" />
|
||||
<ElTableColumn fixed="right" label="操作" width="200px">
|
||||
<template #default="scope">
|
||||
<ElRow>
|
||||
<el-button link @click="showDialog('edit', scope.row)" v-if="hasAuth('port@update')"
|
||||
>编辑</el-button
|
||||
>
|
||||
<el-button
|
||||
type="danger"
|
||||
link
|
||||
@click="deletePort(scope.row)"
|
||||
v-if="hasAuth('port@destroy')"
|
||||
>删除</el-button
|
||||
>
|
||||
</ElRow>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
</template>
|
||||
</ArtTable>
|
||||
</div>
|
||||
|
||||
<ElDialog
|
||||
v-model="dialogVisible"
|
||||
:title="dialogType === 'add' ? '新增港口' : '编辑港口'"
|
||||
align-center
|
||||
width="720px"
|
||||
>
|
||||
<ElForm ref="formRef" :model="form" :rules="rules" label-width="110px" class="form">
|
||||
<ElFormItem label="港口代码" prop="port_code">
|
||||
<ElInput v-model="form.port_code" placeholder="请输入港口代码" />
|
||||
</ElFormItem>
|
||||
<ElFormItem label="港口名称" prop="port_name">
|
||||
<ElInput v-model="form.port_name" placeholder="请输入港口名称" />
|
||||
</ElFormItem>
|
||||
<ElFormItem label="英文名称" prop="port_name_en">
|
||||
<ElInput v-model="form.port_name_en" placeholder="请输入港口英文名称" />
|
||||
</ElFormItem>
|
||||
<ElFormItem label="国家" prop="country_id">
|
||||
<el-select
|
||||
v-model="form.country_id"
|
||||
filterable
|
||||
placeholder="请选择国家"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in countryOptions"
|
||||
:key="item.id"
|
||||
:label="item.cn_addr"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</ElFormItem>
|
||||
<!-- <ElFormItem label="纬度" prop="lat">
|
||||
<ElInput v-model="form.lat" placeholder="请输入纬度" />
|
||||
</ElFormItem>
|
||||
<ElFormItem label="经度" prop="lng">
|
||||
<ElInput v-model="form.lng" placeholder="请输入经度" />
|
||||
</ElFormItem> -->
|
||||
<ElFormItem label="排序" prop="sort">
|
||||
<ElInputNumber v-model="form.sort" :min="0" :max="9999" style="width: 100%" />
|
||||
</ElFormItem>
|
||||
<ElFormItem label="港口描述" prop="description">
|
||||
<ElInput
|
||||
v-model="form.description"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
maxlength="300"
|
||||
show-word-limit
|
||||
placeholder="请输入港口描述"
|
||||
/>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="状态">
|
||||
<ElSwitch v-model="form.status" :active-value="1" :inactive-value="0" />
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<ElButton @click="dialogVisible = false">取消</ElButton>
|
||||
<ElButton type="primary" @click="handleSubmit(formRef)">提交</ElButton>
|
||||
</div>
|
||||
</template>
|
||||
</ElDialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
import { vehicleService } from '@/api/crmpc/vehicleApi'
|
||||
import { commonService } from '@/api/commonApi'
|
||||
import { useAuth } from '@/composables/useAuth'
|
||||
const { hasAuth } = useAuth()
|
||||
defineOptions({ name: 'CrmVehiclePort' })
|
||||
const loading = ref(false)
|
||||
const dialogVisible = ref(false)
|
||||
const dialogType = ref<'add' | 'edit'>('add')
|
||||
const formRef = ref<FormInstance>()
|
||||
const keyword = ref<string>('')
|
||||
const countryFilter = ref<number | undefined>(undefined)
|
||||
const statusFilter = ref<number | undefined>(undefined)
|
||||
|
||||
const countryOptions = ref<any[]>([])
|
||||
const loadCountryOptions = async () => {
|
||||
try {
|
||||
const res = await commonService.getCountrySelect()
|
||||
countryOptions.value = res || []
|
||||
} catch (e) {
|
||||
countryOptions.value = []
|
||||
}
|
||||
}
|
||||
|
||||
const form = reactive<any>({
|
||||
id: '',
|
||||
status: 1,
|
||||
sort: 0,
|
||||
port_code: '',
|
||||
port_name: '',
|
||||
port_name_en: '',
|
||||
lat: '',
|
||||
lng: '',
|
||||
description: '',
|
||||
country_id: undefined
|
||||
})
|
||||
|
||||
const rules = reactive<FormRules>({
|
||||
port_code: [{ required: true, message: '请输入港口代码', trigger: 'blur' }],
|
||||
port_name: [{ required: true, message: '请输入港口名称', trigger: 'blur' }],
|
||||
country_id: [{ required: true, message: '请选择国家', trigger: 'change' }]
|
||||
})
|
||||
|
||||
const pagination = reactive({
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
total: 0
|
||||
})
|
||||
const tableData = ref<any[]>([])
|
||||
const tableRef = ref()
|
||||
|
||||
onMounted(() => {
|
||||
loadCountryOptions()
|
||||
getPortList()
|
||||
})
|
||||
watch([keyword, countryFilter, statusFilter], () => {
|
||||
pagination.currentPage = 1
|
||||
getPortList()
|
||||
})
|
||||
|
||||
const getPortList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const { currentPage, pageSize } = pagination
|
||||
const params = {
|
||||
current: currentPage,
|
||||
size: pageSize,
|
||||
keyword: keyword.value
|
||||
}
|
||||
const { records, current, size, total } = await vehicleService.getPortList(params)
|
||||
tableData.value = records || []
|
||||
Object.assign(pagination, { currentPage: current, pageSize: size, total })
|
||||
} catch (error) {
|
||||
console.error('获取港口列表失败:', error)
|
||||
ElMessage.error('获取港口列表失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleSizeChange = (newPageSize: number) => {
|
||||
pagination.pageSize = newPageSize
|
||||
getPortList()
|
||||
}
|
||||
const handleCurrentChange = (newCurrentPage: number) => {
|
||||
pagination.currentPage = newCurrentPage
|
||||
getPortList()
|
||||
}
|
||||
|
||||
const showDialog = (type: 'add' | 'edit', row?: any) => {
|
||||
dialogVisible.value = true
|
||||
dialogType.value = type
|
||||
if (type === 'edit' && row) {
|
||||
Object.assign(form, {
|
||||
id: row.id,
|
||||
status: row.status,
|
||||
sort: row.sort ?? 0,
|
||||
port_code: row.port_code,
|
||||
port_name: row.port_name,
|
||||
port_name_en: row.port_name_en,
|
||||
lat: row.lat,
|
||||
lng: row.lng,
|
||||
description: row.description,
|
||||
country_id: row.country_id
|
||||
})
|
||||
} else {
|
||||
Object.assign(form, {
|
||||
id: '',
|
||||
status: 1,
|
||||
sort: 0,
|
||||
port_code: '',
|
||||
port_name: '',
|
||||
port_name_en: '',
|
||||
lat: '',
|
||||
lng: '',
|
||||
description: '',
|
||||
country_id: undefined
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const deletePort = (row: any) => {
|
||||
ElMessageBox.confirm('确定删除该港口吗?', '删除确认', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'error'
|
||||
}).then(async () => {
|
||||
try {
|
||||
const { message } = await vehicleService.portDelete({ id: row.id })
|
||||
ElMessage.success(message)
|
||||
getPortList()
|
||||
} catch (error) {
|
||||
console.error('删除失败:', error)
|
||||
ElMessage.error('删除失败')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handleSubmit = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
await formEl.validate(async (valid) => {
|
||||
if (!valid) return
|
||||
try {
|
||||
if (dialogType.value === 'add') {
|
||||
const { message } = await vehicleService.portAdd({
|
||||
status: form.status,
|
||||
sort: form.sort,
|
||||
port_code: form.port_code,
|
||||
port_name: form.port_name,
|
||||
port_name_en: form.port_name_en,
|
||||
lat: form.lat,
|
||||
lng: form.lng,
|
||||
description: form.description,
|
||||
country_id: form.country_id
|
||||
})
|
||||
ElMessage.success(message)
|
||||
} else {
|
||||
const { message } = await vehicleService.portEdit({
|
||||
id: form.id,
|
||||
status: form.status,
|
||||
sort: form.sort,
|
||||
port_code: form.port_code,
|
||||
port_name: form.port_name,
|
||||
port_name_en: form.port_name_en,
|
||||
lat: form.lat,
|
||||
lng: form.lng,
|
||||
description: form.description,
|
||||
country_id: form.country_id
|
||||
})
|
||||
ElMessage.success(message)
|
||||
}
|
||||
dialogVisible.value = false
|
||||
getPortList()
|
||||
} catch (error) {
|
||||
console.error('操作失败:', error)
|
||||
ElMessage.error('操作失败')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const getCountryLabel = (id: number | undefined) => {
|
||||
if (!id) return '-'
|
||||
const found = countryOptions.value.find((c: any) => c.id === id)
|
||||
return found ? found.name : id
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.el-table__empty-block) {
|
||||
height: 100% !important;
|
||||
min-height: 200px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.port-page {
|
||||
background: var(--art-main-bg-color);
|
||||
min-height: 100vh;
|
||||
}
|
||||
.port-toolbar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
padding: 20px 20px;
|
||||
background: var(--art-main-bg-color);
|
||||
}
|
||||
.port-search-group {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
}
|
||||
.form {
|
||||
padding: 20px 0;
|
||||
}
|
||||
.dialog-footer {
|
||||
text-align: right;
|
||||
}
|
||||
.el-col2 {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
</style>
|
||||
@@ -3,25 +3,46 @@
|
||||
<!-- 搜索工具栏 -->
|
||||
<div class="search-toolbar boxshadow">
|
||||
<div class="toolbar-left">
|
||||
<ElButton type="primary" v-ripple @click="handleAdd">新增车辆</ElButton>
|
||||
</div>
|
||||
<div class="toolbar-right">
|
||||
<el-input
|
||||
v-model="searchForm.keyword"
|
||||
placeholder="请输入关键字"
|
||||
clearable
|
||||
style="width: 300px"
|
||||
@keyup.enter="getList"
|
||||
>
|
||||
<template #suffix>
|
||||
<el-icon class="clickable" @click="getList"><Search /></el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
<el-form-item label="关键字:" label-width="70px">
|
||||
<el-input
|
||||
v-model="searchForm.keyword"
|
||||
placeholder="请输入关键字"
|
||||
clearable
|
||||
@keyup.enter="getList"
|
||||
>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="品牌:" label-width="60px">
|
||||
<ElSelect v-model="searchForm.brand_id" filterable placeholder="请选择品牌">
|
||||
<ElOption label="全部" value="" />
|
||||
<ElOption
|
||||
v-for="item in brandOptions"
|
||||
:key="item.brand_code || item.code"
|
||||
:label="item.brand_name || item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
</ElSelect>
|
||||
</el-form-item>
|
||||
<el-form-item label="车系:" label-width="60px">
|
||||
<ElSelect v-model="searchForm.series_id" filterable placeholder="请选择车系">
|
||||
<ElOption label="全部" value="" />
|
||||
<ElOption
|
||||
v-for="s in seriesOptions"
|
||||
:key="s.series_code || s.code"
|
||||
:label="s.series_name || s.name"
|
||||
:value="s.id"
|
||||
/>
|
||||
</ElSelect>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="getList" v-ripple>搜索</el-button>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 数据表格 -->
|
||||
<div class="table-container boxshadow">
|
||||
<ElButton type="primary" v-ripple @click="handleAdd">新增车辆</ElButton>
|
||||
<ArtTable
|
||||
ref="tableRef"
|
||||
row-key="id"
|
||||
@@ -34,12 +55,12 @@
|
||||
@current-change="handleCurrentChange"
|
||||
>
|
||||
<template #default>
|
||||
<el-table-column prop="id" label="编码" min-width="170" show-overflow-tooltip align="center">
|
||||
<!-- <el-table-column prop="id" label="编码" min-width="170" show-overflow-tooltip align="center">
|
||||
<template #default="scope">
|
||||
<div class="ellipsis" :title="scope.row.fingerprint">指纹:{{ scope.row.fingerprint }}</div>
|
||||
<div class="ellipsis" :title="scope.row.sku_code">sku_code:{{ scope.row.sku_code }}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table-column> -->
|
||||
<ElTableColumn label="车型" prop="model_name" min-width="150" align="center">
|
||||
<template #default="scope">
|
||||
<span class="primary">{{ scope.row.model_name }}</span>
|
||||
@@ -48,7 +69,7 @@
|
||||
<ElTableColumn label="品牌" prop="brand_name" min-width="100" align="center" />
|
||||
<ElTableColumn label="车系" prop="series_name" min-width="120" align="center" />
|
||||
<ElTableColumn label="年款" prop="model_year" align="center" />
|
||||
<ElTableColumn label="版型" prop="trim" align="center" />
|
||||
<!-- <ElTableColumn label="版型" prop="trim" align="center" />
|
||||
<ElTableColumn label="配置等级" prop="trim_level" align="center" />
|
||||
<ElTableColumn label="排量" prop="engine_disp_l" align="center">
|
||||
<template #default="scope">
|
||||
@@ -64,7 +85,7 @@
|
||||
<template #default="scope">
|
||||
{{ scope.row.range_km ? scope.row.range_km + 'km' : '-' }}
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
</ElTableColumn> -->
|
||||
<ElTableColumn label="指导价(CNY)" prop="msrp" align="center" min-width="100">
|
||||
<template #default="scope">
|
||||
{{ scope.row.msrp ? scope.row.msrp : '-' }}
|
||||
@@ -93,7 +114,6 @@
|
||||
v-model="dialogVisible"
|
||||
:title="dialogTitle"
|
||||
width="1200px"
|
||||
top="40px"
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
>
|
||||
@@ -117,7 +137,7 @@
|
||||
:value="item.brand_name || item.name"
|
||||
/>
|
||||
</ElSelect>
|
||||
<ElInput v-model="formData.brand_code" placeholder="品牌编码" readonly disabled />
|
||||
<!-- <ElInput v-model="formData.brand_code" placeholder="品牌编码" readonly disabled /> -->
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
<ElCol :span="8">
|
||||
@@ -130,17 +150,74 @@
|
||||
:value="s.series_name || s.name"
|
||||
/>
|
||||
</ElSelect>
|
||||
<ElInput v-model="formData.series_code" placeholder="车系编码" readonly disabled />
|
||||
<!-- <ElInput v-model="formData.series_code" placeholder="车系编码" readonly disabled /> -->
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
<ElCol :span="8">
|
||||
<ElFormItem label="车型/配置名称" prop="model_name">
|
||||
<div style="display: flex; align-items: center; gap: 8px">
|
||||
<!-- 情况1: 有关联车型数据时显示下拉选择和采集按钮 -->
|
||||
<template v-if="modelsOptions.length > 0">
|
||||
<ElSelect
|
||||
v-model="formData.model_name"
|
||||
filterable
|
||||
placeholder="请选择车型名称"
|
||||
style="flex: 1"
|
||||
:disabled="!formData.series_code"
|
||||
allow-create
|
||||
default-first-option
|
||||
@change="handleModelSelectChange"
|
||||
>
|
||||
<ElOption
|
||||
v-for="model in modelsOptions"
|
||||
:key="model.model_code || model.code"
|
||||
:label="model.model_name || model.name"
|
||||
:value="model.model_name || model.name"
|
||||
/>
|
||||
</ElSelect>
|
||||
<ElButton type="primary" size="small" @click="showCollectModelsDialog">
|
||||
采集车型
|
||||
</ElButton>
|
||||
</template>
|
||||
|
||||
<!-- 情况2: 没有车型数据时显示采集按钮 -->
|
||||
<template v-else-if="formData.series_code">
|
||||
<ElInput
|
||||
v-model="formData.model_name"
|
||||
placeholder="请输入车型名称"
|
||||
style="flex: 1"
|
||||
@input="handleModelInputChange"
|
||||
/>
|
||||
<ElButton type="primary" size="small" @click="showCollectModelsDialog">
|
||||
采集车型
|
||||
</ElButton>
|
||||
</template>
|
||||
<!-- 情况3: 没有车系时直接输入 -->
|
||||
<ElInput
|
||||
v-else
|
||||
v-model="formData.model_name"
|
||||
placeholder="请输入车型名称"
|
||||
style="flex: 1"
|
||||
@input="handleModelInputChange"
|
||||
/>
|
||||
</div>
|
||||
<!-- <ElInput
|
||||
v-model="formData.model_code"
|
||||
placeholder="车型编码"
|
||||
readonly
|
||||
disabled
|
||||
style="margin-top: 8px"
|
||||
/> -->
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
<!-- <ElCol :span="8">
|
||||
<ElFormItem label="排量(L)" prop="engine_disp_l">
|
||||
<ElInput v-model="formData.engine_disp_l" placeholder="请输入排量" />
|
||||
<el-text class="mx-1" type="warning">如:1.5;纯电为空</el-text>
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
</ElCol> -->
|
||||
</ElRow>
|
||||
<ElRow :gutter="16">
|
||||
<!-- <ElRow :gutter="16">
|
||||
<ElCol :span="8">
|
||||
<ElFormItem label="版型/细分配置" prop="trim">
|
||||
<ElSelect
|
||||
@@ -208,14 +285,9 @@
|
||||
<ElInput v-model="formData.edition_code" placeholder="版本编码" readonly disabled />
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
<ElRow :gutter="16">
|
||||
<ElCol :span="8">
|
||||
<ElFormItem label="车型/配置名称" prop="model_name">
|
||||
<ElInput v-model="formData.model_name" placeholder="请输入车型名称" />
|
||||
<el-text class="mx-1" type="warning">如:伊兰特,海狮07(不填则自动生成)</el-text>
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
</ElRow> -->
|
||||
<!-- <ElRow :gutter="16">
|
||||
|
||||
<ElCol :span="8">
|
||||
<ElFormItem label="车型/配置代码" prop="model_code">
|
||||
<ElInput v-model="formData.model_code" placeholder="请输入车型代码" />
|
||||
@@ -228,7 +300,7 @@
|
||||
<el-text class="mx-1" type="warning">如:EV多为1</el-text>
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
</ElRow> -->
|
||||
<ElRow :gutter="16">
|
||||
<ElCol :span="8">
|
||||
<ElFormItem label="官方指导价" prop="msrp">
|
||||
@@ -240,13 +312,13 @@
|
||||
<ElInput v-model="formData.model_year" placeholder="YYYY" />
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
<ElCol :span="8">
|
||||
<!-- <ElCol :span="8">
|
||||
<ElFormItem label="最大功率(kW)" prop="engine_power_kw">
|
||||
<ElInput v-model="formData.engine_power_kw" placeholder="请输入最大功率" />
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
</ElCol> -->
|
||||
</ElRow>
|
||||
<ElRow :gutter="16">
|
||||
<!-- <ElRow :gutter="16">
|
||||
<ElCol :span="8">
|
||||
<ElFormItem label="电池容量(kWh)" prop="battery_kwh">
|
||||
<ElInput v-model="formData.battery_kwh" placeholder="请输入电池容量" />
|
||||
@@ -262,8 +334,8 @@
|
||||
<ElInput v-model="formData.doors" placeholder="请输入门数" />
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
<ElRow :gutter="16">
|
||||
</ElRow> -->
|
||||
<!-- <ElRow :gutter="16">
|
||||
<ElCol :span="8">
|
||||
<ElFormItem label="是否涡轮" prop="engine_turbo">
|
||||
<ElSelect v-model="formData.engine_turbo" placeholder="请选择">
|
||||
@@ -282,8 +354,8 @@
|
||||
<ElInput v-model="formData.seats" placeholder="请输入座位数" />
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
<ElRow :gutter="16">
|
||||
</ElRow> -->
|
||||
<!-- <ElRow :gutter="16"> -->
|
||||
<!-- <ElCol :span="8">
|
||||
<ElFormItem label="原产国" prop="origin_country">
|
||||
<ElSelect v-model="formData.origin_country" placeholder="请选择原产国" filterable>
|
||||
@@ -304,7 +376,7 @@
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
</ElCol> -->
|
||||
<ElCol :span="8">
|
||||
<!-- <ElCol :span="8">
|
||||
<ElFormItem label="外观颜色" prop="color_exterior_text">
|
||||
<ElSelect
|
||||
v-model="formData.color_exterior_text"
|
||||
@@ -327,8 +399,8 @@
|
||||
disabled
|
||||
/>
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
<ElCol :span="8">
|
||||
</ElCol> -->
|
||||
<!-- <ElCol :span="8">
|
||||
<ElFormItem label="内饰颜色" prop="color_interior_text">
|
||||
<ElSelect
|
||||
v-model="formData.color_interior_text"
|
||||
@@ -370,10 +442,10 @@
|
||||
disabled
|
||||
/>
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
</ElCol> -->
|
||||
<!-- </ElRow> -->
|
||||
<!-- <el-divider /> -->
|
||||
<ElRow :gutter="16">
|
||||
<!-- <ElRow :gutter="16">
|
||||
<ElCol :span="8">
|
||||
<ElFormItem label="驱动类型" prop="drive_type">
|
||||
<ElSelect v-model="formData.drive_type" placeholder="请选择驱动类型" clearable>
|
||||
@@ -494,7 +566,7 @@
|
||||
/>
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
</ElRow> -->
|
||||
<ElRow :gutter="16">
|
||||
<ElCol :span="8">
|
||||
<ElFormItem label="状态" prop="is_active">
|
||||
@@ -852,18 +924,83 @@
|
||||
</div>
|
||||
</div>
|
||||
</ElDialog>
|
||||
|
||||
<!-- 车型采集弹出层 -->
|
||||
<ElDialog
|
||||
v-model="collectModelsDialogVisible"
|
||||
:title="`${currentCollectSeries?.series_name} - 车型采集`"
|
||||
width="900px"
|
||||
align-center
|
||||
>
|
||||
<div class="collect-models-container">
|
||||
<div class="collect-models-header">
|
||||
<div
|
||||
style="
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 14px;
|
||||
"
|
||||
>
|
||||
请选择一个车型
|
||||
</div>
|
||||
</div>
|
||||
<div class="collect-models-content">
|
||||
<div v-if="collectModelsLoading" class="loading-container">
|
||||
<ElLoading text="正在采集车型数据..." />
|
||||
</div>
|
||||
<div v-else-if="collectModelsData.length === 0" class="empty-container">
|
||||
<ElEmpty description="暂无车型数据" />
|
||||
</div>
|
||||
<div v-else class="models-table-container">
|
||||
<ElTable :data="collectModelsData" style="width: 100%" max-height="400">
|
||||
<ElTableColumn width="60" align="center">
|
||||
<template #default="scope">
|
||||
<ElRadio
|
||||
:model-value="selectedModelId"
|
||||
:value="scope.row.car_id || scope.row.title"
|
||||
@change="selectModel(scope.row.car_id || scope.row.title)"
|
||||
/>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn prop="brand_name" label="品牌" width="100" />
|
||||
<ElTableColumn prop="series_name" label="车系" width="120" />
|
||||
<ElTableColumn prop="title" label="车型名称" min-width="200" show-overflow-tooltip />
|
||||
<ElTableColumn prop="year" label="年款" width="80" />
|
||||
<ElTableColumn prop="msrp" label="指导价" width="120">
|
||||
<template #default="scope">
|
||||
<span v-if="scope.row.msrp" class="price-text">
|
||||
¥{{ formatPrice(scope.row.msrp) }}
|
||||
</span>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
</ElTable>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<ElButton @click="collectModelsDialogVisible = false">取消</ElButton>
|
||||
<ElButton type="primary" @click="saveCollectModels" :disabled="!selectedModelId">
|
||||
保存车型
|
||||
</ElButton>
|
||||
</div>
|
||||
</template>
|
||||
</ElDialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { Search } from '@element-plus/icons-vue'
|
||||
import { vehicleService } from '@/api/crmpc/vehicleApi'
|
||||
import { commonService } from '@/api/commonApi'
|
||||
defineOptions({ name: 'CrmVehicleList' })
|
||||
defineOptions({ name: 'AdminVehicleList' })
|
||||
// 搜索表单
|
||||
const searchForm = reactive({
|
||||
keyword: ''
|
||||
keyword: '',
|
||||
brand_id: '',
|
||||
series_id: ''
|
||||
})
|
||||
|
||||
// 分页
|
||||
@@ -947,6 +1084,16 @@
|
||||
// 车系选项
|
||||
const seriesOptions = ref<Array<any>>([])
|
||||
|
||||
// 车型选项
|
||||
const modelsOptions = ref<Array<any>>([])
|
||||
|
||||
// 车型采集弹出层相关
|
||||
const collectModelsDialogVisible = ref(false)
|
||||
const currentCollectSeries = ref<any | null>(null)
|
||||
const collectModelsLoading = ref(false)
|
||||
const collectModelsData = ref<Array<any>>([])
|
||||
const selectedModelId = ref<string>('')
|
||||
|
||||
// 外观颜色
|
||||
const wcolorOptions = ref<Array<any>>([])
|
||||
// 内饰颜色
|
||||
@@ -992,6 +1139,30 @@
|
||||
ElMessage.error('获取车系列表失败')
|
||||
}
|
||||
}
|
||||
|
||||
// 获取车型列表
|
||||
const loadModelsOptions = async (seriesCode?: string) => {
|
||||
if (!seriesCode) {
|
||||
modelsOptions.value = []
|
||||
return
|
||||
}
|
||||
try {
|
||||
const matched = seriesOptions.value.find((s: any) => s.series_code === seriesCode)
|
||||
const seriesId = matched ? matched.id : ''
|
||||
if (!seriesId) {
|
||||
modelsOptions.value = []
|
||||
return
|
||||
}
|
||||
const response = await commonService.getModelsSelect({
|
||||
series_id: seriesId
|
||||
})
|
||||
modelsOptions.value = response
|
||||
} catch (error) {
|
||||
console.error('获取车型列表失败:', error)
|
||||
ElMessage.error('获取车型列表失败')
|
||||
}
|
||||
}
|
||||
|
||||
//颜色下拉列表
|
||||
const loadColorOptions = async () => {
|
||||
try {
|
||||
@@ -1036,7 +1207,9 @@
|
||||
const params = {
|
||||
current: currentPage,
|
||||
size: pageSize,
|
||||
keyword: searchForm.keyword
|
||||
keyword: searchForm.keyword,
|
||||
brand_id: searchForm.brand_id,
|
||||
series_id: searchForm.series_id
|
||||
}
|
||||
|
||||
const { records, current, size, total } = await vehicleService.getVehicleList(params)
|
||||
@@ -1062,22 +1235,122 @@
|
||||
}
|
||||
|
||||
// 新增车辆
|
||||
const handleAdd = () => {
|
||||
loadBrandOptions()
|
||||
loadColorOptions()
|
||||
loadDictOptions()
|
||||
const handleAdd = async () => {
|
||||
dialogTitle.value = '新增车辆'
|
||||
resetForm()
|
||||
await loadBrandOptions()
|
||||
loadColorOptions()
|
||||
loadDictOptions()
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
// 编辑车辆
|
||||
const handleEdit = (row: any) => {
|
||||
loadBrandOptions()
|
||||
const handleEdit = async (row: any) => {
|
||||
loadColorOptions()
|
||||
loadDictOptions()
|
||||
dialogTitle.value = '编辑车辆'
|
||||
|
||||
// 先清空表单和选项数据,避免监听器干扰
|
||||
resetForm()
|
||||
|
||||
// 使用 nextTick 确保 DOM 更新后再设置数据
|
||||
await nextTick()
|
||||
|
||||
// 设置表单数据
|
||||
Object.assign(formData, row)
|
||||
|
||||
// 立即检查车系名称是否被清空并重新设置
|
||||
setTimeout(() => {
|
||||
if (!formData.series_name) {
|
||||
formData.series_name = row.series_name
|
||||
}
|
||||
}, 1)
|
||||
|
||||
// 先加载品牌选项
|
||||
await loadBrandOptions()
|
||||
|
||||
// 如果品牌已选择,加载车系选项
|
||||
if (row.brand_code) {
|
||||
await loadSeriesOptions(row.brand_code)
|
||||
} else if (row.brand_name) {
|
||||
// 如果只有品牌名称没有品牌代码,尝试匹配品牌代码
|
||||
const matchedBrand = brandOptions.value.find(
|
||||
(b: any) => b.brand_name === row.brand_name || b.name === row.brand_name
|
||||
)
|
||||
if (matchedBrand && matchedBrand.brand_code) {
|
||||
await loadSeriesOptions(matchedBrand.brand_code)
|
||||
}
|
||||
}
|
||||
|
||||
// 如果车系已选择,加载车型选项
|
||||
if (row.series_code) {
|
||||
// 先尝试用原始的车系代码加载
|
||||
await loadModelsOptions(row.series_code)
|
||||
|
||||
// 如果车型选项为空,尝试用车系名称匹配的车系代码
|
||||
if (modelsOptions.value.length === 0 && row.series_name) {
|
||||
const matchedSeries = seriesOptions.value.find(
|
||||
(s: any) => s.series_name === row.series_name || s.name === row.series_name
|
||||
)
|
||||
if (matchedSeries && matchedSeries.series_code) {
|
||||
await loadModelsOptions(matchedSeries.series_code)
|
||||
}
|
||||
}
|
||||
} else if (row.series_name) {
|
||||
// 如果只有车系名称没有车系代码,尝试匹配车系代码
|
||||
const matchedSeries = seriesOptions.value.find(
|
||||
(s: any) => s.series_name === row.series_name || s.name === row.series_name
|
||||
)
|
||||
if (matchedSeries && matchedSeries.series_code) {
|
||||
await loadModelsOptions(matchedSeries.series_code)
|
||||
}
|
||||
}
|
||||
|
||||
// 如果仍然没有车型选项,但有车系信息,尝试再次加载
|
||||
if (modelsOptions.value.length === 0 && (row.series_code || row.series_name)) {
|
||||
// 使用当前表单中的车系代码重新加载
|
||||
if (formData.series_code) {
|
||||
await loadModelsOptions(formData.series_code)
|
||||
}
|
||||
}
|
||||
|
||||
// 确保车系代码与车系名称匹配(在选项数据加载完成后)
|
||||
// 如果车系名称被清空,重新设置
|
||||
if (!formData.series_name && row.series_name) {
|
||||
formData.series_name = row.series_name
|
||||
}
|
||||
|
||||
if (formData.series_name && seriesOptions.value.length > 0) {
|
||||
const matchedSeries = seriesOptions.value.find(
|
||||
(s: any) => s.series_name === formData.series_name || s.name === formData.series_name
|
||||
)
|
||||
|
||||
if (matchedSeries && matchedSeries.series_code) {
|
||||
formData.series_code = matchedSeries.series_code
|
||||
}
|
||||
}
|
||||
|
||||
// 确保车型信息正确回显(在选项数据加载完成后)
|
||||
if (formData.model_name && modelsOptions.value.length > 0) {
|
||||
const matchedModel = modelsOptions.value.find(
|
||||
(m: any) => m.model_name === formData.model_name || m.name === formData.model_name
|
||||
)
|
||||
if (matchedModel) {
|
||||
// 如果找到匹配的车型,更新车型代码、年款和指导价
|
||||
formData.model_code = matchedModel.model_code || matchedModel.code || formData.model_code
|
||||
formData.model_year = matchedModel.model_year || matchedModel.year || formData.model_year
|
||||
formData.msrp = matchedModel.msrp || formData.msrp
|
||||
}
|
||||
}
|
||||
|
||||
// 强制触发一次车系名称监听器(在回显逻辑完成后)
|
||||
const currentSeriesName = formData.series_name
|
||||
if (currentSeriesName) {
|
||||
formData.series_name = ''
|
||||
await nextTick()
|
||||
formData.series_name = currentSeriesName
|
||||
}
|
||||
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
@@ -1128,14 +1401,19 @@
|
||||
const resetForm = () => {
|
||||
Object.keys(formData).forEach((key) => {
|
||||
if (key === 'is_active') {
|
||||
(formData as any)[key] = 1
|
||||
;(formData as any)[key] = 1
|
||||
} else if (key === 'currency') {
|
||||
(formData as any)[key] = 'CNY'
|
||||
;(formData as any)[key] = 'CNY'
|
||||
} else {
|
||||
(formData as any)[key] = ''
|
||||
;(formData as any)[key] = ''
|
||||
}
|
||||
})
|
||||
formRef.value?.clearValidate()
|
||||
|
||||
// 重置选项数据
|
||||
modelsOptions.value = []
|
||||
seriesOptions.value = []
|
||||
brandOptions.value = []
|
||||
}
|
||||
|
||||
// 提交表单
|
||||
@@ -1169,14 +1447,120 @@
|
||||
() => searchForm.keyword,
|
||||
() => {
|
||||
pagination.currentPage = 1
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
// 车型采集相关方法
|
||||
const showCollectModelsDialog = () => {
|
||||
if (!formData.series_code) {
|
||||
ElMessage.warning('请先选择车系')
|
||||
return
|
||||
}
|
||||
|
||||
const matched = seriesOptions.value.find((s: any) => s.series_code === formData.series_code)
|
||||
if (!matched) {
|
||||
ElMessage.warning('未找到对应的车系信息')
|
||||
return
|
||||
}
|
||||
|
||||
currentCollectSeries.value = matched
|
||||
collectModelsDialogVisible.value = true
|
||||
collectModelsData.value = []
|
||||
selectedModelId.value = ''
|
||||
|
||||
// 调用车型采集
|
||||
collectVehicleModels(matched)
|
||||
}
|
||||
|
||||
const collectVehicleModels = async (series: any) => {
|
||||
try {
|
||||
collectModelsLoading.value = true
|
||||
|
||||
// 调用后端接口获取车型数据
|
||||
const response = await vehicleService.collectVehicleModels({ brand_series_id: series.id })
|
||||
|
||||
// 后端返回的数据格式为数组
|
||||
collectModelsData.value = response || []
|
||||
selectedModelId.value = ''
|
||||
|
||||
ElMessage.success(`成功采集到 ${collectModelsData.value.length} 个车型`)
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error.message || '车型采集失败')
|
||||
} finally {
|
||||
collectModelsLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 选择车型
|
||||
const selectModel = (modelId: string) => {
|
||||
selectedModelId.value = modelId
|
||||
}
|
||||
|
||||
const saveCollectModels = async () => {
|
||||
if (!currentCollectSeries.value || !selectedModelId.value) {
|
||||
ElMessage.warning('请选择一个车型')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
// 找到选中的车型数据
|
||||
const selectedModel = collectModelsData.value.find(
|
||||
(model: any) => (model.car_id || model.title) === selectedModelId.value
|
||||
)
|
||||
|
||||
if (!selectedModel) {
|
||||
ElMessage.warning('未找到选中的车型数据')
|
||||
return
|
||||
}
|
||||
|
||||
await vehicleService.saveVehicleModels({
|
||||
series_id: currentCollectSeries.value.id,
|
||||
models: [selectedModel]
|
||||
})
|
||||
|
||||
ElMessage.success('成功保存车型')
|
||||
collectModelsDialogVisible.value = false
|
||||
|
||||
// 将选中的车型信息回显到表单
|
||||
formData.model_name = selectedModel.title || selectedModel.model_name || ''
|
||||
formData.model_code = selectedModel.car_id || selectedModel.model_code || ''
|
||||
formData.model_year = selectedModel.year || ''
|
||||
formData.msrp = selectedModel.msrp || ''
|
||||
// 刷新车型选项
|
||||
await loadModelsOptions(formData.series_code)
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error.message || '保存车型失败')
|
||||
}
|
||||
}
|
||||
|
||||
// 格式化价格显示
|
||||
const formatPrice = (price: number | string) => {
|
||||
if (!price) return '0'
|
||||
const numPrice = typeof price === 'string' ? parseFloat(price) : price
|
||||
return numPrice.toLocaleString()
|
||||
}
|
||||
|
||||
// 页面加载时获取数据
|
||||
onMounted(async () => {
|
||||
loadBrandOptions()
|
||||
await getList()
|
||||
})
|
||||
|
||||
// 品牌名称变更时,联动设置品牌代码
|
||||
watch(
|
||||
() => searchForm.brand_id,
|
||||
(newId) => {
|
||||
const matched = brandOptions.value.find((b: any) => b.id === newId)
|
||||
const brand_code = matched ? matched.brand_code : ''
|
||||
// 品牌变更后,清空车系相关字段并加载对应车系
|
||||
searchForm.series_id = ''
|
||||
if (brand_code) {
|
||||
loadSeriesOptions(brand_code)
|
||||
} else {
|
||||
seriesOptions.value = []
|
||||
}
|
||||
}
|
||||
)
|
||||
watch(
|
||||
() => formData.brand_name,
|
||||
(newName) => {
|
||||
@@ -1196,14 +1580,71 @@
|
||||
// 车系名称变更时,联动设置车系代码
|
||||
watch(
|
||||
() => formData.series_name,
|
||||
(newSeriesName) => {
|
||||
(newSeriesName, oldSeriesName) => {
|
||||
if (!newSeriesName) return
|
||||
|
||||
const matchedSeries = seriesOptions.value.find(
|
||||
(s: any) => s.series_name === newSeriesName || s.name === newSeriesName
|
||||
)
|
||||
formData.series_code = matchedSeries ? matchedSeries.series_code || matchedSeries.code : ''
|
||||
|
||||
if (matchedSeries) {
|
||||
formData.series_code = matchedSeries.series_code || matchedSeries.code || ''
|
||||
|
||||
// 只有在车系真正发生变化时才清空车型相关字段
|
||||
if (oldSeriesName && oldSeriesName !== newSeriesName) {
|
||||
formData.model_name = ''
|
||||
formData.model_code = ''
|
||||
}
|
||||
|
||||
if (formData.series_code) {
|
||||
loadModelsOptions(formData.series_code)
|
||||
} else {
|
||||
modelsOptions.value = []
|
||||
}
|
||||
} else {
|
||||
// 如果找不到匹配的车系,清空车系代码
|
||||
formData.series_code = ''
|
||||
modelsOptions.value = []
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
// 车型名称变更时,联动设置车型代码
|
||||
watch(
|
||||
() => formData.model_name,
|
||||
(newModelName) => {
|
||||
const matchedModel = modelsOptions.value.find(
|
||||
(m: any) => m.model_name === newModelName || m.name === newModelName
|
||||
)
|
||||
formData.model_code = matchedModel ? matchedModel.model_code || matchedModel.code : ''
|
||||
}
|
||||
)
|
||||
|
||||
// 处理车型下拉选择变化
|
||||
const handleModelSelectChange = (value: string) => {
|
||||
// 如果是新创建的值(不在现有选项中),清空车型代码
|
||||
const matchedModel = modelsOptions.value.find(
|
||||
(m: any) => m.model_name === value || m.name === value
|
||||
)
|
||||
if (!matchedModel) {
|
||||
formData.model_code = ''
|
||||
// 清空年款和指导价
|
||||
formData.model_year = ''
|
||||
formData.msrp = ''
|
||||
} else {
|
||||
// 如果找到匹配的车型,更新车型代码、年款和指导价
|
||||
formData.model_code = matchedModel.model_code || matchedModel.code || ''
|
||||
formData.model_year = matchedModel.model_year || matchedModel.year || ''
|
||||
formData.msrp = matchedModel.msrp || ''
|
||||
}
|
||||
}
|
||||
|
||||
// 处理车型直接输入变化
|
||||
const handleModelInputChange = () => {
|
||||
// 直接输入时清空车型代码,让后端自动生成
|
||||
formData.model_code = ''
|
||||
}
|
||||
|
||||
// 外观颜色变更时,联动设置外观颜色代码
|
||||
watch(
|
||||
() => formData.color_exterior_text,
|
||||
@@ -1353,11 +1794,6 @@
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.toolbar-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
@@ -1423,4 +1859,42 @@
|
||||
white-space: nowrap;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.collect-models-container {
|
||||
background: transparent;
|
||||
|
||||
.collect-models-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 15px;
|
||||
border-bottom: 1px solid var(--el-border-color-lighter);
|
||||
}
|
||||
|
||||
.collect-models-content {
|
||||
min-height: 400px;
|
||||
|
||||
.loading-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
.empty-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
.models-table-container {
|
||||
.price-text {
|
||||
color: var(--el-color-danger);
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user