Commit 213060b8 by linjinhong

物流报价和运费计算页面添加完成

parent 5003fd35
...@@ -50,6 +50,7 @@ declare module 'vue' { ...@@ -50,6 +50,7 @@ declare module 'vue' {
ElTag: typeof import('element-plus/es')['ElTag'] ElTag: typeof import('element-plus/es')['ElTag']
ElTooltip: typeof import('element-plus/es')['ElTooltip'] ElTooltip: typeof import('element-plus/es')['ElTooltip']
ElTree: typeof import('element-plus/es')['ElTree'] ElTree: typeof import('element-plus/es')['ElTree']
ElUpload: typeof import('element-plus/es')['ElUpload']
Icon: typeof import('./src/components/Icon.vue')['default'] Icon: typeof import('./src/components/Icon.vue')['default']
ImageView: typeof import('./src/components/ImageView.vue')['default'] ImageView: typeof import('./src/components/ImageView.vue')['default']
LogList: typeof import('./src/components/LogList.vue')['default'] LogList: typeof import('./src/components/LogList.vue')['default']
......
...@@ -114,15 +114,15 @@ export function getlogisticsWayAllList() { ...@@ -114,15 +114,15 @@ export function getlogisticsWayAllList() {
return axios.get<never, BaseRespData<never>>('/logisticsWay/all_list', {}) return axios.get<never, BaseRespData<never>>('/logisticsWay/all_list', {})
} }
//物流报价列表 //物流报价列表
export function getlogisticsQuotationList() { export function getlogisticsQuotationList(params: any) {
return axios.get<never, BaseRespData<never>>( return axios.post<never, BaseRespData<never>>(
'/logistics/logisticsQuotation/list_page', '/logistics/logisticsQuotation/list_page',
{}, params,
) )
} }
//新增 //新增
export function addLogisticsQuotation(params: any) { export function addLogisticsQuotation(params: any) {
return axios.get<never, BaseRespData<never>>( return axios.post<never, BaseRespData<never>>(
'/logistics/logisticsQuotation/add', '/logistics/logisticsQuotation/add',
params, params,
) )
...@@ -142,24 +142,31 @@ export function deleteLogisticsQuotation(params: any) { ...@@ -142,24 +142,31 @@ export function deleteLogisticsQuotation(params: any) {
) )
} }
//导入 //导入
export function importLogisticsQuotation(params: any) { export function importLogisticsQuotation(formData: any) {
return axios.get<never, BaseRespData<never>>( return axios.post<never, BaseRespData<never>>(
'/logistics/logisticsQuotation/import', '/logistics/logisticsQuotation/import',
{ params }, formData,
{
headers: {
'Content-Type': 'multipart/form-data',
},
},
) )
} }
//下载模版 //下载模版
export function exportLogisticsQuotationTemplate(params: any) { export function downloadLogisticsQuotationTemplate() {
return axios.get<never, BaseRespData<never>>( return axios.get<never, BaseRespData<never>>(
'/logistics/logisticsQuotation/exportExcel', '/logistics/logisticsQuotation/exportExcel',
{ params }, {
responseType: 'blob',
},
) )
} }
//根据ID查对象 //根据ID查对象
export function getLogisticsQuotationByID(params: any) { export function getLogisticsQuotationByID(params: any) {
return axios.get<never, BaseRespData<never>>( return axios.post<never, BaseRespData<never>>(
'/logistics/logisticsQuotation/get', '/logistics/logisticsQuotation/get',
{ params }, params,
) )
} }
...@@ -193,3 +200,68 @@ export function deleteLogisticsCustomsRule(params: any) { ...@@ -193,3 +200,68 @@ export function deleteLogisticsCustomsRule(params: any) {
params, params,
}) })
} }
/**
* @description 物流分区
*/
//列表
export function getLogisticsZoneList(params: any) {
return axios.post<never, BaseRespData<never>>(
'/logistics/logisticsZone/list',
params,
)
}
//新增
export function addLogisticsZone(params: any) {
return axios.post<never, BaseRespData<never>>(
'/logistics/logisticsZone/add',
params,
)
}
//修改
export function updateLogisticsZone(params: any) {
return axios.post<never, BaseRespData<never>>(
'/logistics/logisticsZone/update',
params,
)
}
//删除
export function deleteLogisticsZone(params: any) {
return axios.post<never, BaseRespData<never>>(
'/logistics/logisticsZone/delete',
params,
)
}
//物流分区导入
export function importLogisticsZone(formData: any) {
return axios.post<never, BaseRespData<never>>(
'/logistics/logisticsZone/import',
formData,
{
headers: {
'Content-Type': 'multipart/form-data',
},
},
)
}
//物流分区导入模板
export function exportExcelLogisticsZone() {
return axios.get<never, BaseRespData<never>>(
'/logistics/logisticsZone/exportExcel',
{
responseType: 'blob',
},
)
}
/**
* @description 运费试算
*/
export function getLogisticsTrialCalculation(params: any) {
return axios.get<never, BaseRespData<never>>(
'/logistics/logisticsQuotation/logisticsTrialCalculation',
{
params,
},
)
}
...@@ -130,8 +130,8 @@ export default defineComponent({ ...@@ -130,8 +130,8 @@ export default defineComponent({
await formRef.value?.resetFields() await formRef.value?.resetFields()
} }
function refashConfig(showFields: string[]) { function refashConfig(showFields?: string[]) {
if (showFields.length) { if (showFields?.length) {
const filterArr = props.config.filter((item) => const filterArr = props.config.filter((item) =>
showFields.includes(item.prop as string), showFields.includes(item.prop as string),
) )
...@@ -142,6 +142,8 @@ export default defineComponent({ ...@@ -142,6 +142,8 @@ export default defineComponent({
] ]
.filter((item) => !item.fixed) .filter((item) => !item.fixed)
.concat(tableConfig.value.filter((item) => item.fixed)) .concat(tableConfig.value.filter((item) => item.fixed))
} else {
tableConfig.value = props.config
} }
console.log('tableConfig', tableConfig.value) console.log('tableConfig', tableConfig.value)
} }
......
...@@ -9,9 +9,12 @@ ...@@ -9,9 +9,12 @@
@input="iptFn" @input="iptFn"
> >
<template v-if="hasSuffix" #suffix>{{ suffix }}</template> <template v-if="hasSuffix" #suffix>{{ suffix }}</template>
<template v-if="hasAppend" #append>
<component :is="appendRender" />
</template>
</el-input> </el-input>
</template> </template>
<script lang="ts" setup> <script lang="tsx" setup>
import { isNumFloat } from '@/utils/validate' import { isNumFloat } from '@/utils/validate'
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
...@@ -20,6 +23,8 @@ const props = withDefaults( ...@@ -20,6 +23,8 @@ const props = withDefaults(
placeholder?: string placeholder?: string
size?: '' | 'default' | 'small' | 'large' size?: '' | 'default' | 'small' | 'large'
hasSuffix?: boolean hasSuffix?: boolean
hasAppend?: boolean
appendRender?: () => JSX.Element
isDisabled?: boolean isDisabled?: boolean
}>(), }>(),
{ {
...@@ -29,6 +34,8 @@ const props = withDefaults( ...@@ -29,6 +34,8 @@ const props = withDefaults(
size: 'default', size: 'default',
hasSuffix: true, hasSuffix: true,
isDisabled: false, isDisabled: false,
hasAppend: false,
appendRender: null,
}, },
) )
const emits = defineEmits<{ const emits = defineEmits<{
......
...@@ -34,9 +34,10 @@ const props = withDefaults( ...@@ -34,9 +34,10 @@ const props = withDefaults(
hasAllOptions?: boolean hasAllOptions?: boolean
labelIsValue?: boolean // value 使用label字段 labelIsValue?: boolean // value 使用label字段
initChange?: boolean // 首次是否触发change事件 initChange?: boolean // 首次是否触发change事件
modelValue?: string | number modelValue?: string | number | string[] | number[]
isRefresh?: boolean isRefresh?: boolean
isValueKey?: boolean isValueKey?: boolean
load?: (arg: (val: boolean) => void) => void // focus后再加载数据的 load?: (arg: (val: boolean) => void) => void // focus后再加载数据的
}>(), }>(),
{ {
......
...@@ -104,12 +104,23 @@ export default defineComponent({ ...@@ -104,12 +104,23 @@ export default defineComponent({
setup(props, { emit, slots, attrs }) { setup(props, { emit, slots, attrs }) {
const formRef = ref() const formRef = ref()
const formWrapperRef = ref() const formWrapperRef = ref()
const searchForm = ref<ISearchForm>(props.modelValue || {}) const searchForm = ref<ISearchForm>({})
const selectResult = ref<ISeachFormConfig[]>([]) const selectResult = ref<ISeachFormConfig[]>([])
const componentWidth = computed(() => const componentWidth = computed(() =>
props.size === 'default' ? 200 : 220, props.size === 'default' ? 200 : 220,
) )
watch(
() => props.modelValue,
(val) => {
searchForm.value = val
},
{
immediate: true,
deep: true,
},
)
function initSeachForm() { function initSeachForm() {
props.config.forEach((item, index) => { props.config.forEach((item, index) => {
if (componentDataTypeMap.has(item.type as EComponenetType)) { if (componentDataTypeMap.has(item.type as EComponenetType)) {
...@@ -157,7 +168,7 @@ export default defineComponent({ ...@@ -157,7 +168,7 @@ export default defineComponent({
<div style={styles.wrapper} class="mt-15"> <div style={styles.wrapper} class="mt-15">
<ElForm <ElForm
{...getFormAttrs.value} {...getFormAttrs.value}
ref={el=>formRef.value=el} ref={(el) => (formRef.value = el)}
class="w-full" class="w-full"
inline inline
model={searchForm.value} model={searchForm.value}
......
import { VNode } from 'vue' import { VNode } from 'vue'
interface ColumnAttrs { interface ColumnAttrs {
field?: string field?: string
title?: string title?: string
...@@ -52,7 +51,7 @@ export default defineComponent({ ...@@ -52,7 +51,7 @@ export default defineComponent({
}, },
}, },
emits: ['update:modelValue', 'checkbox-change', 'getCheckboxRecords'], emits: ['update:modelValue', 'checkbox-change', 'getCheckboxRecords'],
setup(props, { emit ,attrs}) { setup(props, { emit, attrs }) {
const tableRef = ref(null) const tableRef = ref(null)
const tableData = ref<Record<string, unknown>[]>([]) const tableData = ref<Record<string, unknown>[]>([])
const tableColumns = ref<TableColumn[]>([]) const tableColumns = ref<TableColumn[]>([])
...@@ -79,9 +78,7 @@ export default defineComponent({ ...@@ -79,9 +78,7 @@ export default defineComponent({
watch( watch(
() => props.modelValue, () => props.modelValue,
(val) => { (val) => {
tableData.value = Array.isArray(val) ? val : [] tableData.value = Array.isArray(val) ? val : []
}, },
{ {
immediate: true, immediate: true,
...@@ -98,7 +95,7 @@ export default defineComponent({ ...@@ -98,7 +95,7 @@ export default defineComponent({
} }
} }
} }
//获取选中数据
const getSelectEvent = () => { const getSelectEvent = () => {
const $table = tableRef.value const $table = tableRef.value
if ($table) { if ($table) {
...@@ -106,6 +103,14 @@ export default defineComponent({ ...@@ -106,6 +103,14 @@ export default defineComponent({
emit('getCheckboxRecords', selectRecords) emit('getCheckboxRecords', selectRecords)
} }
} }
//设置高亮行
const selectRowEvent = (row: TableRowData) => {
const $table = tableRef.value
if ($table) {
console.log(110, row)
$table.setCurrentRow(row)
}
}
onMounted(() => { onMounted(() => {
getList() getList()
...@@ -116,17 +121,17 @@ export default defineComponent({ ...@@ -116,17 +121,17 @@ export default defineComponent({
tableData, tableData,
tableColumns, tableColumns,
editConfig, editConfig,
getSelectEvent,attrs getSelectEvent,
selectRowEvent,
attrs,
} }
}, },
render() { render() {
return ( return (
<vxe-table <vxe-table
ref={(el) => this.tableRef = el} ref={(el) => (this.tableRef = el)}
data={this.tableData} data={this.tableData}
height='100%' height="100%"
edit-config={this.editConfig} edit-config={this.editConfig}
onCheckboxChange={this.getSelectEvent} onCheckboxChange={this.getSelectEvent}
onCheckboxAll={this.getSelectEvent} onCheckboxAll={this.getSelectEvent}
...@@ -171,7 +176,6 @@ export default defineComponent({ ...@@ -171,7 +176,6 @@ export default defineComponent({
</vxe-column> </vxe-column>
))} ))}
</vxe-table> </vxe-table>
) )
}, },
}) })
...@@ -148,7 +148,20 @@ const router = createRouter({ ...@@ -148,7 +148,20 @@ const router = createRouter({
}, },
component: () => import('@/views/logistics/declarationRule.vue'), component: () => import('@/views/logistics/declarationRule.vue'),
}, },
{
path: '/logistics/logisticsPartition',
meta: {
title: '物流分区',
},
component: () => import('@/views/logistics/logisticsPartition.vue'),
},
{
path: '/logistics/logisticsCalculate',
meta: {
title: '运费试算',
},
component: () => import('@/views/logistics/logisticsCalculate.vue'),
},
{ {
path: '/warehouse/manage', path: '/warehouse/manage',
meta: { meta: {
......
...@@ -136,19 +136,29 @@ const menu: MenuItem[] = [ ...@@ -136,19 +136,29 @@ const menu: MenuItem[] = [
}, },
{ {
index: '/logistics/shippingAddress', index: '/logistics/shippingAddress',
id: 1, id: 2,
label: '发货地址', label: '发货地址',
}, },
{ {
index: '/logistics/logisticsQuotation', index: '/logistics/logisticsQuotation',
id: 1, id: 3,
label: '物流报价', label: '物流报价',
}, },
{ {
index: '/logistics/declarationRule', index: '/logistics/declarationRule',
id: 1, id: 4,
label: '申报规则', label: '申报规则',
}, },
{
index: '/logistics/logisticsPartition',
id: 5,
label: '物流分区',
},
{
index: '/logistics/logisticsCalculate',
id: 6,
label: '运费试算',
},
], ],
}, },
// { // {
......
...@@ -12,6 +12,7 @@ export interface PaginationData<D> { ...@@ -12,6 +12,7 @@ export interface PaginationData<D> {
size: number size: number
current: number current: number
records: D[] records: D[]
data?: D[]
} }
export interface Statistics<D> { export interface Statistics<D> {
sumNotPassNum: number sumNotPassNum: number
......
...@@ -44,7 +44,6 @@ export default function usePageList<T>(options: UsePageListOptions<T>) { ...@@ -44,7 +44,6 @@ export default function usePageList<T>(options: UsePageListOptions<T>) {
pageSize.value = res.size pageSize.value = res.size
data.value = res.records data.value = res.records
} }
console.log(7, data.value)
} catch (error) { } catch (error) {
console.error(error) console.error(error)
// showError(error) // showError(error)
......
<template>
<div class="user-page flex-column card h-100 overflow-hidden">
<div class="header-filter-form">
<SearchForm
:config="searchConfig"
:isSearchBtn="false"
:isAddBtn="false"
:isDeleteBtn="false"
v-model="searchForm"
>
<template #ontherBtn>
<div style="margin-left: 10px; display: flex; gap: 10px">
<ElButton @click="search" type="primary">计算</ElButton>
</div>
</template>
</SearchForm>
</div>
<div
v-loading="loading"
class="user-content flex-1 flex-column overflow-hidden"
>
<div class="user-list flex-1 overflow-hidden">
<CustomizeTable
ref="tableRef"
v-model="tableData"
:config="tableConfig"
:merge-cells="mergeCells"
highlight-current-row
:isShowCheckBox="false"
border="full"
@getCheckboxRecords="handleCheckboxRecords"
></CustomizeTable>
</div>
</div>
</div>
</template>
<script setup lang="tsx">
import { getLogisticsTrialCalculation } from '@/api/logistics'
import type { LogisticsMethod } from '@/types/api/logistics'
import SearchForm from '@/components/SearchForm.tsx'
import LogDialog from './components/LogDialog.tsx'
import CustomizeForm from '@/components/CustomizeForm.tsx'
import CustomizeTable from '@/components/VxeTable.tsx'
import type { VxeTablePropTypes } from 'vxe-table'
import usePageList from '@/utils/hooks/usePageList'
import { useValue } from './hooks/useValue'
import { showConfirm } from '@/utils/ui'
import { Edit } from '@element-plus/icons-vue'
const [searchForm] = useValue({ code: '', weight: '' })
const tableRef = ref(null)
const selection = ref([])
const mergeCells = ref<VxeTablePropTypes.MergeCells>([])
const tableData = ref([])
const searchConfig = ref([
{
prop: 'code',
type: 'input',
label: '邮编',
attrs: {
placeholder: '请输入邮编',
},
},
{
prop: 'weight',
type: 'amountInput',
label: '重量',
attrs: {
placeholder: '请输入重量',
hasUnit: true,
suffix: 'g',
},
},
])
const tableConfig = ref([
{
prop: 'logisticsWayName',
label: '物流名称',
attrs: {
align: 'center',
width: 100,
},
},
{
prop: 'warehouseName',
label: '发货仓库',
},
{
prop: 'logisticsWayCode',
label: '物流编码',
},
{
prop: 'partition',
label: '所在分区',
},
{
prop: 'status',
label: '状态',
render: {
default: ({ row }) => {
return (
<div style={{ color: row.status ? '#67c23a' : '#f56c6c' }}>
{row.status ? '成功' : '失败'}
</div>
)
},
},
},
{
prop: 'payFreight',
label: '预计运费($)',
},
])
async function search() {
if (!searchForm.value.code) {
ElMessage.warning('请输入邮编')
return
}
if (!searchForm.value.weight) {
ElMessage.warning('请输入重量')
return
}
await getList(searchForm.value)
const itemIndex = tableData.value.findIndex((item) => item.status)
console.log(123, itemIndex)
if (itemIndex !== -1) {
nextTick(() => {
tableRef.value?.selectRowEvent(tableData.value[itemIndex])
})
}
}
const loading = ref(false)
async function getList(data?) {
loading.value = true
try {
const res = await getLogisticsTrialCalculation({
...data,
})
tableData.value = [...res.data]
} catch (error) {
console.log(error)
} finally {
loading.value = false
}
}
function handleCheckboxRecords(value: never[]) {
console.log(351, value)
selection.value = value
}
</script>
<style lang="scss" scoped>
.header-filter-form {
margin-bottom: 20px;
:deep(.el-form-item) {
margin-right: 14px;
margin-bottom: 10px;
}
}
.user-operate-btn {
margin-bottom: 10px;
}
.dialog-footer {
text-align: center;
}
</style>
<template>
<div class="user-page flex-column card h-100 overflow-hidden">
<div class="header-filter-form">
<SearchForm
:config="searchConfig"
@search="search"
@add="addDialog"
:isAddBtn="false"
@delete="deleteFn"
v-model="searchForm"
>
<template #ontherBtn>
<div style="margin-left: 10px; display: flex; gap: 10px">
<ElButton @click="downloadExcel" type="info" plain
>下载模版</ElButton
>
<el-upload
class="upload-demo"
action
:show-file-list="false"
:before-upload="onBeforeUploadImage"
:http-request="exportExcel"
>
<ElButton type="primary" plain> 导入</ElButton>
</el-upload>
</div>
</template></SearchForm
>
</div>
<div
v-loading="loading"
class="user-content flex-1 flex-column overflow-hidden"
>
<div class="user-list flex-1 overflow-hidden">
<CustomizeTable
v-model="tableData"
:config="tableConfig"
:merge-cells="mergeCells"
border="full"
@getCheckboxRecords="handleCheckboxRecords"
></CustomizeTable>
</div>
<!-- <ElPagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="[100, 200, 300, 400, 500]"
background
layout="total, sizes, prev, pager, next, jumper"
:total="total"
style="margin: 10px auto 0; text-align: right"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
></ElPagination> -->
</div>
</div>
<LogDialog
:title="editForm.id ? '编辑物流方式' : '新增物流方式'"
dialogWidth="1000px"
v-model="dialogVisible"
@close="cancelFn"
>
<CustomizeForm
ref="editFormRef"
v-model="editForm"
:config="formConfig"
formItemWidth="100%"
>
</CustomizeForm>
<template #footer>
<div style="text-align: center">
<ElButton @click="cancelFn">取消</ElButton>
<ElButton type="primary" @click="save">保存</ElButton>
</div>
</template>
</LogDialog>
</template>
<script setup lang="tsx">
import {
getLogisticsZoneList,
addLogisticsZone,
updateLogisticsZone,
deleteLogisticsZone,
importLogisticsZone,
exportExcelLogisticsZone,
getlogisticsWayAllList,
} from '@/api/logistics'
import type { LogisticsMethod } from '@/types/api/logistics'
import SearchForm from '@/components/SearchForm.tsx'
import LogDialog from './components/LogDialog.tsx'
import CustomizeForm from '@/components/CustomizeForm.tsx'
import CustomizeTable from '@/components/VxeTable.tsx'
import type { VxeTablePropTypes } from 'vxe-table'
import usePageList from '@/utils/hooks/usePageList'
import { useValue } from './hooks/useValue'
import { showConfirm } from '@/utils/ui'
import { Edit } from '@element-plus/icons-vue'
const [searchForm] = useValue({})
const [editForm, resetEditForm] = useValue({ logisticsList: [] })
const dialogVisible = ref(false)
const editFormRef = ref(null)
const selection = ref([])
const mergeCells = ref<VxeTablePropTypes.MergeCells>([])
const tableData = ref([])
const searchConfig = ref([
{
prop: 'logisticsList',
type: 'select',
label: '物流方式',
attrs: {
placeholder: '请选择物流方式',
multiple: true,
value: 'name',
label: 'name',
collapseTags: true,
collapseTagsTooltip: true,
options: [],
},
},
{
prop: 'codePrefix',
type: 'input',
label: '邮编编码',
attrs: {
placeholder: '请输入邮编编码',
},
},
])
const formConfig = ref([
{ title: '物流基础信息' },
{
prop: 'zone1',
type: 'input',
label: 'ZONE 1',
attrs: {
placeholder: '请输入ZONE 1',
},
},
{
prop: 'zone2',
type: 'input',
label: 'ZONE 2',
attrs: {
placeholder: '请输入ZONE 2',
},
},
{
prop: 'zone3',
type: 'input',
label: 'ZONE 3',
attrs: {
placeholder: '请输入ZONE 3',
},
},
{
prop: 'zone4',
type: 'input',
label: 'ZONE 4',
attrs: {
placeholder: '请输入ZONE 4',
},
},
{
prop: 'zone5',
type: 'input',
label: 'ZONE 5',
attrs: {
placeholder: '请输入ZONE 5',
},
},
{
prop: 'zone6',
type: 'input',
label: 'ZONE 6',
attrs: {
placeholder: '请输入ZONE 6',
},
},
{
prop: 'zone7',
type: 'input',
label: 'ZONE 7',
attrs: {
placeholder: '请输入ZONE 7',
},
},
{
prop: 'zone8',
type: 'input',
label: 'ZONE 8',
attrs: {
placeholder: '请输入ZONE 8',
},
},
{
prop: 'zone9',
type: 'input',
label: 'ZONE 9',
attrs: {
placeholder: '请输入ZONE 9',
},
},
])
const tableConfig = ref([])
watch(
() => searchForm.value,
(val) => {
if (val.logisticsList === '') {
val.logisticsList = []
}
},
{ immediate: true, deep: true },
)
onMounted(() => {
// loading.value = true
getAllList()
})
async function search() {
await getList(searchForm.value)
setCellStyle()
}
function getTableData(arr) {
// 防御式编程:处理非数组或空输入
if (!Array.isArray(arr) || arr.length === 0) return []
// 使用 map 替代 forEach 实现不可变数据
return arr.map((parentItem) => {
// 使用可选链 + 空值合并运算符处理嵌套数据
const quotationList = parentItem.logisticsZoneList ?? []
// 使用 Object.fromEntries 优化属性合并
quotationList?.forEach((el) => {
parentItem[el.logistics] = {
codePrefix: el.codePrefix || '',
highlight: el.highlight,
}
})
// 返回新对象保持不可变性
return {
...parentItem, // 保留原属性
}
})
}
function cancelFn() {
dialogVisible.value = false
editFormRef.value?.resetFields()
resetEditForm()
}
async function checkData() {
const [isValid, postData] = await Promise.all([
new Promise<boolean>((resolve) => {
editFormRef.value
?.validate()
.then(() => resolve(true))
.catch((err) => {
resolve(false)
console.log(err)
})
}),
new Promise<LogisticsMethod>((resolve) => {
const params = { ...editForm.value }
resolve(params)
}),
])
console.log(isValid, postData)
return { isValid, postData }
}
async function save() {
const { isValid, postData } = await checkData()
if (isValid) {
try {
if (!postData.id) {
await addLogisticsZone({
...postData,
})
} else {
await updateLogisticsZone({
...postData,
})
}
ElMessage({
message: '保存成功',
type: 'success',
offset: window.innerHeight / 2,
})
cancelFn()
search()
} catch (e) {
return
}
}
}
function addDialog() {
dialogVisible.value = true
}
async function deleteFn() {
if (!selection.value.length) {
return ElMessage({
message: '请选择分区',
type: 'warning',
})
}
try {
await showConfirm('是否删除改分区下物流方式的邮编编码', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
})
} catch {
return
}
try {
const whiteList = ['logisticsZoneList', 'zoneName', '_X_ROW_KEY']
const zomList = selection.value.map((item) => {
const arr = []
for (const key in item) {
if (!whiteList.includes(key)) {
arr.push(key)
}
}
return [...arr]
})
const params = {
logisticsList: zomList[0],
zoneNameList: selection.value.map((item) => item.zoneName),
}
await deleteLogisticsZone(params)
ElMessage({
message: '删除成功',
type: 'success',
})
getList(searchForm.value)
} catch (e) {
getList(searchForm.value)
// showError(e)
}
}
const loading = ref(false)
async function getList(data?) {
loading.value = true
try {
const res = await getLogisticsZoneList({
...data,
})
tableData.value = [...res.data]
try {
const arr = await getTableData(tableData.value)
const whiteList = ['logisticsZoneList', 'zoneName']
const oldConfig = [
{
prop: 'zoneName',
label: '分区',
},
]
const newConfig = []
if (arr.length) {
for (const key in arr[0]) {
if (!whiteList.includes(key)) {
newConfig.push({
prop: key,
label: key,
render: {
default: ({ row }) => {
return (
<span
class={row[key]?.highlight ? 'tableCell' : 'primaryCell'}
>
{row[key]?.codePrefix}
</span>
)
},
},
})
}
}
}
tableConfig.value = [...oldConfig, ...newConfig]
console.log('tableConfig', tableConfig.value)
console.log(' tableData', tableData.value)
} catch (e) {
console.log(e)
}
} catch (error) {
console.log(error)
} finally {
loading.value = false
}
}
function handleCheckboxRecords(value: never[]) {
console.log(351, value)
selection.value = value
}
function setCellStyle() {
const [cell, primaryCell] = ['.tableCell', '.primaryCell'].map((selector) =>
document.querySelectorAll(selector),
)
console.log(404, cell)
const getAncestor = (element, level = 2) => {
let current = element
while (level-- > 0 && current) {
current = current.parentElement
}
return current || null
}
const safeSetBackground = (element, color) => {
if (element?.style && typeof color === 'string') {
element.style.backgroundColor = color
}
}
if (cell.length) {
cell.forEach((item) => {
const grandParent = getAncestor(item)
safeSetBackground(grandParent, '#e6f7ff')
})
}
if (primaryCell.length) {
primaryCell.forEach((item) => {
const grandParent = getAncestor(item)
safeSetBackground(grandParent, 'transparent')
})
}
}
async function getAllList() {
try {
const res = await Promise.all([getlogisticsWayAllList(), getList()])
// console.log(545, res)
searchConfig.value[0].attrs!.options = [...(res[0]?.data || [])]
} catch (error) {
console.log(error)
}
}
function onBeforeUploadImage(file) {
const isIMAGE =
file.type ==
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
if (!isIMAGE) {
ElMessage.error('上传文件只能是excel格式!')
}
return isIMAGE
}
async function downloadExcel() {
try {
const res = await exportExcelLogisticsZone()
// console.log(483, data)
const blob = new Blob([res])
// 2. 解析文件名(从响应头或自定义)
const filename = '物流分区模版.xlsx'
// 3. 创建临时链接并触发下载
const link = document.createElement('a')
link.href = window.URL.createObjectURL(blob)
link.download = filename
link.click()
} catch (error) {
console.log(error)
}
}
async function exportExcel(file) {
try {
const formData = new FormData()
formData.append('file', file.file)
const res = await importLogisticsZone(formData)
if (res.code === 200) {
ElMessage.success('导入成功!')
getList(searchForm.value)
}
} catch (error) {
ElMessage.error('导入失败!')
}
}
watch(
() => loading.value,
(val) => {
console.log(442, val)
},
{ deep: true, immediate: true },
)
</script>
<style lang="scss" scoped>
.header-filter-form {
margin-bottom: 20px;
:deep(.el-form-item) {
margin-right: 14px;
margin-bottom: 10px;
}
}
.user-operate-btn {
margin-bottom: 10px;
}
.dialog-footer {
text-align: center;
}
</style>
...@@ -9,26 +9,52 @@ ...@@ -9,26 +9,52 @@
v-model="searchForm" v-model="searchForm"
> >
<template #ontherBtn> <template #ontherBtn>
<div style="margin-left: 10px"> <div style="margin-left: 10px; display: flex; gap: 10px">
<ElButton @click="downloadExcel" type="info" plain <ElButton @click="downloadExcel" type="info" plain
>下载模版</ElButton >下载模版</ElButton
> >
<ElButton type="primary" plain @click="exportExcel"
>新增导入</ElButton <el-upload
class="upload-demo"
action
:show-file-list="false"
:before-upload="onBeforeUploadImage"
:http-request="exportExcel"
> >
<ElButton type="primary" plain @click="exportExcel" <ElButton type="primary" plain> 导入</ElButton>
>修改导入</ElButton </el-upload>
<el-upload
class="upload-demo"
action
:show-file-list="false"
:before-upload="onBeforeUploadImage"
:http-request="updateExcel"
> >
<ElButton type="primary" plain> 更新</ElButton>
</el-upload>
</div> </div>
</template></SearchForm </template></SearchForm
> >
</div> </div>
<div class="user-content flex-1 flex-column overflow-hidden"> <div
<div class="user-list flex-1 overflow-hidden" v-loading="loading"> v-loading="loading"
class="user-content flex-1 flex-column overflow-hidden"
>
<div class="user-list flex-1 overflow-hidden">
<CustomizeTable <CustomizeTable
v-model="tableData" v-model="tableData"
:config="tableConfig" :config="tableConfig"
:merge-cells="mergeCells" :merge-cells="mergeCells"
:seq-config="{
seqMethod: (row) => {
if (searchForm.logisticsList?.length) {
return row.row.seq
}
return row.seq
},
}"
border="full"
@getCheckboxRecords="handleCheckboxRecords" @getCheckboxRecords="handleCheckboxRecords"
></CustomizeTable> ></CustomizeTable>
</div> </div>
...@@ -47,7 +73,7 @@ ...@@ -47,7 +73,7 @@
</div> </div>
<LogDialog <LogDialog
:title="editForm.id ? '编辑物流方式' : '新增物流方式'" :title="editForm.id ? '编辑物流报价' : '新增物流报价'"
dialogWidth="1000px" dialogWidth="1000px"
v-model="dialogVisible" v-model="dialogVisible"
@close="cancelFn" @close="cancelFn"
...@@ -78,216 +104,241 @@ import { ...@@ -78,216 +104,241 @@ import {
updateLogisticsQuotation, updateLogisticsQuotation,
deleteLogisticsQuotation, deleteLogisticsQuotation,
importLogisticsQuotation, importLogisticsQuotation,
exportLogisticsQuotationTemplate, downloadLogisticsQuotationTemplate,
} from '@/api/logistics' } from '@/api/logistics'
import type { LogisticsMethod } from '@/types/api/logistics' import type { LogisticsMethod } from '@/types/api/logistics'
import SearchForm from '@/components/SearchForm.tsx' import SearchForm from '@/components/SearchForm.tsx'
import LogDialog from './components/LogDialog.tsx' import LogDialog from './components/LogDialog.tsx'
import CustomizeForm from '@/components/CustomizeForm.tsx' import CustomizeForm from '@/components/CustomizeForm.tsx'
import CustomizeTable from '@/components/VxeTable.tsx' import CustomizeTable from '@/components/VxeTable.tsx'
import Select from '@/components/Form.vue/Select.vue' import type { VxeTablePropTypes } from 'vxe-table'
import usePageList from '@/utils/hooks/usePageList' import usePageList from '@/utils/hooks/usePageList'
import { useValue } from './hooks/useValue' import { useValue } from './hooks/useValue'
import { showConfirm } from '@/utils/ui' import { showConfirm } from '@/utils/ui'
import { Edit, Delete, List } from '@element-plus/icons-vue' import { Edit } from '@element-plus/icons-vue'
const [searchForm] = useValue({}) const [searchForm] = useValue({})
const [editForm, resetEditForm] = useValue<LogisticsMethod>({ const [editForm, resetEditForm] = useValue({ unit: 'oz' })
platformList: [{ platform: [], logisticsName: '' }],
ruleRef: [{ ruleId: '', ruleName: '' }],
status: 1,
})
const { const {
loading,
currentPage, currentPage,
pageSize, pageSize,
total, total,
data: tableData, data,
refresh: search, refresh: search,
onCurrentPageChange: handleCurrentChange, onCurrentPageChange: handleCurrentChange,
onPageSizeChange: handleSizeChange, onPageSizeChange: handleSizeChange,
} = usePageList({ } = usePageList({
query: (page, pageSize) => query: (page, pageSize) =>
// getlogisticsQuotationList({ getlogisticsQuotationList({
// ...searchForm.value, ...searchForm.value,
// pageSize: pageSize, pageSize: pageSize,
// currentPage: page, currentPage: page,
// }).then(({ data }) => { }).then(({ data }) => {
// console.log(130, data) console.log(130, data)
// return data return data
// }), }),
(() => {})(),
}) })
const dialogVisible = ref(false) const dialogVisible = ref(false)
const editFormRef = ref(null) const editFormRef = ref(null)
const selection = ref([]) const selection = ref([])
const mergeCells = ref<VxeTablePropTypes.MergeCells>([])
const tableData = ref([])
const searchConfig = ref([ const searchConfig = ref([
{ {
prop: 'name', prop: 'logisticsList',
type: 'select', type: 'select',
label: '物流方式', label: '物流方式',
attrs: { attrs: {
placeholder: '请选择物流方式', placeholder: '请选择物流方式',
multiple: true,
value: 'name',
label: 'name',
collapseTags: true,
collapseTagsTooltip: true,
options: [], options: [],
}, },
}, },
]) ])
const formConfig = ref([ const formConfig = ref([
{ title: '物流基础信息' },
{ {
prop: 'name', prop: 'logistics',
type: 'input', type: 'select',
label: 'ZONE 1', label: '物流方式',
attrs: { attrs: {
placeholder: '请输入物流名称', placeholder: '请选择物流方式',
value: 'name',
label: 'name',
options: [],
disabled: editForm.value?.['id'] ? true : false,
onChange: (val, id) => {
editForm.value['logisticsId'] = val.id
}, },
rules: [
{
required: true,
message: '请输入物流名称',
}, },
], rules: [{ required: true, message: '请选择物流方式' }],
}, },
{ {
prop: 'warehouseName', prop: 'rate',
type: 'select', type: 'amountInput',
label: 'ZONE 2', label: 'Rate(oz/LB)',
attrs: { attrs: {
placeholder: '请选择仓库名称', placeholder: '请输入Rate(oz/LB)',
options: [
{ label: '仓库1', value: 1 }, hasSuffix: false,
{ label: '仓库2', value: 2 }, hasAppend: true,
appendRender: () => {
return h(
ElSelect,
{
modelValue: editForm.value['unit'],
'onUpdate:modelValue': (val) => (editForm.value['unit'] = val),
disabled: editForm.value['id'] ? true : false,
placeholder: 'Select',
style: { width: '115px' },
},
() => [
h(ElOption, { label: 'oz', value: 'oz' }),
h(ElOption, { label: 'LB', value: 'LB' }),
], ],
)
}, },
rules: [
{
required: true,
message: '请选择仓库名称',
}, },
], rules: [{ required: true, message: '请输入Rate' }],
}, },
{ {
prop: 'ruleRefObj', prop: 'zone1',
type: 'input', type: 'input',
label: 'ZONE 3', label: 'ZONE 1',
attrs: { attrs: {
placeholder: '请选择申报规则', placeholder: '请输入ZONE 1',
options: [ },
{ label: '仓库1', value: 1 },
{ label: '仓库2', value: 2 },
],
}, },
rules: [
{ {
required: true, prop: 'zone2',
message: '请选择申报规则', type: 'input',
label: 'ZONE 2',
attrs: {
placeholder: '请输入ZONE 2',
}, },
],
}, },
{ {
prop: 'serviceCode', prop: 'zone3',
type: 'input', type: 'input',
label: 'ZONE 4', label: 'ZONE 3',
attrs: { attrs: {
placeholder: '请输入物流编码', placeholder: '请输入ZONE 3',
},
}, },
rules: [
{ {
required: true, prop: 'zone4',
message: '请输入物流编码', type: 'input',
label: 'ZONE 4',
attrs: {
placeholder: '请输入ZONE 4',
}, },
],
}, },
{ {
prop: 'siteUrl', prop: 'zone5',
type: 'input', type: 'input',
label: 'ZONE 5', label: 'ZONE 5',
attrs: { attrs: {
placeholder: '请输入查询网址', placeholder: '请输入ZONE 5',
}, },
rules: [
{
required: true,
message: '请输入物流编码',
},
],
}, },
{ {
prop: 'status', prop: 'zone6',
type: 'input', type: 'input',
label: 'ZONE 6', label: 'ZONE 6',
attrs: {
placeholder: '请输入ZONE 6',
},
}, },
{ {
prop: 'status', prop: 'zone7',
type: 'input', type: 'input',
label: 'ZONE 7', label: 'ZONE 7',
attrs: {
placeholder: '请输入ZONE 7',
},
}, },
{ {
prop: 'status', prop: 'zone8',
type: 'input', type: 'input',
label: 'ZONE 8', label: 'ZONE 8',
attrs: {
placeholder: '请输入ZONE 8',
},
}, },
{ {
prop: 'status', prop: 'zone9',
type: 'input', type: 'input',
label: 'ZONE 9', label: 'ZONE 9',
attrs: {
placeholder: '请输入ZONE 9',
},
}, },
]) ])
const tableConfig = ref([ const tableConfig = ref([
{ {
prop: 'name', prop: 'rateType',
label: 'Rate(oz/lb)', label: 'Rate(oz/LB)',
attrs: {
align: 'center',
width: 100,
},
}, },
{ {
prop: 'warehouseName', prop: 'rateG',
label: 'Rate(g)', label: 'Rate(g)',
}, },
{ {
prop: 'serviceCode', prop: 'rateKg',
label: 'Rate(kg)', label: 'Rate(kg)',
}, },
{ {
prop: 'status', prop: 'logistics',
label: 'Logistics', label: 'Logistics',
}, },
{ {
prop: 'status1', prop: 'zone1',
label: 'ZONE 1', label: 'ZONE 1',
}, },
{ {
prop: 'status2', prop: 'zone2',
label: 'ZONE 2', label: 'ZONE 2',
}, },
{ {
prop: 'status2', prop: 'zone3',
label: 'ZONE 3', label: 'ZONE 3',
}, },
{ {
prop: 'siteUrl', prop: 'zone4',
label: 'ZONE 4', label: 'ZONE 4',
}, },
{ {
prop: 'siteUrl', prop: 'zone5',
label: 'ZONE 5', label: 'ZONE 5',
}, },
{ {
prop: 'siteUrl', prop: 'zone6',
label: 'ZONE 6', label: 'ZONE 6',
}, },
{ {
prop: 'siteUrl', prop: 'zone7',
label: 'ZONE 7', label: 'ZONE 7',
}, },
{ {
prop: 'siteUrl', prop: 'zone8',
label: 'ZONE 8', label: 'ZONE 8',
}, },
{ {
prop: 'siteUrl', prop: 'zone9',
label: 'ZONE 9', label: 'ZONE 9',
}, },
...@@ -309,59 +360,87 @@ const tableConfig = ref([ ...@@ -309,59 +360,87 @@ const tableConfig = ref([
> >
<Edit /> <Edit />
</el-icon> </el-icon>
<el-icon
size="24"
title="删除"
color="#f56c6c"
style="cursor: pointer; vertical-align: middle"
onclick={() => deleteWay(row)}
>
<Delete />
</el-icon>
</div> </div>
), ),
}, },
}, },
]) ])
watch(
() => searchForm.value,
(val) => {
if (val.logisticsList === '') {
val.logisticsList = []
}
},
{ immediate: true, deep: true },
)
watch(
() => data.value,
(val) => {
try {
tableData.value = getTableData(val)
if (searchForm.value.logisticsList?.length) {
let startRow = 0
mergeCells.value = []
data.value.forEach((item) => {
if (item.logisticsQuotationList?.length) {
const rowspan = item.logisticsQuotationList.length
for (let col = 0; col < 5; col++) {
mergeCells.value.push({
row: startRow,
col: col,
rowspan: rowspan,
colspan: 1,
})
}
startRow += rowspan
}
})
}
} catch (e) {
console.log(e)
} finally {
// loading.value = false
}
},
{ immediate: true, deep: true },
)
onMounted(() => { onMounted(() => {
// getAllList() // loading.value = true
getAllList()
}) })
const loading = ref(false) function getTableData(arr) {
const newArr = []
if (arr.length) {
arr.forEach((item, index) => {
if (item.logisticsQuotationList?.length) {
item.logisticsQuotationList.forEach((item) => {
item.seq = index + 1
newArr.push(item)
})
}
})
return newArr
}
return []
}
function cancelFn() { function cancelFn() {
dialogVisible.value = false dialogVisible.value = false
editFormRef.value?.resetFields() editFormRef.value?.resetFields()
resetEditForm() resetEditForm()
} }
async function deleteWay(item) {
try {
await showConfirm('是否删除用户', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
})
} catch {
return
}
try {
const ids = [item.id].join(',')
await deleteLogisticsQuotation(ids)
ElMessage({
message: '删除成功',
type: 'success',
})
search()
} catch (e) {
search()
// showError(e)
}
}
async function editWay(item: LogisticsMethod) { async function editWay(item: LogisticsMethod) {
try { try {
editForm.value = { ...item } editForm.value = { ...item }
console.log(432, editForm.value)
// formConfig.value[0].attrs!.disabled = true
// formConfig.value[1].attrs!.disabled = true
dialogVisible.value = true dialogVisible.value = true
} catch (e) { } catch (e) {
console.log(e) console.log(e)
...@@ -418,6 +497,7 @@ async function save() { ...@@ -418,6 +497,7 @@ async function save() {
} }
function addDialog() { function addDialog() {
dialogVisible.value = true dialogVisible.value = true
console.log(502, editForm.value)
} }
async function deleteFn() { async function deleteFn() {
if (!selection.value.length) { if (!selection.value.length) {
...@@ -457,18 +537,77 @@ function handleCheckboxRecords(value: never[]) { ...@@ -457,18 +537,77 @@ function handleCheckboxRecords(value: never[]) {
async function getAllList() { async function getAllList() {
try { try {
const res = await Promise.all([getlogisticsWayAllList()]) const res = await Promise.all([getlogisticsWayAllList()])
searchConfig.value[0].attrs.options = []
console.log(545, res) console.log(545, res)
searchConfig.value[0].attrs!.options = [...(res[0]?.data || [])]
formConfig.value[0].attrs!.options = [...(res[0]?.data || [])]
} catch (error) {
console.log(error)
}
}
function onBeforeUploadImage(file) {
const isIMAGE =
file.type ==
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
if (!isIMAGE) {
ElMessage.error('上传文件只能是excel格式!')
}
return isIMAGE
}
async function downloadExcel() {
try {
const res = await downloadLogisticsQuotationTemplate()
console.log(483, data)
const blob = new Blob([res])
// 2. 解析文件名(从响应头或自定义)
const filename = '物流报价模版.xlsx'
// 3. 创建临时链接并触发下载
const link = document.createElement('a')
link.href = window.URL.createObjectURL(blob)
link.download = filename
link.click()
} catch (error) { } catch (error) {
console.log(error) console.log(error)
} }
} }
async function exportExcel(file) {
try {
const formData = new FormData()
formData.append('file', file.file)
formData.append('type', 'add')
const res = await importLogisticsQuotation(formData)
if (res.code === 200) {
ElMessage.success('导入成功!')
search()
}
} catch (error) {
ElMessage.error('导入失败!')
}
}
async function updateExcel(file) {
try {
const formData = new FormData()
formData.append('file', file.file)
formData.append('type', 'update')
function downloadExcel() {} const res = await importLogisticsQuotation(formData)
function exportExcel() {} if (res.code === 200) {
ElMessage.success('导入成功!')
search()
}
} catch (error) {
ElMessage.error('导入失败!')
}
}
watch( watch(
editForm, () => loading.value,
(val) => { (val) => {
console.log(442, val) console.log(442, val)
}, },
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment