Commit 797aadc2 by linjinhong Committed by qinjianhui

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

parent bfee1467
......@@ -114,15 +114,15 @@ export function getlogisticsWayAllList() {
return axios.get<never, BaseRespData<never>>('/logisticsWay/all_list', {})
}
//物流报价列表
export function getlogisticsQuotationList() {
return axios.get<never, BaseRespData<never>>(
export function getlogisticsQuotationList(params: any) {
return axios.post<never, BaseRespData<never>>(
'/logistics/logisticsQuotation/list_page',
{},
params,
)
}
//新增
export function addLogisticsQuotation(params: any) {
return axios.get<never, BaseRespData<never>>(
return axios.post<never, BaseRespData<never>>(
'/logistics/logisticsQuotation/add',
params,
)
......@@ -142,24 +142,31 @@ export function deleteLogisticsQuotation(params: any) {
)
}
//导入
export function importLogisticsQuotation(params: any) {
return axios.get<never, BaseRespData<never>>(
export function importLogisticsQuotation(formData: any) {
return axios.post<never, BaseRespData<never>>(
'/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>>(
'/logistics/logisticsQuotation/exportExcel',
{ params },
{
responseType: 'blob',
},
)
}
//根据ID查对象
export function getLogisticsQuotationByID(params: any) {
return axios.get<never, BaseRespData<never>>(
return axios.post<never, BaseRespData<never>>(
'/logistics/logisticsQuotation/get',
{ params },
params,
)
}
......@@ -193,3 +200,68 @@ export function deleteLogisticsCustomsRule(params: any) {
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({
await formRef.value?.resetFields()
}
function refashConfig(showFields: string[]) {
if (showFields.length) {
function refashConfig(showFields?: string[]) {
if (showFields?.length) {
const filterArr = props.config.filter((item) =>
showFields.includes(item.prop as string),
)
......@@ -142,6 +142,8 @@ export default defineComponent({
]
.filter((item) => !item.fixed)
.concat(tableConfig.value.filter((item) => item.fixed))
} else {
tableConfig.value = props.config
}
console.log('tableConfig', tableConfig.value)
}
......
......@@ -9,9 +9,12 @@
@input="iptFn"
>
<template v-if="hasSuffix" #suffix>{{ suffix }}</template>
<template v-if="hasAppend" #append>
<component :is="appendRender" />
</template>
</el-input>
</template>
<script lang="ts" setup>
<script lang="tsx" setup>
import { isNumFloat } from '@/utils/validate'
const props = withDefaults(
defineProps<{
......@@ -20,6 +23,8 @@ const props = withDefaults(
placeholder?: string
size?: '' | 'default' | 'small' | 'large'
hasSuffix?: boolean
hasAppend?: boolean
appendRender?: () => JSX.Element
isDisabled?: boolean
}>(),
{
......@@ -29,6 +34,8 @@ const props = withDefaults(
size: 'default',
hasSuffix: true,
isDisabled: false,
hasAppend: false,
appendRender: null,
},
)
const emits = defineEmits<{
......
......@@ -34,9 +34,10 @@ const props = withDefaults(
hasAllOptions?: boolean
labelIsValue?: boolean // value 使用label字段
initChange?: boolean // 首次是否触发change事件
modelValue?: string | number
modelValue?: string | number | string[] | number[]
isRefresh?: boolean
isValueKey?: boolean
load?: (arg: (val: boolean) => void) => void // focus后再加载数据的
}>(),
{
......
......@@ -104,12 +104,23 @@ export default defineComponent({
setup(props, { emit, slots, attrs }) {
const formRef = ref()
const formWrapperRef = ref()
const searchForm = ref<ISearchForm>(props.modelValue || {})
const searchForm = ref<ISearchForm>({})
const selectResult = ref<ISeachFormConfig[]>([])
const componentWidth = computed(() =>
props.size === 'default' ? 200 : 220,
)
watch(
() => props.modelValue,
(val) => {
searchForm.value = val
},
{
immediate: true,
deep: true,
},
)
function initSeachForm() {
props.config.forEach((item, index) => {
if (componentDataTypeMap.has(item.type as EComponenetType)) {
......@@ -157,7 +168,7 @@ export default defineComponent({
<div style={styles.wrapper} class="mt-15">
<ElForm
{...getFormAttrs.value}
ref={el=>formRef.value=el}
ref={(el) => (formRef.value = el)}
class="w-full"
inline
model={searchForm.value}
......
import { VNode } from 'vue'
interface ColumnAttrs {
field?: string
title?: string
......@@ -52,7 +51,7 @@ export default defineComponent({
},
},
emits: ['update:modelValue', 'checkbox-change', 'getCheckboxRecords'],
setup(props, { emit ,attrs}) {
setup(props, { emit, attrs }) {
const tableRef = ref(null)
const tableData = ref<Record<string, unknown>[]>([])
const tableColumns = ref<TableColumn[]>([])
......@@ -79,9 +78,7 @@ export default defineComponent({
watch(
() => props.modelValue,
(val) => {
tableData.value = Array.isArray(val) ? val : []
tableData.value = Array.isArray(val) ? val : []
},
{
immediate: true,
......@@ -98,7 +95,7 @@ export default defineComponent({
}
}
}
//获取选中数据
const getSelectEvent = () => {
const $table = tableRef.value
if ($table) {
......@@ -106,6 +103,14 @@ export default defineComponent({
emit('getCheckboxRecords', selectRecords)
}
}
//设置高亮行
const selectRowEvent = (row: TableRowData) => {
const $table = tableRef.value
if ($table) {
console.log(110, row)
$table.setCurrentRow(row)
}
}
onMounted(() => {
getList()
......@@ -116,62 +121,61 @@ export default defineComponent({
tableData,
tableColumns,
editConfig,
getSelectEvent,attrs
getSelectEvent,
selectRowEvent,
attrs,
}
},
render() {
return (
<vxe-table
ref={(el) => this.tableRef = el}
data={this.tableData}
height='100%'
edit-config={this.editConfig}
onCheckboxChange={this.getSelectEvent}
onCheckboxAll={this.getSelectEvent}
{...this.attrs}
>
{this.isShowCheckBox && (
<vxe-column type="checkbox" width="50" align="center"></vxe-column>
)}
<vxe-table
ref={(el) => (this.tableRef = el)}
data={this.tableData}
height="100%"
edit-config={this.editConfig}
onCheckboxChange={this.getSelectEvent}
onCheckboxAll={this.getSelectEvent}
{...this.attrs}
>
{this.isShowCheckBox && (
<vxe-column type="checkbox" width="50" align="center"></vxe-column>
)}
<vxe-column align="center" type="seq" width="50" title="序号" />
{this.tableColumns.map((item: TableColumn, index: number) => (
<vxe-column
key={index}
field={item.prop}
title={item.label}
{...item.attrs}
edit-render={{}}
>
{{
...(() => {
// 创建基础插槽配置
const slots: Record<string, SlotFunction> = {
// 默认的 default 插槽实现
default: ({ row }) => <span>{row[item.prop]}</span>,
}
<vxe-column align="center" type="seq" width="50" title="序号" />
{this.tableColumns.map((item: TableColumn, index: number) => (
<vxe-column
key={index}
field={item.prop}
title={item.label}
{...item.attrs}
edit-render={{}}
>
{{
...(() => {
// 创建基础插槽配置
const slots: Record<string, SlotFunction> = {
// 默认的 default 插槽实现
default: ({ row }) => <span>{row[item.prop]}</span>,
}
if (item.render) {
// 添加所有自定义插槽
Object.entries(item.render).forEach(
([slotName, renderFn]) => {
if (renderFn) {
slots[slotName] = (scope: { row: TableRowData }) =>
renderFn(scope)
}
},
)
}
if (item.render) {
// 添加所有自定义插槽
Object.entries(item.render).forEach(
([slotName, renderFn]) => {
if (renderFn) {
slots[slotName] = (scope: { row: TableRowData }) =>
renderFn(scope)
}
},
)
}
return slots
})(),
}}
</vxe-column>
))}
</vxe-table>
return slots
})(),
}}
</vxe-column>
))}
</vxe-table>
)
},
})
......@@ -183,7 +183,20 @@ const router = createRouter({
},
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',
meta: {
......
......@@ -136,19 +136,29 @@ const menu: MenuItem[] = [
},
{
index: '/logistics/shippingAddress',
id: 1,
id: 2,
label: '发货地址',
},
{
index: '/logistics/logisticsQuotation',
id: 1,
id: 3,
label: '物流报价',
},
{
index: '/logistics/declarationRule',
id: 1,
id: 4,
label: '申报规则',
},
{
index: '/logistics/logisticsPartition',
id: 5,
label: '物流分区',
},
{
index: '/logistics/logisticsCalculate',
id: 6,
label: '运费试算',
},
],
},
// {
......
......@@ -12,6 +12,7 @@ export interface PaginationData<D> {
size: number
current: number
records: D[]
data?: D[]
}
export interface Statistics<D> {
sumNotPassNum: number
......
......@@ -44,7 +44,6 @@ export default function usePageList<T>(options: UsePageListOptions<T>) {
pageSize.value = res.size
data.value = res.records
}
console.log(7, data.value)
} catch (error) {
console.error(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 @@
v-model="searchForm"
>
<template #ontherBtn>
<div style="margin-left: 10px">
<div style="margin-left: 10px; display: flex; gap: 10px">
<ElButton @click="downloadExcel" type="info" plain
>下载模版</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
<ElButton type="primary" plain> 导入</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>
</template></SearchForm
>
</div>
<div class="user-content flex-1 flex-column overflow-hidden">
<div class="user-list flex-1 overflow-hidden" v-loading="loading">
<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"
:seq-config="{
seqMethod: (row) => {
if (searchForm.logisticsList?.length) {
return row.row.seq
}
return row.seq
},
}"
border="full"
@getCheckboxRecords="handleCheckboxRecords"
></CustomizeTable>
</div>
......@@ -47,7 +73,7 @@
</div>
<LogDialog
:title="editForm.id ? '编辑物流方式' : '新增物流方式'"
:title="editForm.id ? '编辑物流报价' : '新增物流报价'"
dialogWidth="1000px"
v-model="dialogVisible"
@close="cancelFn"
......@@ -78,216 +104,241 @@ import {
updateLogisticsQuotation,
deleteLogisticsQuotation,
importLogisticsQuotation,
exportLogisticsQuotationTemplate,
downloadLogisticsQuotationTemplate,
} 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 Select from '@/components/Form.vue/Select.vue'
import type { VxeTablePropTypes } from 'vxe-table'
import usePageList from '@/utils/hooks/usePageList'
import { useValue } from './hooks/useValue'
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 [editForm, resetEditForm] = useValue<LogisticsMethod>({
platformList: [{ platform: [], logisticsName: '' }],
ruleRef: [{ ruleId: '', ruleName: '' }],
status: 1,
})
const [editForm, resetEditForm] = useValue({ unit: 'oz' })
const {
loading,
currentPage,
pageSize,
total,
data: tableData,
data,
refresh: search,
onCurrentPageChange: handleCurrentChange,
onPageSizeChange: handleSizeChange,
} = usePageList({
query: (page, pageSize) =>
// getlogisticsQuotationList({
// ...searchForm.value,
// pageSize: pageSize,
// currentPage: page,
// }).then(({ data }) => {
// console.log(130, data)
// return data
// }),
(() => {})(),
getlogisticsQuotationList({
...searchForm.value,
pageSize: pageSize,
currentPage: page,
}).then(({ data }) => {
console.log(130, data)
return data
}),
})
const dialogVisible = ref(false)
const editFormRef = ref(null)
const selection = ref([])
const mergeCells = ref<VxeTablePropTypes.MergeCells>([])
const tableData = ref([])
const searchConfig = ref([
{
prop: 'name',
prop: 'logisticsList',
type: 'select',
label: '物流方式',
attrs: {
placeholder: '请选择物流方式',
multiple: true,
value: 'name',
label: 'name',
collapseTags: true,
collapseTagsTooltip: true,
options: [],
},
},
])
const formConfig = ref([
{ title: '物流基础信息' },
{
prop: 'name',
prop: 'logistics',
type: 'select',
label: '物流方式',
attrs: {
placeholder: '请选择物流方式',
value: 'name',
label: 'name',
options: [],
disabled: editForm.value?.['id'] ? true : false,
onChange: (val, id) => {
editForm.value['logisticsId'] = val.id
},
},
rules: [{ required: true, message: '请选择物流方式' }],
},
{
prop: 'rate',
type: 'amountInput',
label: 'Rate(oz/LB)',
attrs: {
placeholder: '请输入Rate(oz/LB)',
hasSuffix: false,
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: '请输入Rate' }],
},
{
prop: 'zone1',
type: 'input',
label: 'ZONE 1',
attrs: {
placeholder: '请输入物流名称',
placeholder: '请输入ZONE 1',
},
rules: [
{
required: true,
message: '请输入物流名称',
},
],
},
{
prop: 'warehouseName',
type: 'select',
prop: 'zone2',
type: 'input',
label: 'ZONE 2',
attrs: {
placeholder: '请选择仓库名称',
options: [
{ label: '仓库1', value: 1 },
{ label: '仓库2', value: 2 },
],
placeholder: '请输入ZONE 2',
},
rules: [
{
required: true,
message: '请选择仓库名称',
},
],
},
{
prop: 'ruleRefObj',
prop: 'zone3',
type: 'input',
label: 'ZONE 3',
attrs: {
placeholder: '请选择申报规则',
options: [
{ label: '仓库1', value: 1 },
{ label: '仓库2', value: 2 },
],
placeholder: '请输入ZONE 3',
},
rules: [
{
required: true,
message: '请选择申报规则',
},
],
},
{
prop: 'serviceCode',
prop: 'zone4',
type: 'input',
label: 'ZONE 4',
attrs: {
placeholder: '请输入物流编码',
placeholder: '请输入ZONE 4',
},
rules: [
{
required: true,
message: '请输入物流编码',
},
],
},
{
prop: 'siteUrl',
prop: 'zone5',
type: 'input',
label: 'ZONE 5',
attrs: {
placeholder: '请输入查询网址',
placeholder: '请输入ZONE 5',
},
rules: [
{
required: true,
message: '请输入物流编码',
},
],
},
{
prop: 'status',
prop: 'zone6',
type: 'input',
label: 'ZONE 6',
attrs: {
placeholder: '请输入ZONE 6',
},
},
{
prop: 'status',
prop: 'zone7',
type: 'input',
label: 'ZONE 7',
attrs: {
placeholder: '请输入ZONE 7',
},
},
{
prop: 'status',
prop: 'zone8',
type: 'input',
label: 'ZONE 8',
attrs: {
placeholder: '请输入ZONE 8',
},
},
{
prop: 'status',
prop: 'zone9',
type: 'input',
label: 'ZONE 9',
attrs: {
placeholder: '请输入ZONE 9',
},
},
])
const tableConfig = ref([
{
prop: 'name',
label: 'Rate(oz/lb)',
prop: 'rateType',
label: 'Rate(oz/LB)',
attrs: {
align: 'center',
width: 100,
},
},
{
prop: 'warehouseName',
prop: 'rateG',
label: 'Rate(g)',
},
{
prop: 'serviceCode',
prop: 'rateKg',
label: 'Rate(kg)',
},
{
prop: 'status',
prop: 'logistics',
label: 'Logistics',
},
{
prop: 'status1',
prop: 'zone1',
label: 'ZONE 1',
},
{
prop: 'status2',
prop: 'zone2',
label: 'ZONE 2',
},
{
prop: 'status2',
prop: 'zone3',
label: 'ZONE 3',
},
{
prop: 'siteUrl',
prop: 'zone4',
label: 'ZONE 4',
},
{
prop: 'siteUrl',
prop: 'zone5',
label: 'ZONE 5',
},
{
prop: 'siteUrl',
prop: 'zone6',
label: 'ZONE 6',
},
{
prop: 'siteUrl',
prop: 'zone7',
label: 'ZONE 7',
},
{
prop: 'siteUrl',
prop: 'zone8',
label: 'ZONE 8',
},
{
prop: 'siteUrl',
prop: 'zone9',
label: 'ZONE 9',
},
......@@ -309,59 +360,87 @@ const tableConfig = ref([
>
<Edit />
</el-icon>
<el-icon
size="24"
title="删除"
color="#f56c6c"
style="cursor: pointer; vertical-align: middle"
onclick={() => deleteWay(row)}
>
<Delete />
</el-icon>
</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(() => {
// 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() {
dialogVisible.value = false
editFormRef.value?.resetFields()
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) {
try {
editForm.value = { ...item }
console.log(432, editForm.value)
// formConfig.value[0].attrs!.disabled = true
// formConfig.value[1].attrs!.disabled = true
dialogVisible.value = true
} catch (e) {
console.log(e)
......@@ -418,6 +497,7 @@ async function save() {
}
function addDialog() {
dialogVisible.value = true
console.log(502, editForm.value)
}
async function deleteFn() {
if (!selection.value.length) {
......@@ -457,18 +537,77 @@ function handleCheckboxRecords(value: never[]) {
async function getAllList() {
try {
const res = await Promise.all([getlogisticsWayAllList()])
searchConfig.value[0].attrs.options = []
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) {
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() {}
function exportExcel() {}
const res = await importLogisticsQuotation(formData)
if (res.code === 200) {
ElMessage.success('导入成功!')
search()
}
} catch (error) {
ElMessage.error('导入失败!')
}
}
watch(
editForm,
() => loading.value,
(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