|
|
|
@@ -8,8 +8,7 @@
|
|
|
|
|
@close="emit('update:visible', false)"
|
|
|
|
|
>
|
|
|
|
|
<div class="toolbar">
|
|
|
|
|
<el-input v-model="keyword" clearable placeholder="产品名称 / 工作表 / 国家" style="width: 260px" />
|
|
|
|
|
<el-button type="danger" plain :disabled="!selectedIds.length" @click="handleRemove" v-hasPermi="['price:label:remove']">移除</el-button>
|
|
|
|
|
<el-input v-model="keyword" clearable :placeholder="phSearch" style="width: 300px" />
|
|
|
|
|
</div>
|
|
|
|
|
<el-table
|
|
|
|
|
v-loading="loading"
|
|
|
|
@@ -17,30 +16,26 @@
|
|
|
|
|
border
|
|
|
|
|
height="calc(100vh - 220px)"
|
|
|
|
|
row-key="termId"
|
|
|
|
|
@selection-change="onSelect"
|
|
|
|
|
>
|
|
|
|
|
<el-table-column type="selection" width="48" align="center" />
|
|
|
|
|
<el-table-column prop="productId" label="产品编号" width="90" align="center" />
|
|
|
|
|
<el-table-column prop="productName" label="产品名称" min-width="150" show-overflow-tooltip />
|
|
|
|
|
<el-table-column prop="supplierName" label="价卡归属" width="100" align="center" />
|
|
|
|
|
<el-table-column prop="countryScope" label="国家/口岸" min-width="120" show-overflow-tooltip />
|
|
|
|
|
<el-table-column prop="chargeType" label="费用类型" width="110" align="center" />
|
|
|
|
|
<el-table-column prop="sheetName" label="报价工作表" min-width="140" show-overflow-tooltip />
|
|
|
|
|
<el-table-column prop="versionNo" label="版本" width="70" align="center" />
|
|
|
|
|
<el-table-column label="有效期" min-width="180" align="center">
|
|
|
|
|
<el-table-column prop="productId" :label="colId" width="90" align="center" />
|
|
|
|
|
<el-table-column prop="productName" :label="colName" min-width="150" show-overflow-tooltip />
|
|
|
|
|
<el-table-column :label="colTerm" min-width="180" align="center">
|
|
|
|
|
<template #default="{ row }">{{ termText(row) }}</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="状态" width="90" align="center">
|
|
|
|
|
<el-table-column :label="colStatus" width="90" align="center">
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
<el-tag :type="termStatusTag(row.termStatus)" size="small">{{ termStatusText(row.termStatus) }}</el-tag>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column prop="weightCount" label="重量段" width="80" align="center" />
|
|
|
|
|
<el-table-column prop="surchargeCount" label="附加费" width="80" align="center" />
|
|
|
|
|
<el-table-column label="操作" width="160" align="center" fixed="right">
|
|
|
|
|
<el-table-column prop="weightCount" :label="colWeight" width="80" align="center" />
|
|
|
|
|
<el-table-column prop="surchargeCount" :label="colSc" width="80" align="center" />
|
|
|
|
|
<el-table-column :label="colBoundChannels" min-width="180" show-overflow-tooltip>
|
|
|
|
|
<template #default="{ row }">{{ boundChannelText(row) }}</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column :label="colAction" width="160" align="center" fixed="right">
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
<el-button link type="primary" @click="openProduct(row)" v-hasPermi="['price:label:query']">查看</el-button>
|
|
|
|
|
<el-button link type="danger" @click="handleRemove([row.productId])" v-hasPermi="['price:label:remove']">移除</el-button>
|
|
|
|
|
<el-button link type="primary" @click="openBindList(row)" v-hasPermi="['price:label:edit']">绑定</el-button>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
</el-table>
|
|
|
|
@@ -51,11 +46,89 @@
|
|
|
|
|
<el-button @click="detailVisible = false">关闭</el-button>
|
|
|
|
|
</template>
|
|
|
|
|
</el-drawer>
|
|
|
|
|
|
|
|
|
|
<el-drawer
|
|
|
|
|
v-model="bindListVisible"
|
|
|
|
|
:title="bindListTitle"
|
|
|
|
|
size="520px"
|
|
|
|
|
append-to-body
|
|
|
|
|
destroy-on-close
|
|
|
|
|
@closed="onBindListClosed"
|
|
|
|
|
>
|
|
|
|
|
<div class="bind-list-toolbar">
|
|
|
|
|
<el-button type="primary" plain @click="openAddChannels" v-hasPermi="['price:label:edit']">添加渠道</el-button>
|
|
|
|
|
</div>
|
|
|
|
|
<el-table
|
|
|
|
|
v-loading="bindListLoading"
|
|
|
|
|
:data="boundChannels"
|
|
|
|
|
border
|
|
|
|
|
height="calc(100vh - 240px)"
|
|
|
|
|
:empty-text="emptyBound"
|
|
|
|
|
>
|
|
|
|
|
<el-table-column prop="channelCode" :label="colChannelCode" min-width="120" show-overflow-tooltip />
|
|
|
|
|
<el-table-column prop="channelName" :label="colChannelName" min-width="160" show-overflow-tooltip />
|
|
|
|
|
<el-table-column :label="colAction" width="90" align="center" fixed="right">
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
<el-button link type="danger" @click="removeBoundChannel(row)" v-hasPermi="['price:label:edit']">移除</el-button>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
</el-table>
|
|
|
|
|
<template #footer>
|
|
|
|
|
<el-button @click="bindListVisible = false">关闭</el-button>
|
|
|
|
|
</template>
|
|
|
|
|
</el-drawer>
|
|
|
|
|
|
|
|
|
|
<el-dialog
|
|
|
|
|
v-model="addVisible"
|
|
|
|
|
:title="addTitle"
|
|
|
|
|
width="560px"
|
|
|
|
|
append-to-body
|
|
|
|
|
destroy-on-close
|
|
|
|
|
@closed="onAddClosed"
|
|
|
|
|
>
|
|
|
|
|
<div v-loading="addLoading">
|
|
|
|
|
<el-form label-width="88px">
|
|
|
|
|
<el-form-item :label="lblProduct">
|
|
|
|
|
<span>{{ bindProductName }}</span>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item :label="lblChannels">
|
|
|
|
|
<el-select
|
|
|
|
|
v-model="addChannelIds"
|
|
|
|
|
multiple
|
|
|
|
|
filterable
|
|
|
|
|
clearable
|
|
|
|
|
collapse-tags
|
|
|
|
|
collapse-tags-tooltip
|
|
|
|
|
:placeholder="phChannels"
|
|
|
|
|
style="width: 100%"
|
|
|
|
|
>
|
|
|
|
|
<el-option
|
|
|
|
|
v-for="item in addableChannelOptions"
|
|
|
|
|
:key="item.id"
|
|
|
|
|
:label="channelOptionLabel(item)"
|
|
|
|
|
:value="String(item.id)"
|
|
|
|
|
/>
|
|
|
|
|
</el-select>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<div class="bind-hint">{{ bindHint }}</div>
|
|
|
|
|
</el-form>
|
|
|
|
|
</div>
|
|
|
|
|
<template #footer>
|
|
|
|
|
<el-button @click="addVisible = false">取消</el-button>
|
|
|
|
|
<el-button type="primary" :loading="addSaving" @click="submitAddChannels">保存</el-button>
|
|
|
|
|
</template>
|
|
|
|
|
</el-dialog>
|
|
|
|
|
</el-drawer>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup>
|
|
|
|
|
import { listLabelProducts, getLabelProduct, removeLabelProducts } from '@/api/price/label'
|
|
|
|
|
import {
|
|
|
|
|
listLabelProducts,
|
|
|
|
|
getLabelProduct,
|
|
|
|
|
listLabelProductChannels,
|
|
|
|
|
replaceLabelProductChannels
|
|
|
|
|
} from '@/api/price/label'
|
|
|
|
|
import { listChannelOptions } from '@/api/masterdata/serviceChannel'
|
|
|
|
|
import PayConfigDetail from '@/views/price/basic/pay/components/PayConfigDetail.vue'
|
|
|
|
|
|
|
|
|
|
const props = defineProps({
|
|
|
|
@@ -69,11 +142,40 @@ const { proxy } = getCurrentInstance()
|
|
|
|
|
const loading = ref(false)
|
|
|
|
|
const rows = ref([])
|
|
|
|
|
const keyword = ref('')
|
|
|
|
|
const selectedIds = ref([])
|
|
|
|
|
const detailVisible = ref(false)
|
|
|
|
|
const detailLoading = ref(false)
|
|
|
|
|
const detail = ref(null)
|
|
|
|
|
|
|
|
|
|
const bindListVisible = ref(false)
|
|
|
|
|
const bindListLoading = ref(false)
|
|
|
|
|
const bindProductId = ref(null)
|
|
|
|
|
const bindProductName = ref('')
|
|
|
|
|
const boundChannels = ref([])
|
|
|
|
|
const channelOptions = ref([])
|
|
|
|
|
|
|
|
|
|
const addVisible = ref(false)
|
|
|
|
|
const addLoading = ref(false)
|
|
|
|
|
const addSaving = ref(false)
|
|
|
|
|
const addChannelIds = ref([])
|
|
|
|
|
|
|
|
|
|
const phSearch = '产品名称 / 绑定渠道'
|
|
|
|
|
const colId = '产品编号'
|
|
|
|
|
const colName = '产品名称'
|
|
|
|
|
const colTerm = '有效期'
|
|
|
|
|
const colStatus = '状态'
|
|
|
|
|
const colWeight = '重量段'
|
|
|
|
|
const colSc = '附加费'
|
|
|
|
|
const colBoundChannels = '绑定渠道'
|
|
|
|
|
const colAction = '操作'
|
|
|
|
|
const colChannelCode = '渠道编码'
|
|
|
|
|
const colChannelName = '渠道名称'
|
|
|
|
|
const lblProduct = '产品'
|
|
|
|
|
const lblChannels = '添加渠道'
|
|
|
|
|
const phChannels = '请选择要添加的渠道(可多选)'
|
|
|
|
|
const bindHint = '绑定为收款(AR),落库 srv_channel_product_map;一个渠道同一收付类型仅保留一条产品映射。'
|
|
|
|
|
const emptyBound = '暂无绑定渠道'
|
|
|
|
|
const addTitle = '添加渠道'
|
|
|
|
|
|
|
|
|
|
const title = computed(() => {
|
|
|
|
|
const v = props.version || {}
|
|
|
|
|
const label = v.versionLabel || (v.versionNo != null ? `V${v.versionNo}` : '')
|
|
|
|
@@ -86,15 +188,24 @@ const detailTitle = computed(() => {
|
|
|
|
|
return `${detail.value.productName || '产品'} ${no}`.trim()
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const bindListTitle = computed(() => {
|
|
|
|
|
return bindProductName.value ? `绑定渠道 · ${bindProductName.value}` : '绑定渠道'
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const filteredRows = computed(() => {
|
|
|
|
|
const kw = keyword.value.trim().toLowerCase()
|
|
|
|
|
if (!kw) return rows.value
|
|
|
|
|
return rows.value.filter(row => {
|
|
|
|
|
const text = [row.productName, row.sheetName, row.countryScope, row.supplierName].join(' ').toLowerCase()
|
|
|
|
|
const text = [row.productName, boundChannelText(row)].join(' ').toLowerCase()
|
|
|
|
|
return text.includes(kw)
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const addableChannelOptions = computed(() => {
|
|
|
|
|
const bound = new Set((boundChannels.value || []).map(item => String(item.channelId)))
|
|
|
|
|
return (channelOptions.value || []).filter(item => item && item.id != null && !bound.has(String(item.id)))
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
function formatHour(val) {
|
|
|
|
|
if (!val) return ''
|
|
|
|
|
const text = String(val).replace('T', ' ')
|
|
|
|
@@ -120,8 +231,17 @@ function termStatusTag(status) {
|
|
|
|
|
return map[String(status)] || 'info'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function onSelect(selection) {
|
|
|
|
|
selectedIds.value = (selection || []).map(item => item.productId).filter(Boolean)
|
|
|
|
|
function channelOptionLabel(item) {
|
|
|
|
|
const code = item?.channelCode || item?.id || ''
|
|
|
|
|
const name = item?.channelName || ''
|
|
|
|
|
if (code && name && code !== name) return code + ' / ' + name
|
|
|
|
|
return code || name || '-'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function boundChannelText(row) {
|
|
|
|
|
if (!row) return '-'
|
|
|
|
|
if (row.boundChannelText) return row.boundChannelText
|
|
|
|
|
return '-'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function load() {
|
|
|
|
@@ -150,16 +270,96 @@ async function openProduct(row) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function handleRemove(ids) {
|
|
|
|
|
const list = (Array.isArray(ids) ? ids : selectedIds.value).filter(Boolean)
|
|
|
|
|
if (!list.length || !props.priceSetId) return
|
|
|
|
|
proxy.$modal.confirm(`确认从本套餐移除选中的 ${list.length} 个产品?`).then(() => {
|
|
|
|
|
return removeLabelProducts(props.priceSetId, list.join(','))
|
|
|
|
|
}).then(() => {
|
|
|
|
|
proxy.$modal.msgSuccess('已移除')
|
|
|
|
|
load()
|
|
|
|
|
emit('changed')
|
|
|
|
|
}).catch(() => {})
|
|
|
|
|
async function ensureChannelOptions() {
|
|
|
|
|
if (channelOptions.value.length) return
|
|
|
|
|
const res = await listChannelOptions({ blOpen: '1' })
|
|
|
|
|
channelOptions.value = res.data || []
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function loadBoundChannels() {
|
|
|
|
|
if (!bindProductId.value) {
|
|
|
|
|
boundChannels.value = []
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
bindListLoading.value = true
|
|
|
|
|
try {
|
|
|
|
|
const res = await listLabelProductChannels(bindProductId.value, 'AR')
|
|
|
|
|
boundChannels.value = res.data || []
|
|
|
|
|
} finally {
|
|
|
|
|
bindListLoading.value = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function openBindList(row) {
|
|
|
|
|
if (!row?.productId) return
|
|
|
|
|
bindProductId.value = row.productId
|
|
|
|
|
bindProductName.value = row.productName || String(row.productId)
|
|
|
|
|
boundChannels.value = []
|
|
|
|
|
bindListVisible.value = true
|
|
|
|
|
await loadBoundChannels()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function onBindListClosed() {
|
|
|
|
|
bindProductId.value = null
|
|
|
|
|
bindProductName.value = ''
|
|
|
|
|
boundChannels.value = []
|
|
|
|
|
addVisible.value = false
|
|
|
|
|
addChannelIds.value = []
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function openAddChannels() {
|
|
|
|
|
addChannelIds.value = []
|
|
|
|
|
addVisible.value = true
|
|
|
|
|
addLoading.value = true
|
|
|
|
|
try {
|
|
|
|
|
await ensureChannelOptions()
|
|
|
|
|
} finally {
|
|
|
|
|
addLoading.value = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function onAddClosed() {
|
|
|
|
|
addChannelIds.value = []
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function currentBoundIds() {
|
|
|
|
|
return (boundChannels.value || []).map(item => String(item.channelId)).filter(Boolean)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function submitAddChannels() {
|
|
|
|
|
if (!bindProductId.value) return
|
|
|
|
|
const selected = (addChannelIds.value || []).map(String).filter(Boolean)
|
|
|
|
|
if (!selected.length) {
|
|
|
|
|
proxy.$modal.msgWarning('请先选择要添加的渠道')
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
const merged = Array.from(new Set(currentBoundIds().concat(selected)))
|
|
|
|
|
addSaving.value = true
|
|
|
|
|
replaceLabelProductChannels(bindProductId.value, {
|
|
|
|
|
payType: 'AR',
|
|
|
|
|
channelIds: merged
|
|
|
|
|
}).then((res) => {
|
|
|
|
|
proxy.$modal.msgSuccess((res && res.msg) || '添加成功')
|
|
|
|
|
addVisible.value = false
|
|
|
|
|
return loadBoundChannels()
|
|
|
|
|
}).then(() => load()).catch(() => {}).finally(() => {
|
|
|
|
|
addSaving.value = false
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function removeBoundChannel(row) {
|
|
|
|
|
if (!bindProductId.value || !row?.channelId) return
|
|
|
|
|
const label = channelOptionLabel(row)
|
|
|
|
|
proxy.$modal.confirm(`确认移除渠道「${label}」?`).then(() => {
|
|
|
|
|
const remain = currentBoundIds().filter(id => id !== String(row.channelId))
|
|
|
|
|
return replaceLabelProductChannels(bindProductId.value, {
|
|
|
|
|
payType: 'AR',
|
|
|
|
|
channelIds: remain
|
|
|
|
|
})
|
|
|
|
|
}).then((res) => {
|
|
|
|
|
proxy.$modal.msgSuccess((res && res.msg) || '已移除')
|
|
|
|
|
return loadBoundChannels()
|
|
|
|
|
}).then(() => load()).catch(() => {})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
watch(() => [props.visible, props.priceSetId, props.version?.versionNo], ([vis]) => {
|
|
|
|
@@ -174,4 +374,15 @@ watch(() => [props.visible, props.priceSetId, props.version?.versionNo], ([vis])
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
align-items: center;
|
|
|
|
|
}
|
|
|
|
|
.bind-list-toolbar {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: flex-end;
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
}
|
|
|
|
|
.bind-hint {
|
|
|
|
|
margin: -4px 0 0 88px;
|
|
|
|
|
color: #909399;
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
line-height: 1.5;
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|