应付计费邮编改造
This commit is contained in:
@@ -51,3 +51,39 @@ export function delPayPostcode(groupIds) {
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
export function listPayPostcodeZone(query) {
|
||||
return request({ url: '/data/payPostcode/zones', method: 'get', params: query })
|
||||
}
|
||||
|
||||
export function addPayPostcodeZone(data) {
|
||||
return request({ url: '/data/payPostcode/zones', method: 'post', data })
|
||||
}
|
||||
|
||||
export function updatePayPostcodeZone(data) {
|
||||
return request({ url: '/data/payPostcode/zones', method: 'put', data })
|
||||
}
|
||||
|
||||
export function delPayPostcodeZone(query) {
|
||||
return request({ url: '/data/payPostcode/zones', method: 'delete', params: query })
|
||||
}
|
||||
|
||||
export function updatePayPostcodeCountry(data) {
|
||||
return request({ url: '/data/payPostcode/country', method: 'put', data })
|
||||
}
|
||||
|
||||
export function delPayPostcodeCountry(query) {
|
||||
return request({ url: '/data/payPostcode/country', method: 'delete', params: query })
|
||||
}
|
||||
|
||||
export function addPayPostcodeLine(data) {
|
||||
return request({ url: '/data/payPostcode/lines', method: 'post', data })
|
||||
}
|
||||
|
||||
export function updatePayPostcodeLine(data) {
|
||||
return request({ url: '/data/payPostcode/lines', method: 'put', data })
|
||||
}
|
||||
|
||||
export function delPayPostcodeLine(lineIds) {
|
||||
return request({ url: '/data/payPostcode/lines/' + lineIds, method: 'delete' })
|
||||
}
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
<template>
|
||||
<el-drawer v-model="visible" size="760px" append-to-body>
|
||||
<template #header>
|
||||
<div class="drawer-head">
|
||||
<span class="drawer-title">邮编明细</span>
|
||||
<span v-if="zone" class="drawer-sub">{{ zone.zoneName }}({{ total }} 条)</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="drawer-toolbar">
|
||||
<el-input v-model="query.postcode" clearable placeholder="搜索邮编" style="width: 200px" @keyup.enter="loadList" />
|
||||
<el-button type="primary" plain icon="Search" size="small" @click="loadList">搜索</el-button>
|
||||
<el-button type="primary" plain icon="Plus" size="small" @click="handleAdd" v-hasPermi="['data:payPostcode:edit']">新增邮编</el-button>
|
||||
</div>
|
||||
|
||||
<el-table v-loading="loading" :data="list" border row-key="lineId" empty-text="暂无邮编">
|
||||
<el-table-column label="地段" prop="cityName" min-width="140" show-overflow-tooltip>
|
||||
<template #default="{ row }">{{ row.cityName || row.regionName || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="邮编" prop="postcode" min-width="140" show-overflow-tooltip />
|
||||
<el-table-column label="截止邮编" prop="postcodeEnd" width="140" show-overflow-tooltip />
|
||||
<el-table-column label="操作" width="160" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="primary" icon="Edit" @click="handleEdit(row)" v-hasPermi="['data:payPostcode:edit']">编辑</el-button>
|
||||
<el-button link type="primary" icon="Delete" @click="handleDelete(row)" v-hasPermi="['data:payPostcode:remove']">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="drawer-pager">
|
||||
<el-pagination
|
||||
v-model:current-page="query.pageNum"
|
||||
v-model:page-size="query.pageSize"
|
||||
:total="total"
|
||||
:page-sizes="[20, 50, 100, 200]"
|
||||
layout="total, sizes, prev, pager, next"
|
||||
@size-change="loadList"
|
||||
@current-change="loadList"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<pay-postcode-form-dialog ref="formRef" @success="onChanged" />
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script setup name="PayPostcodeDrawer">
|
||||
import { listPayPostcodeLine, delPayPostcodeLine } from '@/api/data/payPostcode'
|
||||
import PayPostcodeFormDialog from './PayPostcodeFormDialog.vue'
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
const emit = defineEmits(['changed'])
|
||||
|
||||
const visible = ref(false)
|
||||
const loading = ref(false)
|
||||
const country = ref(null)
|
||||
const zone = ref(null)
|
||||
const list = ref([])
|
||||
const total = ref(0)
|
||||
const query = ref({ pageNum: 1, pageSize: 50, postcode: '' })
|
||||
const formRef = ref()
|
||||
|
||||
function open(countryRow, zoneRow) {
|
||||
country.value = countryRow
|
||||
zone.value = zoneRow
|
||||
query.value = { pageNum: 1, pageSize: 50, postcode: '' }
|
||||
visible.value = true
|
||||
loadList()
|
||||
}
|
||||
|
||||
function loadList() {
|
||||
if (!zone.value) {
|
||||
list.value = []
|
||||
total.value = 0
|
||||
return
|
||||
}
|
||||
loading.value = true
|
||||
listPayPostcodeLine({
|
||||
countryCode: country.value?.countryCode,
|
||||
countryName: country.value?.countryName,
|
||||
zoneName: zone.value.zoneName,
|
||||
postcode: query.value.postcode || undefined,
|
||||
pageNum: query.value.pageNum,
|
||||
pageSize: query.value.pageSize
|
||||
}).then(res => {
|
||||
list.value = res.rows || []
|
||||
total.value = res.total || 0
|
||||
}).catch(() => {
|
||||
list.value = []
|
||||
total.value = 0
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
function onChanged() {
|
||||
loadList()
|
||||
emit('changed')
|
||||
}
|
||||
|
||||
function handleAdd() {
|
||||
formRef.value.open(country.value, zone.value)
|
||||
}
|
||||
|
||||
function handleEdit(row) {
|
||||
formRef.value.open(country.value, zone.value, row)
|
||||
}
|
||||
|
||||
function handleDelete(row) {
|
||||
proxy.$modal.confirm('删除邮编「' + row.postcode + '」?').then(() => delPayPostcodeLine(row.lineId)).then(() => {
|
||||
proxy.$modal.msgSuccess('删除成功')
|
||||
onChanged()
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.drawer-head { display: flex; align-items: center; gap: 10px; }
|
||||
.drawer-title { font-size: 16px; font-weight: 600; }
|
||||
.drawer-sub { color: #909399; font-size: 13px; }
|
||||
.drawer-toolbar { margin-bottom: 12px; display: flex; align-items: center; gap: 8px; }
|
||||
.drawer-pager { margin-top: 16px; display: flex; justify-content: flex-end; }
|
||||
</style>
|
||||
@@ -0,0 +1,78 @@
|
||||
<template>
|
||||
<el-dialog :title="title" v-model="visible" width="460px" append-to-body>
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="90px">
|
||||
<el-form-item label="分区">
|
||||
<span>{{ zone?.zoneName || '-' }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="地段" prop="cityName">
|
||||
<el-input v-model="form.cityName" maxlength="200" placeholder="例如罗马,可空" />
|
||||
</el-form-item>
|
||||
<el-form-item label="邮编" prop="postcode">
|
||||
<el-input v-model="form.postcode" maxlength="200" placeholder="邮编或前缀" />
|
||||
</el-form-item>
|
||||
<el-form-item label="截止邮编" prop="postcodeEnd">
|
||||
<el-input v-model="form.postcodeEnd" maxlength="200" placeholder="区间结束,可空" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button type="primary" @click="submit">确定</el-button>
|
||||
<el-button @click="visible = false">取消</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup name="PayPostcodeFormDialog">
|
||||
import { addPayPostcodeLine, updatePayPostcodeLine } from '@/api/data/payPostcode'
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
const emit = defineEmits(['success'])
|
||||
|
||||
const visible = ref(false)
|
||||
const title = ref('')
|
||||
const formRef = ref()
|
||||
const form = ref({})
|
||||
const zone = ref(null)
|
||||
const rules = {
|
||||
postcode: [{ required: true, message: '请填写邮编', trigger: 'blur' }]
|
||||
}
|
||||
|
||||
function open(country, zoneRow, row) {
|
||||
zone.value = zoneRow
|
||||
form.value = row
|
||||
? {
|
||||
lineId: row.lineId,
|
||||
countryCode: country?.countryCode,
|
||||
countryName: country?.countryName,
|
||||
zoneName: zoneRow?.zoneName,
|
||||
cityName: row.cityName || row.regionName || '',
|
||||
postcode: row.postcode,
|
||||
postcodeEnd: row.postcodeEnd || ''
|
||||
}
|
||||
: {
|
||||
countryCode: country?.countryCode,
|
||||
countryName: country?.countryName,
|
||||
zoneName: zoneRow?.zoneName,
|
||||
cityName: '',
|
||||
postcode: '',
|
||||
postcodeEnd: ''
|
||||
}
|
||||
title.value = row ? '编辑邮编' : '新增邮编'
|
||||
visible.value = true
|
||||
nextTick(() => formRef.value?.clearValidate())
|
||||
}
|
||||
|
||||
function submit() {
|
||||
formRef.value.validate(valid => {
|
||||
if (!valid) return
|
||||
const payload = { ...form.value, postcode: String(form.value.postcode || '').trim() }
|
||||
const req = payload.lineId ? updatePayPostcodeLine(payload) : addPayPostcodeLine(payload)
|
||||
req.then(() => {
|
||||
proxy.$modal.msgSuccess(payload.lineId ? '修改成功' : '新增成功')
|
||||
visible.value = false
|
||||
emit('success')
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
@@ -0,0 +1,146 @@
|
||||
<template>
|
||||
<el-drawer v-model="visible" size="860px" append-to-body>
|
||||
<template #header>
|
||||
<div class="drawer-head">
|
||||
<span class="drawer-title">国家分区</span>
|
||||
<span v-if="country" class="drawer-sub">
|
||||
{{ country.countryName || '未填写国家' }}
|
||||
<template v-if="country.countryCode">({{ country.countryCode }})</template>
|
||||
,共 {{ zoneList.length }} 个分区
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div v-loading="loading">
|
||||
<div class="zone-toolbar">
|
||||
<el-button type="primary" plain icon="Plus" size="small" :disabled="!country" @click="handleAdd" v-hasPermi="['data:payPostcode:edit']">新增分区</el-button>
|
||||
</div>
|
||||
<el-table :data="zoneList" border row-key="zoneName" :empty-text="country ? '这个国家还没有分区' : '请选择国家'">
|
||||
<el-table-column label="分区" min-width="180" align="center">
|
||||
<template #default="{ row }">
|
||||
<div class="zone-country-cell">
|
||||
<div class="zone-title">{{ row.zoneName }}</div>
|
||||
<el-button link type="primary" class="zone-click" @click="openLines(row)" v-hasPermi="['data:payPostcode:query']">分区点击</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="邮编属性" width="120" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag v-if="row.coverage" size="small" :type="coverageType(firstPart(row.coverage))">{{ coverageLabel(firstPart(row.coverage)) }}</el-tag>
|
||||
<el-tag v-else size="small" type="info">普通</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="邮编数" prop="lineCount" width="100" align="center" />
|
||||
<el-table-column label="操作" width="220" align="center" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="primary" icon="View" @click="openLines(row)" v-hasPermi="['data:payPostcode:query']">明细</el-button>
|
||||
<el-button link type="primary" icon="Edit" @click="handleEdit(row)" v-hasPermi="['data:payPostcode:edit']">编辑</el-button>
|
||||
<el-button link type="primary" icon="Delete" @click="handleDelete(row)" v-hasPermi="['data:payPostcode:remove']">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<pay-zone-form-dialog ref="formRef" :country="country" @success="onChanged" />
|
||||
<pay-postcode-drawer ref="lineRef" @changed="onChanged" />
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script setup name="PayZoneDrawer">
|
||||
import { listPayPostcodeZone, delPayPostcodeZone } from '@/api/data/payPostcode'
|
||||
import PayZoneFormDialog from './PayZoneFormDialog.vue'
|
||||
import PayPostcodeDrawer from './PayPostcodeDrawer.vue'
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
const emit = defineEmits(['changed'])
|
||||
|
||||
const visible = ref(false)
|
||||
const loading = ref(false)
|
||||
const country = ref(null)
|
||||
const zoneList = ref([])
|
||||
const formRef = ref()
|
||||
const lineRef = ref()
|
||||
|
||||
const coverageOptions = [
|
||||
{ label: '可达', value: 'REACH' },
|
||||
{ label: '不可达', value: 'UNREACH' },
|
||||
{ label: '偏远', value: 'REMOTE' }
|
||||
]
|
||||
|
||||
function firstPart(value) {
|
||||
return String(value || '').split('、').map(item => item.trim()).filter(Boolean)[0] || ''
|
||||
}
|
||||
|
||||
function coverageLabel(value) {
|
||||
return coverageOptions.find(item => item.value === value)?.label || value
|
||||
}
|
||||
|
||||
function coverageType(value) {
|
||||
if (value === 'UNREACH') return 'danger'
|
||||
if (value === 'REMOTE') return 'warning'
|
||||
return 'success'
|
||||
}
|
||||
|
||||
function open(row) {
|
||||
country.value = row
|
||||
visible.value = true
|
||||
loadList()
|
||||
}
|
||||
|
||||
function loadList() {
|
||||
if (!country.value) {
|
||||
zoneList.value = []
|
||||
return
|
||||
}
|
||||
loading.value = true
|
||||
listPayPostcodeZone({ countryCode: country.value.countryCode, countryName: country.value.countryName }).then(res => {
|
||||
zoneList.value = res.data || []
|
||||
}).catch(() => {
|
||||
zoneList.value = []
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
function onChanged() {
|
||||
loadList()
|
||||
emit('changed')
|
||||
}
|
||||
|
||||
function handleAdd() {
|
||||
formRef.value.open()
|
||||
}
|
||||
|
||||
function handleEdit(row) {
|
||||
formRef.value.open(row)
|
||||
}
|
||||
|
||||
function handleDelete(row) {
|
||||
proxy.$modal.confirm('删除分区「' + row.zoneName + '」及其邮编?').then(() => {
|
||||
return delPayPostcodeZone({
|
||||
countryCode: country.value.countryCode,
|
||||
countryName: country.value.countryName,
|
||||
zoneName: row.zoneName
|
||||
})
|
||||
}).then(() => {
|
||||
proxy.$modal.msgSuccess('删除成功')
|
||||
onChanged()
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
function openLines(row) {
|
||||
lineRef.value.open(country.value, row)
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.drawer-head { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
|
||||
.drawer-title { font-size: 16px; font-weight: 600; }
|
||||
.drawer-sub { color: #909399; font-size: 13px; }
|
||||
.zone-toolbar { display: flex; align-items: center; margin-bottom: 12px; }
|
||||
.zone-country-cell { display: flex; flex-direction: column; align-items: center; gap: 2px; padding: 4px 0; }
|
||||
.zone-title { font-weight: 500; }
|
||||
.zone-click { padding: 0; height: auto; font-size: 12px; }
|
||||
</style>
|
||||
@@ -0,0 +1,83 @@
|
||||
<template>
|
||||
<el-dialog :title="title" v-model="visible" width="480px" append-to-body>
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
|
||||
<el-form-item label="国家">
|
||||
<span>{{ country?.countryName || '-' }}<template v-if="country?.countryCode">({{ country.countryCode }})</template></span>
|
||||
</el-form-item>
|
||||
<el-form-item label="分区" prop="zoneName">
|
||||
<el-input v-model="form.zoneName" maxlength="200" placeholder="例如 mainland" />
|
||||
</el-form-item>
|
||||
<el-form-item label="邮编属性" prop="coverage">
|
||||
<el-select v-model="form.coverage" clearable placeholder="普通" style="width: 100%">
|
||||
<el-option v-for="item in coverageOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button type="primary" @click="submit">确定</el-button>
|
||||
<el-button @click="visible = false">取消</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup name="PayZoneFormDialog">
|
||||
import { addPayPostcodeZone, updatePayPostcodeZone } from '@/api/data/payPostcode'
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
const props = defineProps({
|
||||
country: { type: Object, default: null }
|
||||
})
|
||||
const emit = defineEmits(['success'])
|
||||
|
||||
const visible = ref(false)
|
||||
const title = ref('')
|
||||
const formRef = ref()
|
||||
const form = ref({})
|
||||
const rules = {
|
||||
zoneName: [{ required: true, message: '请填写分区', trigger: 'blur' }]
|
||||
}
|
||||
const coverageOptions = [
|
||||
{ label: '可达', value: 'REACH' },
|
||||
{ label: '不可达', value: 'UNREACH' },
|
||||
{ label: '偏远', value: 'REMOTE' }
|
||||
]
|
||||
|
||||
function open(row) {
|
||||
form.value = row
|
||||
? {
|
||||
countryCode: props.country?.countryCode,
|
||||
countryName: props.country?.countryName,
|
||||
oldZoneName: row.zoneName,
|
||||
zoneName: row.zoneName,
|
||||
coverage: String(row.coverage || '').split('、')[0] || ''
|
||||
}
|
||||
: {
|
||||
countryCode: props.country?.countryCode,
|
||||
countryName: props.country?.countryName,
|
||||
zoneName: '',
|
||||
coverage: ''
|
||||
}
|
||||
title.value = row ? '编辑分区' : '新增分区'
|
||||
visible.value = true
|
||||
nextTick(() => formRef.value?.clearValidate())
|
||||
}
|
||||
|
||||
function submit() {
|
||||
formRef.value.validate(valid => {
|
||||
if (!valid) return
|
||||
const payload = {
|
||||
...form.value,
|
||||
zoneName: String(form.value.zoneName || '').trim(),
|
||||
coverage: form.value.coverage || ''
|
||||
}
|
||||
const req = payload.oldZoneName ? updatePayPostcodeZone(payload) : addPayPostcodeZone(payload)
|
||||
req.then(() => {
|
||||
proxy.$modal.msgSuccess(payload.oldZoneName ? '修改成功' : '新增成功')
|
||||
visible.value = false
|
||||
emit('success')
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
@@ -1,40 +1,41 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-alert
|
||||
type="info"
|
||||
:closable="false"
|
||||
show-icon
|
||||
class="mb16"
|
||||
title="每个国家一行。点「分区」维护分区和邮编。偏远分区按目的国家参与应付偏远附加费;某个产品的报价表自己已经挂了偏远名单时,仍只用那一份。"
|
||||
/>
|
||||
|
||||
<basic-table
|
||||
ref="tableRef"
|
||||
:columns="columns"
|
||||
:search-columns="searchColumns"
|
||||
:query="queryList"
|
||||
row-key="groupId"
|
||||
selection
|
||||
search-action-align="right"
|
||||
>
|
||||
<template #toolbar="{ ids, selection }">
|
||||
<template #toolbar>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Upload" @click="open = true" v-hasPermi="['data:payPostcode:import']">导入</el-button>
|
||||
<el-button type="primary" plain icon="Upload" @click="open = true" v-hasPermi="['data:payPostcode:import']">导入邮编</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="success" plain icon="Edit" :disabled="selection.length !== 1" @click="handleUpdate(selection[0])" v-hasPermi="['data:payPostcode:edit']">修改</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="danger" plain icon="Delete" :disabled="!ids.length" @click="handleDelete(ids)" v-hasPermi="['data:payPostcode:remove']">删除</el-button>
|
||||
</el-col>
|
||||
</template>
|
||||
<template #col-configKind="{ row }">
|
||||
<el-tag size="small" :type="row.configKind === 'FEE' ? 'warning' : 'success'">{{ row.configKind === 'FEE' ? '附加费' : '邮编分区' }}</el-tag>
|
||||
</template>
|
||||
<template #col-coverage="{ row }">
|
||||
<el-tag v-if="row.coverage" size="small" :type="coverageType(row.coverage)">{{ coverageLabel(row.coverage) }}</el-tag>
|
||||
<template v-if="coverageParts(row.coverage).length">
|
||||
<el-tag v-for="item in coverageParts(row.coverage)" :key="item" size="small" class="cov-tag" :type="coverageType(item)">{{ coverageLabel(item) }}</el-tag>
|
||||
</template>
|
||||
<el-tag v-else size="small" type="info">普通</el-tag>
|
||||
</template>
|
||||
<template #col-action="{ row }">
|
||||
<el-button link type="primary" @click="openLines(row)" v-hasPermi="['data:payPostcode:query']">邮编</el-button>
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(row)" v-hasPermi="['data:payPostcode:edit']">修改</el-button>
|
||||
<el-button link type="danger" @click="handleDelete([row.groupId])" v-hasPermi="['data:payPostcode:remove']">删除</el-button>
|
||||
<el-button link type="primary" icon="List" @click.stop="openZones(row)" v-hasPermi="['data:payPostcode:query']">分区</el-button>
|
||||
<el-button link type="primary" icon="Edit" @click.stop="handleUpdate(row)" v-hasPermi="['data:payPostcode:edit']">修改</el-button>
|
||||
<el-button link type="danger" icon="Delete" @click.stop="handleClear(row)" v-hasPermi="['data:payPostcode:remove']">清空</el-button>
|
||||
</template>
|
||||
</basic-table>
|
||||
|
||||
<el-dialog v-model="open" title="导入应付邮编" width="480px" append-to-body destroy-on-close>
|
||||
<p class="import-tip">表头里带邮编或附加费的工作表会被导入。目录里的分区表会带上服务类型、国家、销售产品和报价表。可达、可派记为可达,不可达记为不可达,偏远和岛屿记为偏远。渠道名称从标题里识别,例如 Royal Mail、EVRI。邮编末尾的 * 会去掉,同一国家、同一渠道、同一附加费、金额相同只保留一条。</p>
|
||||
<p class="import-tip">导入后按国家归成一行。表头里带邮编或附加费的工作表会被导入。可达、可派记为可达,不可达记为不可达,偏远和岛屿记为偏远。</p>
|
||||
<el-upload :auto-upload="false" :limit="1" accept=".xlsx" :on-change="onFile" :on-remove="() => file = null">
|
||||
<el-button type="primary" plain>选择 xlsx</el-button>
|
||||
</el-upload>
|
||||
@@ -44,105 +45,44 @@
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="修改应付邮编" v-model="editOpen" width="560px" append-to-body destroy-on-close>
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
|
||||
<el-form-item label="服务类型" prop="serviceType">
|
||||
<el-input v-model="form.serviceType" maxlength="100" placeholder="目录中的服务类型" />
|
||||
<el-dialog title="修改国家邮编" v-model="editOpen" width="520px" append-to-body destroy-on-close>
|
||||
<el-form ref="formRef" :model="form" label-width="100px">
|
||||
<el-form-item label="国家">
|
||||
<span>{{ form.countryName || '-' }}<template v-if="form.countryCode">({{ form.countryCode }})</template></span>
|
||||
</el-form-item>
|
||||
<el-form-item label="目录国家" prop="catalogCountry">
|
||||
<el-input v-model="form.catalogCountry" maxlength="100" placeholder="目录中的国家" />
|
||||
<el-form-item label="服务类型">
|
||||
<el-input v-model="form.serviceType" maxlength="100" />
|
||||
</el-form-item>
|
||||
<el-form-item label="销售产品" prop="productName">
|
||||
<el-input v-model="form.productName" maxlength="200" placeholder="目录中的销售产品" />
|
||||
<el-form-item label="销售产品">
|
||||
<el-input v-model="form.productName" maxlength="200" />
|
||||
</el-form-item>
|
||||
<el-form-item label="报价表" prop="quoteSheet">
|
||||
<el-input v-model="form.quoteSheet" maxlength="200" placeholder="目录中的报价工作表" />
|
||||
</el-form-item>
|
||||
<el-form-item label="渠道名称" prop="channelName">
|
||||
<el-form-item label="渠道名称">
|
||||
<el-input v-model="form.channelName" maxlength="100" placeholder="如 Royal Mail、EVRI" />
|
||||
</el-form-item>
|
||||
<el-form-item label="类型" prop="configKind">
|
||||
<el-select v-model="form.configKind" style="width: 100%">
|
||||
<el-option v-for="item in kindOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="邮编属性" prop="coverage">
|
||||
<el-select v-model="form.coverage" clearable placeholder="普通分区" style="width: 100%">
|
||||
<el-option v-for="item in coverageOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="国家" prop="countryName">
|
||||
<el-input v-model="form.countryName" maxlength="100" />
|
||||
</el-form-item>
|
||||
<el-form-item label="国家二字码" prop="countryCode">
|
||||
<el-input v-model="form.countryCode" maxlength="20" />
|
||||
</el-form-item>
|
||||
<el-form-item label="附加费" prop="feeName">
|
||||
<el-input v-model="form.feeName" maxlength="200" />
|
||||
</el-form-item>
|
||||
<el-form-item label="金额原文" prop="feeText">
|
||||
<el-input v-model="form.feeText" maxlength="200" />
|
||||
</el-form-item>
|
||||
<el-form-item label="区块标题" prop="blockTitle">
|
||||
<el-input v-model="form.blockTitle" maxlength="200" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" :rows="2" maxlength="500" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<p class="import-tip">保存后,这个国家下的分区都使用这里的服务类型、销售产品和渠道。报价表保持不变。偏远附加费按目的国家的偏远邮编匹配,再用价卡上的分区或邮编前缀选定费用档。</p>
|
||||
<template #footer>
|
||||
<el-button type="primary" @click="submitEdit">确定</el-button>
|
||||
<el-button @click="editOpen = false">取消</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-drawer v-model="lineOpen" :title="lineTitle" size="72%" append-to-body>
|
||||
<el-input v-model="lineQuery.postcode" clearable placeholder="筛选邮编" class="line-filter" @change="loadLines" />
|
||||
<el-table :data="lineRows" border size="small" max-height="640" empty-text="没有邮编">
|
||||
<el-table-column prop="countryName" label="国家" width="90" show-overflow-tooltip />
|
||||
<el-table-column prop="regionName" label="地区" min-width="140" show-overflow-tooltip />
|
||||
<el-table-column prop="regionNameCn" label="地区中文" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column prop="cityName" label="城市" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column prop="regionCode" label="地区代码" width="100" show-overflow-tooltip />
|
||||
<el-table-column prop="zoneName" label="分区" min-width="140" show-overflow-tooltip />
|
||||
<el-table-column prop="postcode" label="邮编" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column prop="postcodeRaw" label="邮编原文" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column prop="postcodeEnd" label="截止邮编" width="120" show-overflow-tooltip />
|
||||
<el-table-column prop="extraText" label="其余列" min-width="180" show-overflow-tooltip />
|
||||
</el-table>
|
||||
<el-pagination
|
||||
v-if="lineTotal > lineQuery.pageSize"
|
||||
class="line-page"
|
||||
layout="total, prev, pager, next"
|
||||
:total="lineTotal"
|
||||
:page-size="lineQuery.pageSize"
|
||||
v-model:current-page="lineQuery.pageNum"
|
||||
@current-change="loadLines"
|
||||
/>
|
||||
</el-drawer>
|
||||
<pay-zone-drawer ref="zoneDrawerRef" @changed="reload" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="PayPostcode">
|
||||
import { listPayPostcode, getPayPostcode, updatePayPostcode, listPayPostcodeLine, importPayPostcode, delPayPostcode } from '@/api/data/payPostcode'
|
||||
import { listPayPostcode, importPayPostcode, updatePayPostcodeCountry, delPayPostcodeCountry } from '@/api/data/payPostcode'
|
||||
import PayZoneDrawer from './components/PayZoneDrawer.vue'
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
const tableRef = ref()
|
||||
const formRef = ref()
|
||||
const zoneDrawerRef = ref()
|
||||
const open = ref(false)
|
||||
const editOpen = ref(false)
|
||||
const importing = ref(false)
|
||||
const file = ref(null)
|
||||
const lineOpen = ref(false)
|
||||
const lineTitle = ref('邮编')
|
||||
const lineRows = ref([])
|
||||
const lineTotal = ref(0)
|
||||
const lineQuery = reactive({ groupId: undefined, postcode: '', pageNum: 1, pageSize: 20 })
|
||||
|
||||
const kindOptions = [
|
||||
{ label: '附加费', value: 'FEE' },
|
||||
{ label: '邮编分区', value: 'ZONE' }
|
||||
]
|
||||
const form = ref({})
|
||||
|
||||
const coverageOptions = [
|
||||
{ label: '可达', value: 'REACH' },
|
||||
@@ -150,8 +90,12 @@ const coverageOptions = [
|
||||
{ label: '偏远', value: 'REMOTE' }
|
||||
]
|
||||
|
||||
function coverageParts(value) {
|
||||
return String(value || '').split('、').map(item => item.trim()).filter(Boolean)
|
||||
}
|
||||
|
||||
function coverageLabel(value) {
|
||||
return coverageOptions.find(item => item.value === value)?.label || ''
|
||||
return coverageOptions.find(item => item.value === value)?.label || value
|
||||
}
|
||||
|
||||
function coverageType(value) {
|
||||
@@ -160,47 +104,39 @@ function coverageType(value) {
|
||||
return 'success'
|
||||
}
|
||||
|
||||
function firstPart(value) {
|
||||
return coverageParts(value)[0] || ''
|
||||
}
|
||||
|
||||
const searchColumns = [
|
||||
{ label: '销售产品', prop: 'productName', component: 'input', placeholder: '销售产品', width: '140px' },
|
||||
{ label: '报价表', prop: 'quoteSheet', component: 'input', placeholder: '报价表', width: '160px' },
|
||||
{ label: '来源文件', prop: 'sourceFile', component: 'input', placeholder: '文件名', width: '180px' },
|
||||
{ label: '工作表', prop: 'sheetName', component: 'input', placeholder: '工作表', width: '160px' },
|
||||
{ label: '国家', prop: 'countryName', component: 'input', placeholder: '国家或二字码', width: '140px' },
|
||||
{ label: '国家', prop: 'countryName', component: 'input', placeholder: '国家或二字码', width: '160px' },
|
||||
{ label: '服务类型', prop: 'serviceType', component: 'input', placeholder: '服务类型', width: '140px' },
|
||||
{ label: '销售产品', prop: 'productName', component: 'input', placeholder: '销售产品', width: '160px' },
|
||||
{ label: '渠道', prop: 'channelName', component: 'input', placeholder: '渠道名称', width: '140px' },
|
||||
{ label: '类型', prop: 'configKind', component: 'select', options: kindOptions, placeholder: '全部', width: '130px', props: { clearable: true } },
|
||||
{ label: '邮编属性', prop: 'coverage', component: 'select', options: coverageOptions, placeholder: '全部', width: '130px', props: { clearable: true } },
|
||||
{ label: '附加费', prop: 'feeName', component: 'input', placeholder: '附加费名称', width: '160px' },
|
||||
{ label: '邮编', prop: 'postcode', component: 'input', placeholder: '邮编', width: '140px' }
|
||||
]
|
||||
|
||||
const columns = [
|
||||
{ label: '类型', prop: 'configKind', width: 100, align: 'center', slot: 'configKind' },
|
||||
{ label: '邮编属性', prop: 'coverage', width: 100, align: 'center', slot: 'coverage' },
|
||||
{ label: '服务类型', prop: 'serviceType', width: 100, showOverflowTooltip: true },
|
||||
{ label: '目录国家', prop: 'catalogCountry', width: 100, showOverflowTooltip: true },
|
||||
{ label: '销售产品', prop: 'productName', minWidth: 120, showOverflowTooltip: true },
|
||||
{ label: '报价表', prop: 'quoteSheet', minWidth: 140, showOverflowTooltip: true },
|
||||
{ label: '渠道名称', prop: 'channelName', minWidth: 120, showOverflowTooltip: true },
|
||||
{ label: '国家', prop: 'countryName', width: 90, showOverflowTooltip: true },
|
||||
{ label: '工作表', prop: 'sheetName', minWidth: 140, showOverflowTooltip: true },
|
||||
{ label: '区块', prop: 'blockTitle', minWidth: 180, showOverflowTooltip: true },
|
||||
{ label: '附加费', prop: 'feeName', minWidth: 140, showOverflowTooltip: true },
|
||||
{ label: '金额原文', prop: 'feeText', minWidth: 140, showOverflowTooltip: true },
|
||||
{ label: '邮编行数', prop: 'lineCount', width: 100, align: 'right' },
|
||||
{ label: '来源文件', prop: 'sourceFile', minWidth: 220, showOverflowTooltip: true },
|
||||
{ label: '国家', prop: 'countryName', minWidth: 120, showOverflowTooltip: true },
|
||||
{ label: '邮编属性', prop: 'coverage', minWidth: 140, align: 'center', slot: 'coverage' },
|
||||
{ label: '服务类型', prop: 'serviceType', minWidth: 120, showOverflowTooltip: true },
|
||||
{ label: '销售产品', prop: 'productName', minWidth: 140, showOverflowTooltip: true },
|
||||
{ label: '渠道名称', prop: 'channelName', minWidth: 140, showOverflowTooltip: true },
|
||||
{ label: '分区数', prop: 'zoneCount', width: 90, align: 'center' },
|
||||
{ label: '邮编总数', prop: 'lineCount', width: 100, align: 'center' },
|
||||
{ label: '导入时间', prop: 'createTime', width: 170, align: 'center' },
|
||||
{ label: '操作', prop: 'action', width: 170, align: 'center', fixed: 'right', slot: 'action', showInToolbar: false }
|
||||
{ label: '操作', prop: 'action', width: 200, align: 'center', fixed: 'right', slot: 'action', showInToolbar: false }
|
||||
]
|
||||
|
||||
const form = ref({})
|
||||
const rules = {
|
||||
configKind: [{ required: true, message: '请选择类型', trigger: 'change' }]
|
||||
}
|
||||
|
||||
function queryList(params) {
|
||||
return listPayPostcode(params)
|
||||
}
|
||||
|
||||
function reload() {
|
||||
tableRef.value?.refresh?.() || tableRef.value?.reload?.()
|
||||
}
|
||||
|
||||
function onFile(uploadFile) {
|
||||
file.value = uploadFile.raw
|
||||
}
|
||||
@@ -215,66 +151,48 @@ function submitImport() {
|
||||
proxy.$modal.msgSuccess(res.data?.message || res.msg || '导入完成')
|
||||
open.value = false
|
||||
file.value = null
|
||||
tableRef.value?.reload?.() || tableRef.value?.getList?.()
|
||||
reload()
|
||||
}).finally(() => {
|
||||
importing.value = false
|
||||
})
|
||||
}
|
||||
|
||||
function openZones(row) {
|
||||
zoneDrawerRef.value.open(row)
|
||||
}
|
||||
|
||||
function handleUpdate(row) {
|
||||
getPayPostcode(row.groupId).then(res => {
|
||||
form.value = Object.assign({}, res.data || {})
|
||||
editOpen.value = true
|
||||
})
|
||||
form.value = {
|
||||
countryCode: row.countryCode,
|
||||
countryName: row.countryName,
|
||||
serviceType: firstPart(row.serviceType),
|
||||
productName: firstPart(row.productName),
|
||||
channelName: firstPart(row.channelName)
|
||||
}
|
||||
editOpen.value = true
|
||||
}
|
||||
|
||||
function submitEdit() {
|
||||
formRef.value.validate(valid => {
|
||||
if (!valid) return
|
||||
updatePayPostcode(form.value).then(() => {
|
||||
proxy.$modal.msgSuccess('保存成功')
|
||||
editOpen.value = false
|
||||
tableRef.value?.reload?.()
|
||||
})
|
||||
updatePayPostcodeCountry(form.value).then(() => {
|
||||
proxy.$modal.msgSuccess('保存成功')
|
||||
editOpen.value = false
|
||||
reload()
|
||||
})
|
||||
}
|
||||
|
||||
function openLines(row) {
|
||||
lineTitle.value = `${coverageLabel(row.coverage)} ${row.countryName || ''} ${row.feeName || row.blockTitle || row.sheetName}`.trim()
|
||||
lineQuery.groupId = row.groupId
|
||||
lineQuery.postcode = ''
|
||||
lineQuery.pageNum = 1
|
||||
lineOpen.value = true
|
||||
loadLines()
|
||||
}
|
||||
|
||||
function loadLines() {
|
||||
listPayPostcodeLine(lineQuery).then(res => {
|
||||
lineRows.value = res.rows || []
|
||||
lineTotal.value = res.total || 0
|
||||
})
|
||||
}
|
||||
|
||||
function handleDelete(ids) {
|
||||
proxy.$modal.confirm('确认删除选中的邮编配置吗?邮编明细会一起删除。').then(() => delPayPostcode(ids)).then(() => {
|
||||
proxy.$modal.msgSuccess('删除成功')
|
||||
tableRef.value?.reload?.() || tableRef.value?.getList?.()
|
||||
function handleClear(row) {
|
||||
const name = row.countryName || row.countryCode
|
||||
proxy.$modal.confirm('清空「' + name + '」下的全部分区和邮编?计费将不再使用这些邮编。').then(() => {
|
||||
return delPayPostcodeCountry({ countryCode: row.countryCode, countryName: row.countryName })
|
||||
}).then(() => {
|
||||
proxy.$modal.msgSuccess('已清空')
|
||||
reload()
|
||||
}).catch(() => {})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.import-tip {
|
||||
margin: 0 0 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
line-height: 1.5;
|
||||
}
|
||||
.line-filter {
|
||||
width: 240px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.line-page {
|
||||
margin-top: 8px;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.mb16 { margin-bottom: 16px; }
|
||||
.import-tip { margin: 0 0 12px; color: var(--el-text-color-secondary); line-height: 1.5; }
|
||||
.cov-tag { margin: 0 4px 4px 0; }
|
||||
</style>
|
||||
|
||||
@@ -420,6 +420,7 @@ const keyword = ref('')
|
||||
const found = ref([])
|
||||
const products = ref([])
|
||||
const feeOptions = ref([])
|
||||
const feeCounts = ref({})
|
||||
const zoneOptions = ref([])
|
||||
const enabled = reactive({})
|
||||
const groups = reactive({})
|
||||
@@ -455,6 +456,7 @@ function reset() {
|
||||
found.value = []
|
||||
products.value = []
|
||||
feeOptions.value = []
|
||||
feeCounts.value = {}
|
||||
zoneOptions.value = []
|
||||
blankGroups()
|
||||
saving.value = false
|
||||
@@ -504,24 +506,32 @@ function loadFees() {
|
||||
const bound = products.value.slice()
|
||||
if (!bound.length) {
|
||||
feeOptions.value = []
|
||||
feeCounts.value = {}
|
||||
zoneOptions.value = keepZones([])
|
||||
return
|
||||
return Promise.resolve()
|
||||
}
|
||||
Promise.all(bound.map(item => getPay(item.feeProductId || item.productId).catch(() => null))).then(results => {
|
||||
return Promise.all(bound.map(item => getPay(item.feeProductId || item.productId).catch(() => null))).then(results => {
|
||||
if (token !== feeToken) return
|
||||
const map = new Map()
|
||||
const counts = {}
|
||||
const zones = new Set()
|
||||
results.forEach(res => {
|
||||
const local = {}
|
||||
const rows = res?.data?.surcharges || []
|
||||
rows.forEach(fee => {
|
||||
const name = String(fee.feeName || '').trim()
|
||||
const zone = String(fee.zoneName || '').trim()
|
||||
if (zone) zones.add(zone)
|
||||
if (!name || map.has(name)) return
|
||||
map.set(name, { feeName: name, scId: fee.scId || '' })
|
||||
if (!name) return
|
||||
local[name] = (local[name] || 0) + 1
|
||||
if (!map.has(name)) map.set(name, { feeName: name, scId: fee.scId || '' })
|
||||
})
|
||||
Object.keys(local).forEach(name => {
|
||||
counts[name] = Math.max(counts[name] || 0, local[name])
|
||||
})
|
||||
})
|
||||
zoneOptions.value = keepZones([...zones])
|
||||
feeCounts.value = counts
|
||||
for (const row of groups.ALWAYS || []) {
|
||||
const name = String(row.feeName || '').trim()
|
||||
if (name && !map.has(name)) map.set(name, { feeName: name, scId: row.scId || '' })
|
||||
@@ -659,35 +669,48 @@ function save() {
|
||||
}
|
||||
}
|
||||
if (enabled.ALWAYS) {
|
||||
const names = new Set()
|
||||
for (const row of groups.ALWAYS) {
|
||||
const name = String(row.feeName || '').trim()
|
||||
if (!name) {
|
||||
if (!String(row.feeName || '').trim()) {
|
||||
proxy.$modal.msgError('每票都收要选择费用项')
|
||||
return
|
||||
}
|
||||
if (names.has(name)) {
|
||||
proxy.$modal.msgError('每票都收的费用项不能重复:' + name)
|
||||
return
|
||||
}
|
||||
names.add(name)
|
||||
}
|
||||
}
|
||||
saving.value = true
|
||||
savePaySurchargeRule({
|
||||
products: products.value.map(item => ({
|
||||
productId: item.productId,
|
||||
productName: item.productName
|
||||
})),
|
||||
rules: payloadRules()
|
||||
}).then(() => {
|
||||
proxy.$modal.msgSuccess('已保存,计算时按当前生效版本上的附加费执行')
|
||||
visible.value = false
|
||||
loadFees().then(() => {
|
||||
const duplicated = sameTypeFees()
|
||||
if (duplicated.length) {
|
||||
proxy.$modal.alert('存在两笔同类型的附加费:' + duplicated.join('、'))
|
||||
return
|
||||
}
|
||||
return savePaySurchargeRule({
|
||||
products: products.value.map(item => ({
|
||||
productId: item.productId,
|
||||
productName: item.productName
|
||||
})),
|
||||
rules: payloadRules()
|
||||
}).then(() => {
|
||||
proxy.$modal.msgSuccess('已保存,计算时按当前生效版本上的附加费执行')
|
||||
visible.value = false
|
||||
})
|
||||
}).finally(() => {
|
||||
saving.value = false
|
||||
})
|
||||
}
|
||||
|
||||
function sameTypeFees() {
|
||||
if (!enabled.ALWAYS) return []
|
||||
const seen = new Set()
|
||||
const duplicated = []
|
||||
for (const row of groups.ALWAYS) {
|
||||
const name = String(row.feeName || '').trim()
|
||||
if (!name) continue
|
||||
if (seen.has(name) || Number(feeCounts.value[name] || 0) >= 2) duplicated.push(name)
|
||||
seen.add(name)
|
||||
}
|
||||
return [...new Set(duplicated)]
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
|
||||
|
||||
@@ -281,10 +281,12 @@
|
||||
|
||||
<script setup name="PayBill">
|
||||
import { getToken } from '@/utils/auth'
|
||||
import useUserStore from '@/store/modules/user'
|
||||
import { listSupplier } from '@/api/customersuppliermanagement/supplierinfo/supplier'
|
||||
import { listPayBill, getPayBill, previewPayBill, generatePayBill, voidPayBill, submitPayBill, approvePayBill, rejectPayBill, savePayBillDiff, acceptPayBillDiff, removePayBillLine, savePayBillFile, deletePayBillFile, redPayBill, savePayFinance, exportPayStatement, importPaySupplier } from '@/api/price/payBill'
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
const userStore = useUserStore()
|
||||
const loading = ref(false)
|
||||
const rows = ref([])
|
||||
const total = ref(0)
|
||||
@@ -480,7 +482,8 @@ function submitRow(row) {
|
||||
}
|
||||
|
||||
function approveRow(row) {
|
||||
proxy.$modal.confirm('审核通过?蓝字需要发票价税合计、税额和账单一致,审核人不能是提交人。').then(() => approvePayBill(row.billId)).then(() => {
|
||||
const limit = String(userStore.id) === '1' ? '' : ',审核人不能是提交人'
|
||||
proxy.$modal.confirm('审核通过?蓝字需要发票价税合计、税额和账单一致' + limit + '。').then(() => approvePayBill(row.billId)).then(() => {
|
||||
proxy.$modal.msgSuccess('已审核通过')
|
||||
loadList()
|
||||
if (detail.value?.billId === row.billId) reloadDetail()
|
||||
@@ -558,6 +561,11 @@ function onFileUploaded(res) {
|
||||
fileUrl: res.url || res.fileName
|
||||
}).then(() => {
|
||||
proxy.$modal.msgSuccess('凭证已上传')
|
||||
fileForm.fileType = 'INVOICE'
|
||||
fileForm.invoiceNo = ''
|
||||
fileForm.amount = null
|
||||
fileForm.taxAmount = 0
|
||||
fileForm.sellerName = ''
|
||||
reloadDetail()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -38,8 +38,8 @@
|
||||
<el-button link type="primary" @click="openDetail(row)">查看</el-button>
|
||||
<el-button v-if="row.status === 'DRAFT'" link type="primary" @click="confirmRow(row)" v-hasPermi="['price:payPayment:confirm']">确认付款</el-button>
|
||||
<el-button v-if="row.status === 'DRAFT'" link type="danger" @click="voidRow(row)" v-hasPermi="['price:payPayment:void']">作废</el-button>
|
||||
<el-button v-if="row.status === 'PAID'" link type="warning" @click="reverseRow(row)" v-hasPermi="['price:payPayment:confirm']">反核销</el-button>
|
||||
<el-button v-if="row.status === 'PAID'" link type="danger" @click="refundRow(row)" v-hasPermi="['price:payPayment:confirm']">退款</el-button>
|
||||
<el-button v-if="canAct(row)" link type="warning" @click="openAct(row, 'reverse')" v-hasPermi="['price:payPayment:confirm']">反核销</el-button>
|
||||
<el-button v-if="canAct(row)" link type="danger" @click="openAct(row, 'refund')" v-hasPermi="['price:payPayment:confirm']">退款</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -119,14 +119,55 @@
|
||||
</el-table>
|
||||
</template>
|
||||
</el-drawer>
|
||||
|
||||
<el-dialog v-model="actVisible" :title="actTitle" width="920px" append-to-body>
|
||||
<div v-loading="actLoading">
|
||||
<template v-if="actPayment">
|
||||
<el-descriptions :column="3" border size="small" class="mb8">
|
||||
<el-descriptions-item label="付款单号">{{ actPayment.paymentNo }}</el-descriptions-item>
|
||||
<el-descriptions-item label="供应商">{{ actPayment.supplierName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="币种">{{ actPayment.currency }}</el-descriptions-item>
|
||||
<el-descriptions-item label="付款金额">{{ actPayment.amount }}</el-descriptions-item>
|
||||
<el-descriptions-item label="已核销">{{ actPayment.allocAmount || 0 }}</el-descriptions-item>
|
||||
<el-descriptions-item label="手续费">{{ actPayment.bankFee }}</el-descriptions-item>
|
||||
<el-descriptions-item label="付款日">{{ actPayment.payDate }}</el-descriptions-item>
|
||||
<el-descriptions-item label="方式">{{ methodText(actPayment.payMethod) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="账户">{{ actPayment.bankAccount || '-' }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<div class="mb8">核销账单</div>
|
||||
<el-table :data="actAllocs" border size="small" class="mb8" empty-text="没有仍有效的核销">
|
||||
<el-table-column prop="billNo" label="账单号" min-width="160" />
|
||||
<el-table-column prop="allocAmount" label="核销金额" width="120" align="right" />
|
||||
<el-table-column prop="currency" label="币种" width="80" align="center" />
|
||||
</el-table>
|
||||
<div class="mb8">费用明细</div>
|
||||
<el-table :data="actFees" border size="small" max-height="280" empty-text="没有费用明细">
|
||||
<el-table-column prop="billNo" label="账单号" min-width="150" />
|
||||
<el-table-column prop="orderNo" label="订单号" min-width="140" />
|
||||
<el-table-column prop="ladingNo" label="提单号" min-width="120" />
|
||||
<el-table-column prop="feeName" label="费用" min-width="120" />
|
||||
<el-table-column prop="amount" label="金额" width="110" align="right" />
|
||||
<el-table-column prop="currency" label="币种" width="80" align="center" />
|
||||
</el-table>
|
||||
<div class="act-hint">{{ actHint }}</div>
|
||||
</template>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button @click="actVisible = false">取消</el-button>
|
||||
<el-button :type="actType === 'refund' ? 'danger' : 'warning'" :loading="actSaving" :disabled="!actPayment" @click="runAct">确认</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="PayPayment">
|
||||
import useUserStore from '@/store/modules/user'
|
||||
import { listSupplier } from '@/api/customersuppliermanagement/supplierinfo/supplier'
|
||||
import { listPayPayment, getPayPayment, openPayBills, createPayPayment, confirmPayPayment, voidPayPayment, reversePayPayment, refundPayPayment } from '@/api/price/payPayment'
|
||||
import { getPayBill } from '@/api/price/payBill'
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
const userStore = useUserStore()
|
||||
const { bms_currency } = useDict('bms_currency')
|
||||
const loading = ref(false)
|
||||
const saving = ref(false)
|
||||
@@ -145,8 +186,23 @@ const openBills = ref([])
|
||||
const form = reactive({ supplierId: null, currency: 'CNY', payDate: '', payMethod: 'BANK', bankAccount: '', amount: null, bankFee: 0, exchangeRate: 1 })
|
||||
const detailVisible = ref(false)
|
||||
const detail = ref(null)
|
||||
const actVisible = ref(false)
|
||||
const actLoading = ref(false)
|
||||
const actSaving = ref(false)
|
||||
const actType = ref('reverse')
|
||||
const actPayment = ref(null)
|
||||
const actFees = ref([])
|
||||
|
||||
const allocTotal = computed(() => openBills.value.reduce((sum, item) => sum + (Number(item.allocAmount) || 0), 0).toFixed(2))
|
||||
const actTitle = computed(() => actType.value === 'refund' ? '应付退款' : '应付反核销')
|
||||
const actAllocs = computed(() => (actPayment.value?.allocs || []).filter(item => item.allocStatus !== 'REVERSED'))
|
||||
const actHint = computed(() => {
|
||||
const admin = String(userStore.id) === '1'
|
||||
if (actType.value === 'refund') {
|
||||
return admin ? '确认后这些核销会冲回,付款单变为已退款。' : '确认后这些核销会冲回,付款单变为已退款。确认人不能自己退款。'
|
||||
}
|
||||
return admin ? '确认后这些核销会冲回,账单未付金额会回来。' : '确认后这些核销会冲回,账单未付金额会回来。确认人不能自己反核销。'
|
||||
})
|
||||
|
||||
function statusText(value) {
|
||||
return statusOptions.find(item => item.value === value)?.label || value || '-'
|
||||
@@ -156,6 +212,10 @@ function methodText(value) {
|
||||
return { BANK: '银行转账', CASH: '现金', OTHER: '其他' }[value] || value || '-'
|
||||
}
|
||||
|
||||
function canAct(row) {
|
||||
return row.status === 'PAID' && Number(row.allocAmount) > 0
|
||||
}
|
||||
|
||||
function loadList() {
|
||||
loading.value = true
|
||||
listPayPayment(query).then(res => {
|
||||
@@ -205,22 +265,46 @@ function openDetail(row) {
|
||||
})
|
||||
}
|
||||
|
||||
function reverseRow(row) {
|
||||
proxy.$modal.confirm('反核销这张付款?账单未付金额会回来。确认人不能自己反核销。').then(() => reversePayPayment(row.paymentId)).then(() => {
|
||||
proxy.$modal.msgSuccess('已反核销')
|
||||
loadList()
|
||||
}).catch(() => {})
|
||||
function openAct(row, type) {
|
||||
actType.value = type
|
||||
actPayment.value = null
|
||||
actFees.value = []
|
||||
actVisible.value = true
|
||||
actLoading.value = true
|
||||
getPayPayment(row.paymentId).then(res => {
|
||||
actPayment.value = res.data
|
||||
const allocs = (res.data?.allocs || []).filter(item => item.allocStatus !== 'REVERSED' && item.billId)
|
||||
return Promise.all(allocs.map(alloc => getPayBill(alloc.billId).then(billRes => {
|
||||
const bill = billRes.data || {}
|
||||
return (bill.lines || []).map(line => ({
|
||||
billNo: alloc.billNo || bill.billNo,
|
||||
orderNo: line.orderNo,
|
||||
ladingNo: line.ladingNo,
|
||||
feeName: line.feeName,
|
||||
amount: line.amount,
|
||||
currency: line.currency || bill.currency
|
||||
}))
|
||||
}).catch(() => [])))
|
||||
}).then(groups => {
|
||||
if (Array.isArray(groups)) actFees.value = groups.flat()
|
||||
}).finally(() => { actLoading.value = false })
|
||||
}
|
||||
|
||||
function refundRow(row) {
|
||||
proxy.$modal.confirm('把这笔付款退回?已核销金额会冲回。').then(() => refundPayPayment(row.paymentId)).then(() => {
|
||||
proxy.$modal.msgSuccess('已退款')
|
||||
function runAct() {
|
||||
if (!actPayment.value) return
|
||||
const request = actType.value === 'refund' ? refundPayPayment : reversePayPayment
|
||||
actSaving.value = true
|
||||
request(actPayment.value.paymentId).then(() => {
|
||||
proxy.$modal.msgSuccess(actType.value === 'refund' ? '已退款' : '已反核销')
|
||||
actVisible.value = false
|
||||
loadList()
|
||||
}).catch(() => {})
|
||||
if (detail.value?.paymentId === actPayment.value.paymentId) openDetail(actPayment.value)
|
||||
}).finally(() => { actSaving.value = false })
|
||||
}
|
||||
|
||||
function confirmRow(row) {
|
||||
proxy.$modal.confirm('确认这张付款?登记人和确认人不能是同一人。').then(() => confirmPayPayment(row.paymentId)).then(() => {
|
||||
const limit = String(userStore.id) === '1' ? '' : '登记人和确认人不能是同一人。'
|
||||
proxy.$modal.confirm('确认这张付款?' + limit).then(() => confirmPayPayment(row.paymentId)).then(() => {
|
||||
proxy.$modal.msgSuccess('已确认付款')
|
||||
loadList()
|
||||
}).catch(() => {})
|
||||
@@ -238,4 +322,5 @@ loadList()
|
||||
|
||||
<style scoped>
|
||||
.mb8 { margin-bottom: 8px; }
|
||||
.act-hint { margin-top: 8px; }
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user