供应商管理数据同步, tms系统产品,服务渠道,产品渠道关系数据同步
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
/** 服务渠道下拉选项(非分页,来源 srv_service_channel 老系统镜像) */
|
||||
export function listChannelOptions(query) {
|
||||
return request({
|
||||
url: '/masterdata/servicechannel/optionselect',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
<template>
|
||||
<div class="channel-select">
|
||||
<el-button type="primary" plain icon="Connection" @click="openDialog">
|
||||
{{ selectedCodes.length ? '重新选择渠道' : '选择渠道' }}
|
||||
</el-button>
|
||||
<span class="cs-summary">
|
||||
已绑定 <b>{{ selectedCodes.length }}</b> 个渠道
|
||||
<el-button v-if="selectedCodes.length" link type="danger" @click="clearSelection">清空</el-button>
|
||||
</span>
|
||||
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
title="选择绑定渠道"
|
||||
width="960px"
|
||||
append-to-body
|
||||
:close-on-click-modal="false"
|
||||
@open="handleOpen"
|
||||
>
|
||||
<el-form :inline="true" class="cs-filter" @submit.prevent>
|
||||
<el-form-item label="渠道编码">
|
||||
<el-input v-model="queryParams.channelCode" placeholder="如 SK_DEPDHLL" clearable style="width: 160px" @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="渠道名称">
|
||||
<el-input v-model="queryParams.channelName" placeholder="渠道名称" clearable style="width: 170px" @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="服务商">
|
||||
<el-input v-model="queryParams.serviceName" placeholder="服务商名称" clearable style="width: 130px" @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="queryParams.blOpen" placeholder="全部" clearable style="width: 110px">
|
||||
<el-option label="启用" value="1" />
|
||||
<el-option label="停用" value="0" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table
|
||||
ref="tableRef"
|
||||
v-loading="loading"
|
||||
:data="filteredList"
|
||||
row-key="channelCode"
|
||||
height="380"
|
||||
border
|
||||
@select="handleSelect"
|
||||
@select-all="handleSelectAll"
|
||||
>
|
||||
<el-table-column type="selection" width="48" align="center" />
|
||||
<el-table-column label="渠道编码" prop="channelCode" width="150" show-overflow-tooltip />
|
||||
<el-table-column label="渠道名称" prop="channelName" min-width="170" show-overflow-tooltip />
|
||||
<el-table-column label="服务商" prop="serviceName" width="120" show-overflow-tooltip />
|
||||
<el-table-column label="付款方式" prop="payType" width="90" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="货物类型" prop="cargoType" width="90" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="状态" width="80" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag v-if="row.blOpen === '1'" type="success" size="small">启用</el-tag>
|
||||
<el-tag v-else-if="row.blOpen === '0'" type="info" size="small">停用</el-tag>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<template #empty>
|
||||
<el-empty description="暂无渠道数据" :image-size="80" />
|
||||
</template>
|
||||
</el-table>
|
||||
|
||||
<template #footer>
|
||||
<div class="cs-footer">
|
||||
<span>已选 <b>{{ draftCodes.size }}</b> 个渠道</span>
|
||||
<div>
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="confirmSelection">确定</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="ChannelSelect">
|
||||
import { ref, reactive, computed, watch, nextTick } from 'vue'
|
||||
import { useChannels } from './useChannels'
|
||||
|
||||
const props = defineProps({
|
||||
/** v-model 绑定值:逗号分隔的渠道编码串,如 "SK_DEPDHLL,015A" */
|
||||
modelValue: { type: [String, Number], default: '' }
|
||||
})
|
||||
const emit = defineEmits(['update:modelValue', 'change'])
|
||||
|
||||
const { channelList, ensureChannels } = useChannels()
|
||||
|
||||
const dialogVisible = ref(false)
|
||||
const loading = ref(false)
|
||||
const tableRef = ref(null)
|
||||
const draftCodes = reactive(new Set())
|
||||
const queryParams = reactive({
|
||||
channelCode: '',
|
||||
channelName: '',
|
||||
serviceName: '',
|
||||
blOpen: ''
|
||||
})
|
||||
|
||||
const selectedCodes = computed(() =>
|
||||
String(props.modelValue || '').split(',').map(s => s.trim()).filter(Boolean)
|
||||
)
|
||||
|
||||
const filteredList = computed(() => {
|
||||
return channelList.value.filter(c => {
|
||||
if (queryParams.channelCode && !(c.channelCode || '').toLowerCase().includes(queryParams.channelCode.trim().toLowerCase())) return false
|
||||
if (queryParams.channelName && !(c.channelName || '').includes(queryParams.channelName.trim())) return false
|
||||
if (queryParams.serviceName && !(c.serviceName || '').includes(queryParams.serviceName.trim())) return false
|
||||
if (queryParams.blOpen !== '' && queryParams.blOpen !== null && queryParams.blOpen !== undefined && String(c.blOpen) !== String(queryParams.blOpen)) return false
|
||||
return true
|
||||
})
|
||||
})
|
||||
|
||||
function openDialog() {
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
function handleOpen() {
|
||||
loading.value = true
|
||||
draftCodes.clear()
|
||||
selectedCodes.value.forEach(code => draftCodes.add(code))
|
||||
resetQuery(false)
|
||||
// 拉取最新渠道后,按 draftCodes 全量同步一次勾选(覆盖上次弹窗遗留状态)
|
||||
ensureChannels(true).finally(() => {
|
||||
loading.value = false
|
||||
nextTick(syncSelection)
|
||||
})
|
||||
}
|
||||
|
||||
/** 按 draftCodes 同步当前渲染行的勾选状态(draftCodes 为唯一数据源,筛选切换不丢选择) */
|
||||
function syncSelection() {
|
||||
if (!tableRef.value) return
|
||||
filteredList.value.forEach(row => {
|
||||
tableRef.value.toggleRowSelection(row, draftCodes.has(String(row.channelCode)))
|
||||
})
|
||||
}
|
||||
|
||||
/** 数据/筛选变化后同步表格勾选状态 */
|
||||
watch(filteredList, () => {
|
||||
if (!dialogVisible.value) return
|
||||
nextTick(syncSelection)
|
||||
}, { flush: 'post' })
|
||||
|
||||
function handleSelect(selection, row) {
|
||||
const code = String(row.channelCode)
|
||||
if (draftCodes.has(code)) draftCodes.delete(code)
|
||||
else draftCodes.add(code)
|
||||
}
|
||||
|
||||
function handleSelectAll(selection) {
|
||||
// selection 为当前过滤后页面被选中的行;长度>0 表示全选,否则取消全选
|
||||
if (selection.length) {
|
||||
filteredList.value.forEach(row => draftCodes.add(String(row.channelCode)))
|
||||
} else {
|
||||
filteredList.value.forEach(row => draftCodes.delete(String(row.channelCode)))
|
||||
}
|
||||
}
|
||||
|
||||
function handleQuery() {
|
||||
// 纯前端过滤(全量已缓存),watch filteredList 自动同步勾选
|
||||
}
|
||||
|
||||
function resetQuery(reload = true) {
|
||||
queryParams.channelCode = ''
|
||||
queryParams.channelName = ''
|
||||
queryParams.serviceName = ''
|
||||
queryParams.blOpen = ''
|
||||
if (reload) ensureChannels(true)
|
||||
}
|
||||
|
||||
function confirmSelection() {
|
||||
const codes = Array.from(draftCodes)
|
||||
const value = codes.join(',')
|
||||
emit('update:modelValue', value)
|
||||
emit('change', codes)
|
||||
dialogVisible.value = false
|
||||
}
|
||||
|
||||
function clearSelection() {
|
||||
emit('update:modelValue', '')
|
||||
emit('change', [])
|
||||
}
|
||||
|
||||
/** 供父组件预加载渠道缓存(tab 内明细表格使用) */
|
||||
defineExpose({ ensureChannels })
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.channel-select {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
.cs-summary {
|
||||
color: #606266;
|
||||
font-size: 14px;
|
||||
}
|
||||
.cs-summary b {
|
||||
color: #409eff;
|
||||
font-size: 15px;
|
||||
margin: 0 2px;
|
||||
}
|
||||
.cs-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.cs-footer b {
|
||||
color: #409eff;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,72 @@
|
||||
import { ref } from 'vue'
|
||||
import { listChannelOptions } from '@/api/masterdata/serviceChannel'
|
||||
|
||||
/**
|
||||
* 服务渠道列表模块级共享缓存:全应用只拉取一次
|
||||
* 用于供应商绑定渠道的选择与已绑渠道编码/名称回显
|
||||
*
|
||||
* 口径说明:供应商绑定关系统一存「渠道编码 channelCode」逗号串
|
||||
* (sup_supplier.channel_ids 历史字段名沿用,实际存的是渠道编码),
|
||||
* 与老系统 TAB_SERVICE_CHANNEL.CHANNEL_CODE 稳定对应。
|
||||
*/
|
||||
const channelList = ref([])
|
||||
let loadingPromise = null
|
||||
|
||||
/** 逗号分隔编码串 -> 字符串编码数组 */
|
||||
export function parseChannelCodes(value) {
|
||||
if (Array.isArray(value)) return value.map(v => String(v)).filter(Boolean)
|
||||
if (value === null || value === undefined) return []
|
||||
return String(value).split(',').map(s => s.trim()).filter(Boolean)
|
||||
}
|
||||
|
||||
/** 编码数组 -> 逗号分隔串(去重,保持原顺序) */
|
||||
export function joinChannelCodes(codes) {
|
||||
const seen = new Set()
|
||||
const out = []
|
||||
;(codes || []).forEach(code => {
|
||||
const key = String(code)
|
||||
if (key && !seen.has(key)) {
|
||||
seen.add(key)
|
||||
out.push(key)
|
||||
}
|
||||
})
|
||||
return out.join(',')
|
||||
}
|
||||
|
||||
export function useChannels() {
|
||||
/** 确保渠道列表已加载(force=true 强制刷新),拉取全部状态以便回显已停用渠道 */
|
||||
function ensureChannels(force = false) {
|
||||
if (!force && loadingPromise) return loadingPromise
|
||||
if (!force && channelList.value.length) return Promise.resolve(channelList.value)
|
||||
loadingPromise = listChannelOptions({})
|
||||
.then(res => {
|
||||
channelList.value = res.data || []
|
||||
return channelList.value
|
||||
})
|
||||
.finally(() => {
|
||||
loadingPromise = null
|
||||
})
|
||||
return loadingPromise
|
||||
}
|
||||
|
||||
/** 按渠道编码查渠道对象 */
|
||||
function findChannel(channelCode) {
|
||||
if (channelCode === null || channelCode === undefined || channelCode === '') return null
|
||||
const code = String(channelCode).trim()
|
||||
return channelList.value.find(c => String(c.channelCode) === code) || null
|
||||
}
|
||||
|
||||
/** 渠道展示名:编码 + 名称;查不到的渠道回显原编码(镜像未同步/已删除渠道不致空白) */
|
||||
function getChannelLabel(channelCode) {
|
||||
const c = findChannel(channelCode)
|
||||
if (!c) return String(channelCode)
|
||||
return c.channelCode + ' ' + c.channelName
|
||||
}
|
||||
|
||||
return {
|
||||
channelList,
|
||||
ensureChannels,
|
||||
findChannel,
|
||||
getChannelLabel
|
||||
}
|
||||
}
|
||||
@@ -55,6 +55,8 @@ import UserSelect from '@/components/UserSelect'
|
||||
import UserName from '@/components/UserName'
|
||||
// 产品多选绑定组件
|
||||
import ProductSelect from '@/components/ProductSelect'
|
||||
// 渠道多选绑定组件(供应商绑定老系统服务渠道)
|
||||
import ChannelSelect from '@/components/ChannelSelect'
|
||||
// 多值输入组件(一个字段搜索多个值,如订单号批量搜索)
|
||||
import MultiValueInput from '@/components/MultiValueInput'
|
||||
// 表格列设置组件(自定义列显隐)
|
||||
@@ -88,6 +90,7 @@ app.component('CustomerSelect', CustomerSelect)
|
||||
app.component('UserSelect', UserSelect)
|
||||
app.component('UserName', UserName)
|
||||
app.component('ProductSelect', ProductSelect)
|
||||
app.component('ChannelSelect', ChannelSelect)
|
||||
app.component('MultiValueInput', MultiValueInput)
|
||||
app.component('ColumnSettings', ColumnSettings)
|
||||
|
||||
|
||||
@@ -167,7 +167,14 @@
|
||||
<el-table-column v-if="isColVisible('cooperationStatus')" label="合作状态" align="center" prop="cooperationStatus">
|
||||
<template #default="scope">{{ formatOption(cooperationStatusOptions, scope.row.cooperationStatus) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="isColVisible('channelIds')" label="绑定渠道ID" align="center" prop="channelIds" />
|
||||
<el-table-column v-if="isColVisible('channelIds')" label="绑定渠道" align="center" prop="channelIds" min-width="200">
|
||||
<template #default="scope">
|
||||
<span v-if="channelNames(scope.row.channelIds)" :title="channelNames(scope.row.channelIds)">
|
||||
{{ channelNames(scope.row.channelIds) }}
|
||||
</span>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="isColVisible('remark')" label="备注说明" align="center" prop="remark" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="180" fixed="right">
|
||||
<template #default="scope">
|
||||
@@ -189,10 +196,18 @@
|
||||
|
||||
<script setup name="Supplier">
|
||||
import { listSupplier, delSupplier } from "@/api/customersuppliermanagement/supplierinfo/supplier.js"
|
||||
import { useChannels, parseChannelCodes } from '@/components/ChannelSelect/useChannels'
|
||||
import { useColumnSettings } from '@/utils/columnSettings'
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
|
||||
/** 绑定渠道编码串 -> 渠道编码+名称展示(查不到的渠道回显原编码) */
|
||||
const { ensureChannels, getChannelLabel } = useChannels()
|
||||
function channelNames(channelIds) {
|
||||
return parseChannelCodes(channelIds).map(code => getChannelLabel(code)).join(',')
|
||||
}
|
||||
ensureChannels()
|
||||
|
||||
/** 可自定义列(默认全部可见;选择列固定、操作列固定右侧不参与设置) */
|
||||
const columnDefs = [
|
||||
{ key: 'supplierCode', label: '供应商编码' },
|
||||
@@ -217,7 +232,7 @@ const columnDefs = [
|
||||
{ key: 'throwRatio', label: '抛比' },
|
||||
{ key: 'cooperationStartDate', label: '合作开始日期' },
|
||||
{ key: 'cooperationStatus', label: '合作状态' },
|
||||
{ key: 'channelIds', label: '绑定渠道ID' },
|
||||
{ key: 'channelIds', label: '绑定渠道' },
|
||||
{ key: 'remark', label: '备注说明' }
|
||||
]
|
||||
const { colVisible, isColVisible, toggleColumn, resetColumns } = useColumnSettings('supplier-info-list', columnDefs)
|
||||
|
||||
@@ -168,11 +168,6 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="绑定渠道ID" prop="channelIds">
|
||||
<el-input v-model="form.channelIds" type="textarea" :rows="2" placeholder="多个渠道ID用英文逗号分隔" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="备注说明" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入内容" />
|
||||
@@ -182,6 +177,37 @@
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- ============ 绑定渠道 ============ -->
|
||||
<el-tab-pane label="绑定渠道" name="channel">
|
||||
<div class="pane-toolbar">
|
||||
<channel-select v-model="form.channelIds" />
|
||||
<span class="pane-tip">绑定结果以渠道编码逗号串存入供应商主表 channel_ids,渠道来源于老系统服务渠道镜像(每日自动同步)</span>
|
||||
</div>
|
||||
<el-table :data="boundChannels" border size="small" class="bound-table">
|
||||
<el-table-column label="序号" type="index" width="55" align="center" />
|
||||
<el-table-column label="渠道编码" prop="channelCode" width="160" show-overflow-tooltip />
|
||||
<el-table-column label="渠道名称" prop="channelName" min-width="180" show-overflow-tooltip />
|
||||
<el-table-column label="服务商" prop="serviceName" width="120" show-overflow-tooltip />
|
||||
<el-table-column label="付款方式" prop="payType" width="90" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="货物类型" prop="cargoType" width="90" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="状态" width="90" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag v-if="row.blOpen === '1'" type="success" size="small">启用</el-tag>
|
||||
<el-tag v-else-if="row.blOpen === '0'" type="info" size="small">停用</el-tag>
|
||||
<span v-else>未知</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="90" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="danger" size="small" @click="removeChannel(row.channelCode)">移除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<template #empty>
|
||||
<el-empty description="暂未绑定渠道,点击上方「选择渠道」进行绑定" :image-size="80" />
|
||||
</template>
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- ============ 公司主体 ============ -->
|
||||
<el-tab-pane label="公司主体" name="company">
|
||||
<div class="pane-toolbar">
|
||||
@@ -589,6 +615,7 @@ import {
|
||||
import { getToken } from '@/utils/auth'
|
||||
import axios from 'axios'
|
||||
import { saveAs } from 'file-saver'
|
||||
import { useChannels, parseChannelCodes } from '@/components/ChannelSelect/useChannels'
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
const route = useRoute()
|
||||
@@ -596,6 +623,10 @@ const router = useRouter()
|
||||
|
||||
const { settlement_type } = useDict('settlement_type')
|
||||
|
||||
const { ensureChannels, findChannel } = useChannels()
|
||||
/** 进入页面即预加载渠道缓存,供已绑渠道明细表回显名称 */
|
||||
ensureChannels()
|
||||
|
||||
const pageLoading = ref(false)
|
||||
const saving = ref(false)
|
||||
const activeTab = ref('base')
|
||||
@@ -671,6 +702,17 @@ function buildEmptyForm() {
|
||||
}
|
||||
const form = ref(buildEmptyForm())
|
||||
|
||||
/* ---------------- 渠道绑定 ---------------- */
|
||||
/** 已绑渠道明细:镜像中能查到的回显完整信息,查不到的以占位行展示原编码(不丢失旧数据) */
|
||||
const boundChannels = computed(() =>
|
||||
parseChannelCodes(form.value.channelIds).map(code => findChannel(code) || { channelCode: code })
|
||||
)
|
||||
|
||||
function removeChannel(channelCode) {
|
||||
const codes = parseChannelCodes(form.value.channelIds).filter(code => code !== String(channelCode))
|
||||
form.value.channelIds = codes.join(',')
|
||||
}
|
||||
|
||||
/* ---------------- 用户冗余姓名 ---------------- */
|
||||
function handleUserChange(uid, user, nameField) {
|
||||
form.value[nameField] = user ? (user.nickName || user.userName || null) : null
|
||||
|
||||
Reference in New Issue
Block a user