Commit 67ef1de4 by linjinhong Committed by wusiyi

feat:【工厂端】工厂端-供应模块新增供应商管理#1001007

parent 40cfb810
import { BasePaginationData, BaseRespData } from '@/types/api' import { BasePaginationData, BaseRespData } from '@/types/api'
import axios from './../axios' import axios from './../axios'
import {
IListPage,
IsupplierType,
} from '@/views/supply/supplierManagement/types/index'
export interface IListPage {
pageSize: number | string
currentPage: number | string
}
//供应商分页查询 //供应商分页查询
export function getSupplierListApi(params: IListPage) { export function getSupplierListApi(params: IListPage) {
return axios.post<never, BasePaginationData<never>>( return axios.get<never, BasePaginationData<never>>(
'/factory/supplier/list_page', '/factory/supplier/list_page',
params, { params },
) )
} }
...@@ -19,3 +19,46 @@ export function deleteSupplierApi(params: { ids: string }) { ...@@ -19,3 +19,46 @@ export function deleteSupplierApi(params: { ids: string }) {
params, params,
}) })
} }
// 编辑回显接口
export function getSupplierDetailApi(id: string | number) {
return axios.get<never, BaseRespData<never>>('factory/supplier/get', {
params: { id },
})
}
// 根据分类id获取属性信息
export function getPropertyByCateIdApi(cateId: string | number) {
return axios.get<never, BaseRespData<never>>(
'/factory/supplier/getPropertyByCateId',
{
params: { cateId },
},
)
}
// 根据spu获取商品信息
export function getProductInfoBySpuApi(spu: string | number) {
return axios.get<never, BaseRespData<never>>(
'/factory/supplier/getProductInfoBySpu',
{
params: { spu },
},
)
}
// 获取币种接口
export function getBaseCurrencyInfoApi() {
return axios.get<never, BaseRespData<never>>(
'factory/supplier/getBaseCurrencyInfo',
)
}
//新增
export function addSupplierApi(params: IsupplierType) {
return axios.post<never, BaseRespData<never>>('/factory/supplier/add', params)
}
//修改
export function updateSupplierApi(params: IsupplierType) {
return axios.post<never, BaseRespData<never>>(
'/factory/supplier/update',
params,
)
}
...@@ -110,6 +110,13 @@ export default defineComponent({ ...@@ -110,6 +110,13 @@ export default defineComponent({
emit('getCheckboxRecords', selectRecords) emit('getCheckboxRecords', selectRecords)
} }
} }
//获取设置多选框
const setCheckboxRow = (row: TableRowData, checked: boolean) => {
const $table = tableRef.value
if ($table) {
$table.setCheckboxRow(row, checked)
}
}
//设置高亮行 //设置高亮行
const selectRowEvent = (row: TableRowData) => { const selectRowEvent = (row: TableRowData) => {
const $table = tableRef.value const $table = tableRef.value
...@@ -129,6 +136,7 @@ export default defineComponent({ ...@@ -129,6 +136,7 @@ export default defineComponent({
editConfig, editConfig,
getSelectEvent, getSelectEvent,
selectRowEvent, selectRowEvent,
setCheckboxRow,
attrs, attrs,
} }
}, },
......
import { defineComponent, ref } from 'vue'
import {
ElPopover,
ElScrollbar,
ElCheckbox,
ElCheckboxGroup,
ElRadioGroup,
ElRadio,
ElInput,
} from 'element-plus'
const styles = {
searchForm: {
position: 'relative',
},
titleBox: {
display: 'flex',
padding: '10px 18px',
justifyContent: 'space-between',
alignItems: 'center',
},
checkboxGroup: {
display: 'flex',
padding: '10px',
justifyContent: 'flex-start',
flexWrap: 'wrap',
backgroundColor: '#efefef',
},
checkbox: {
width: '33.3333%',
'margin-right': '0px !important',
},
} as const
interface ICompanyList {
warehouseName: string
wayList: IwayList[]
}
interface IwayList {
name: string
id: string
}
interface IAllList {
factoryId?: number
id: number | string
name: string
serviceCode?: string
siteUrl?: string
status?: number
uinuinWarehouseId?: number | null
updateTime?: string
warehouseId?: number
warehouseName?: string
wayList?: IwayList[]
}
export default defineComponent({
name: 'CustomizeForm',
props: {
modelValue: {
type: [Array, String, Number] as PropType<
(string | number)[] | string | number
>,
default: () => [],
// 可选:添加自定义验证器确保类型安全
validator: (value: unknown) => {
return (
Array.isArray(value) ||
typeof value === 'string' ||
typeof value === 'number'
)
},
},
companyList: {
type: Array as PropType<IAllList[] | ICompanyList[]>,
default: () => [],
},
isRadio: {
type: Boolean,
default: false,
},
valueKey: {
type: String,
default: 'id',
},
searchPlaceholder: {
type: String,
default: '请搜索承运商',
},
startPlaceholder: {
type: String,
default: '请选择物流方式',
},
startWidth: {
type: String,
default: '100%',
},
},
emits: ['update:modelValue'],
setup(props, { emit }) {
const companyList = ref<ICompanyList[]>([])
const templeCompanyList = ref<ICompanyList[] | IwayList[] | IAllList[]>([])
const allList = ref<IAllList[]>([])
const allWayLists = ref<IwayList[]>([])
const selectedList = ref<(string | number)[]>([])
const selectedRadioList = ref<string | number>()
const waysName = ref('')
const searchName = ref('')
watch(
() => props.modelValue,
(newVal) => {
if (props.isRadio) {
allWayLists.value = props.companyList.flatMap(
(company) => company.wayList,
) as IwayList[]
if (props.valueKey === 'id') {
selectedRadioList.value = newVal as string | number
} else {
selectedRadioList.value =
allWayLists.value.find((el: IwayList) => newVal === el.name)
?.id || ''
}
} else {
selectedList.value = newVal as (string | number)[]
}
},
{
immediate: true,
deep: true,
},
)
watch(
[
() => selectedList.value,
() => props.companyList,
() => selectedRadioList.value,
],
(newVal) => {
if (props.isRadio) {
companyList.value = newVal[1] as ICompanyList[]
allWayLists.value = props.companyList.flatMap(
(company) => company.wayList,
) as IwayList[]
waysName.value =
allWayLists.value.find((el: IwayList) => newVal[2] === el.id)
?.name || ''
if (props.valueKey === 'id') {
emit('update:modelValue', newVal[2])
} else {
emit('update:modelValue', waysName.value)
}
} else {
emit('update:modelValue', newVal[0])
allList.value = newVal[1] as IAllList[]
companyList.value = transformData(newVal[1] as IAllList[])
if (newVal[1]?.length) {
waysName.value = (newVal[1] as IAllList[])
.filter((item) => {
if (newVal[0].includes(item.id)) {
return item.name
}
})
.map((item) => item.name)
.join(',')
// emit('waysName', res)
// console.log(87, waysName.value)
}
}
},
{ deep: true, immediate: true },
)
const handleClearSelection = () => {
/* 1. 清空显示文本 */
waysName.value = ''
/* 2. 清空双向绑定结果 */
if (props.isRadio) {
selectedRadioList.value = '' // 单选
emit('update:modelValue', '')
} else {
selectedList.value = [] // 多选
emit('update:modelValue', [])
}
/* 3. 可选:把搜索框也重置 */
searchName.value = ''
}
function setCheckAll(company: ICompanyList, event: boolean) {
if (event) {
selectedList.value = [
...selectedList.value,
...company.wayList.map((item) => item.id),
]
} else {
selectedList.value = selectedList.value.filter(
(item) =>
!company.wayList.map((el) => el.id).includes(item as string),
)
}
}
const getCompanySelectedStatus = computed(() => {
const statusMap = new Map()
companyList.value.forEach((company: ICompanyList) => {
const allSelected = company.wayList.every((way) =>
selectedList.value.includes(way.id),
)
statusMap.set(company.warehouseName, allSelected)
})
return (company: ICompanyList) => statusMap.get(company.warehouseName)
})
function fuzzySearch<T>(
items: T[],
searchTerm: string,
key: keyof T = 'name' as keyof T,
): T[] {
// 空搜索返回所有元素
if (!searchTerm.trim()) {
return [...items]
}
// 将搜索词转为小写(不区分大小写)
const searchLower = searchTerm.toLowerCase()
return items.filter((item) => {
// 获取属性值
const value = item[key]
// 确保属性值存在且为字符串
if (typeof value !== 'string') return false
// 将属性值转为小写并检查是否包含搜索词
return value.toLowerCase().includes(searchLower)
})
}
function transformData(data: IAllList[]) {
const warehouseMap = new Map()
for (const item of data) {
const warehouseName = item.warehouseName ?? '未命名仓库'
if (!warehouseMap.has(warehouseName)) {
warehouseMap.set(warehouseName, new Set())
}
warehouseMap.get(warehouseName).add({ name: item.name, id: item.id })
}
const result: {
warehouseName: string
wayList: { name: string; id: string }[]
}[] = []
warehouseMap.forEach((children, parent) => {
result.push({
warehouseName: parent,
wayList: Array.from(children),
})
})
return result
}
return () => (
<ElPopover
width="750px"
placement="bottom-start"
trigger="click"
popper-style={{ padding: 0 }}
onUpdate:visible={(value) => {
if (value) searchName.value = ''
}}
v-slots={{
reference: () => (
<ElInput
modelValue={waysName.value}
style={{ width: props.startWidth }}
placeholder={props.startPlaceholder}
clearable
onClear={handleClearSelection}
/>
),
}}
>
<ElInput
modelValue={searchName.value}
onUpdate:modelValue={(value) => {
if (props.isRadio) {
templeCompanyList.value = fuzzySearch(allWayLists.value, value)
} else {
templeCompanyList.value = fuzzySearch(allList.value, value)
}
console.log('templeCompanyList', templeCompanyList.value)
searchName.value = value
}}
clearable
style={{ width: '200px', padding: '10px' }}
placeholder={props.searchPlaceholder}
/>
<ElScrollbar
class="scroll-container"
maxHeight="450px"
style={{ 'border-top': '1px solid #eee' }}
>
{!searchName.value ? (
companyList.value.map((company, index) => (
<div class="companyBox" key={index}>
<div style={styles.titleBox}>
<div class="title">{company.warehouseName}</div>
{!props.isRadio && (
<ElCheckbox
modelValue={getCompanySelectedStatus.value(
company as ICompanyList,
)}
onChange={(v) =>
setCheckAll(company as ICompanyList, v as boolean)
}
class="selectAll"
>
全选
</ElCheckbox>
)}
</div>
{props.isRadio ? (
<ElRadioGroup
modelValue={selectedRadioList.value}
onUpdate:modelValue={(value) => {
console.log('company', value)
selectedRadioList.value = value as string | number
}}
style={styles.checkboxGroup}
>
{company.wayList?.map((item) => (
<div title={item.name} style={styles.checkbox}>
<ElRadio
label={item.name}
value={item.id}
key={`item-${item.id}`}
>
{item.name}
</ElRadio>
</div>
))}
</ElRadioGroup>
) : (
<ElCheckboxGroup
modelValue={selectedList.value}
onUpdate:modelValue={(value) =>
(selectedList.value = value)
}
style={styles.checkboxGroup}
>
{(company as ICompanyList).wayList.map((item) => (
<div title={item.name} style={styles.checkbox}>
<ElCheckbox
label={item.name}
value={item.id}
key={`item-${item.id}`}
class="checkboxItem"
/>
</div>
))}
</ElCheckboxGroup>
)}
</div>
))
) : props.isRadio ? (
<ElRadioGroup
modelValue={selectedRadioList.value}
onUpdate:modelValue={(value) => {
console.log('company', value)
selectedRadioList.value = value as string | number
}}
style={styles.checkboxGroup}
>
{(templeCompanyList.value as IwayList[]).map((item) => (
<div title={item.name} style={styles.checkbox}>
<ElRadio
label={item.name}
value={item.id}
key={`item-${item.id}`}
>
{item.name}
</ElRadio>
</div>
))}
</ElRadioGroup>
) : (
<ElCheckboxGroup
modelValue={selectedList.value}
onUpdate:modelValue={(value) => (selectedList.value = value)}
style={styles.checkboxGroup}
>
{(templeCompanyList.value as IwayList[]).map((item) => (
<div title={item.name} style={styles.checkbox}>
<ElCheckbox
label={item.name}
value={item.id}
key={`item-${item.id}`}
class="checkboxItem"
/>
</div>
))}
</ElCheckboxGroup>
)}
</ElScrollbar>
</ElPopover>
)
},
})
...@@ -32,23 +32,25 @@ export default defineComponent({ ...@@ -32,23 +32,25 @@ export default defineComponent({
return () => { return () => {
return ( return (
<ElDialog <div>
ref={formRef} <ElDialog
v-model={isShow.value} ref={formRef}
title={props.title} v-model={isShow.value}
width={props.dialogWidth} title={props.title}
onClose={() => { width={props.dialogWidth}
emit('close') onClose={() => {
}} emit('close')
destroy-on-close={true} }}
close-on-click-modal={false} destroy-on-close={true}
{...attrs} close-on-click-modal={false}
> {...attrs}
<div class="dialog-form"> >
{slots.default?.()} <div class="dialog-form">
{slots.footer?.()} {slots.default?.()}
</div> {slots.footer?.()}
</ElDialog> </div>
</ElDialog>
</div>
) )
} }
}, },
......
import { Ref, ref } from 'vue'
import { cloneDeep } from 'lodash-es'
export function useValue<T extends object>(
initialValue: T,
): [Ref<T>, () => void] {
const value = ref<T>(cloneDeep(initialValue)) as Ref<T>
const resetToDefault = () => {
value.value = cloneDeep(initialValue)
}
return [value, resetToDefault]
}
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
width="75%" width="75%"
> >
<CustomizeForm <CustomizeForm
v-loading="editLoading"
ref="editFormRef" ref="editFormRef"
v-model="editForm" v-model="editForm"
:config="formConfig" :config="formConfig"
...@@ -54,34 +55,81 @@ ...@@ -54,34 +55,81 @@
</Dialog> </Dialog>
<Dialog <Dialog
title="管理供应价格" title="管理供应价格"
v-model="piceDialogVisible" v-model="priceDialogVisible"
width="65%" width="65%"
v-loading="priceLoading"
@close="cancelPiceFn" @close="cancelPiceFn"
teleported
> >
<div style="display: flex; margin-bottom: 10px"> <div style="display: flex; margin-bottom: 10px">
<div style="display: flex; align-items: center; gap: 20px"> <div style="display: flex; align-items: center; gap: 20px">
<div><span style="color: red">*</span> 结算币种</div> <div><span style="color: red">*</span> 结算币种</div>
<el-select style="width: 300px; flex: 1"></el-select> <el-select
style="width: 300px; flex: 1"
v-model="currentGoods.currencyCode"
@change="
(v:string) => {
currentGoods.currencyName =
currencyOptions?.find((el) => v == el.currencyCode)
?.currencyName || ''
console.log(73,currentGoods.currencyName);
}
"
>
<el-option
v-for="(item, index) in currencyOptions"
:key="index"
:label="`${item.currencyName}(${item.currencyCode})`"
:value="item.currencyCode"
>
</el-option>
</el-select>
</div> </div>
</div> </div>
<div style="border-top: 1px solid #eee; margin-bottom: 10px"> <div
style="border-top: 1px solid #eee; margin-bottom: 10px"
v-if="showColorList.length"
>
<div style="font-size: 20px; font-weight: bold; margin: 10px 0"> <div style="font-size: 20px; font-weight: bold; margin: 10px 0">
颜色(Color) 颜色(Color)
</div> </div>
<div class="flex gap-2"> <div class="flex" style="flex-wrap: wrap; gap: 5px">
<el-tag color="#0000ff" v-for="value in 8" :key="value" <el-tag
>Tag {{ value }}</el-tag :title="`${item.cnname}(${item.enname})`"
:color="item.bgColor"
v-for="(item, index) in showColorList"
:key="index"
:style="{ color: item.fontColor }"
class="tabBox"
:class="{ active: colorIndex === item.code }"
@click="optionSelection(item, 'color')"
>{{ item.cnname }}({{ item.enname }})</el-tag
> >
</div> </div>
</div> </div>
<div style="border-top: 1px solid #eee; margin-bottom: 10px"> <div
style="border-top: 1px solid #eee; margin-bottom: 10px"
v-if="showSizeList.length"
>
<div style="font-size: 20px; font-weight: bold; margin: 10px 0"> <div style="font-size: 20px; font-weight: bold; margin: 10px 0">
尺码(Size) 尺码(Size)
</div> </div>
<div class="flex gap-2"> <div class="flex" style="flex-wrap: wrap; gap: 5px">
<el-tag color="#0000ff" v-for="value in 8" :key="value" <el-tag
>Tag {{ value }}</el-tag class="tabBox"
color="#ffff87"
style="color: #333"
effect="dark"
type="info"
v-for="(item, index) in showSizeList"
:key="index"
:class="{ active: sizeIndex === item.code }"
@click="optionSelection(item, 'size')"
>
{{ item.cnname }}</el-tag
> >
</div> </div>
</div> </div>
...@@ -95,23 +143,29 @@ ...@@ -95,23 +143,29 @@
> >
<div style="margin: 10px 0"> <div style="margin: 10px 0">
<el-input <el-input
placeholder="请输入更新价格" placeholder="请输入供应价格"
style="width: 200px; margin-right: 20px" style="width: 200px; margin-right: 10px"
size="small" size="small"
v-model="supplierPirce"
clearable
></el-input ></el-input
><ElButton type="primary" @click="updatePrices" size="small" ><ElButton type="primary" @click="updatePrices" size="small"
>批量更新价格</ElButton >批量更新供应价格</ElButton
>
<span style="color: #f56c6c; vertical-align: middle"
>(请注意!该操作会覆盖已有供应价格)</span
> >
</div> </div>
</div> </div>
<CustomizeTable <CustomizeTable
ref="tableRef"
border border
style="margin-bottom: 20px" style="margin-bottom: 20px"
v-model="picetableData" v-model="pricetableData"
:config="supplyTableConfig" :config="priceTableConfig"
align="center" align="center"
height="500px" height="500px"
@getCheckboxRecords="handleCheckboxRecords" @getCheckboxRecords="selectPirce"
></CustomizeTable> ></CustomizeTable>
<template #footer> <template #footer>
<div style="text-align: center"> <div style="text-align: center">
...@@ -130,6 +184,12 @@ defineOptions({ ...@@ -130,6 +184,12 @@ defineOptions({
import { import {
getSupplierListApi, getSupplierListApi,
deleteSupplierApi, deleteSupplierApi,
getSupplierDetailApi,
getPropertyByCateIdApi,
getProductInfoBySpuApi,
getBaseCurrencyInfoApi,
addSupplierApi,
updateSupplierApi,
} from '@/api/supplier/supplierManagement.ts' } from '@/api/supplier/supplierManagement.ts'
import Dialog from './components/dialog.tsx' import Dialog from './components/dialog.tsx'
...@@ -137,18 +197,24 @@ import CustomizeForm from '@/components/CustomizeForm.tsx' ...@@ -137,18 +197,24 @@ import CustomizeForm from '@/components/CustomizeForm.tsx'
import CustomizeTable from '@/components/VxeTable.tsx' import CustomizeTable from '@/components/VxeTable.tsx'
import { IFormConfig } from '@/components/CustomizeForm.tsx' import { IFormConfig } from '@/components/CustomizeForm.tsx'
import usePageList from '@/utils/hooks/usePageList' import usePageList from '@/utils/hooks/usePageList'
import { useValue } from './hooks/useValue' import { useValue } from '@/utils/hooks/useValue'
import { showConfirm } from '@/utils/ui' import { showConfirm } from '@/utils/ui'
import { debounce } from 'lodash-es' import { debounce, cloneDeep } from 'lodash-es'
import { AddDeclarationRuleObj } from './types/declarationRule'
import { Edit, CirclePlus } from '@element-plus/icons-vue' import { Edit, CirclePlus } from '@element-plus/icons-vue'
import { TableColumn } from '@/components/VxeTable' import { TableColumn } from '@/components/VxeTable'
import {
IgoodsType,
IsupplierType,
IcurrencyCode,
IcolorType,
IsizeType,
IPropertyResponseItem,
Iprice,
} from './types/index.ts'
const [editForm, resetEditForm] = useValue<AddDeclarationRuleObj>({ const [editForm, resetEditForm] = useValue<IsupplierType>({})
type: 1,
currency: 'USD',
})
const { const {
loading, loading,
currentPage, currentPage,
...@@ -169,14 +235,15 @@ const { ...@@ -169,14 +235,15 @@ const {
}), }),
}) })
const dialogVisible = ref(false) const dialogVisible = ref(false)
const editLoading = ref(false)
const goodsTableData = ref([]) const goodsTableData = ref([])
const supplierTableData = ref([]) const supplierTableData = ref<IgoodsType[]>([])
const piceDialogVisible = ref(false) const priceDialogVisible = ref(false)
const picetableData = ref([]) const pricetableData = ref<Iprice[]>([])
const editFormRef = ref<InstanceType<typeof CustomizeForm> | null>(null) const editFormRef = ref<InstanceType<typeof CustomizeForm> | null>(null)
const selection = ref([]) const selection = ref<Iprice[]>([])
interface IOption { interface IOption {
[key: string]: unknown [key: string]: unknown
...@@ -184,12 +251,12 @@ interface IOption { ...@@ -184,12 +251,12 @@ interface IOption {
const formConfig = computed<IFormConfig[]>(() => [ const formConfig = computed<IFormConfig[]>(() => [
{ {
prop: 'name', prop: 'supplierName',
type: 'input', type: 'input',
label: '供应商名称', label: '供应商名称',
attrs: { attrs: {
placeholder: '请输入供应商名称', placeholder: '请输入供应商名称',
width: '33%', width: '33.333%',
}, },
rules: [ rules: [
{ {
...@@ -199,12 +266,12 @@ const formConfig = computed<IFormConfig[]>(() => [ ...@@ -199,12 +266,12 @@ const formConfig = computed<IFormConfig[]>(() => [
], ],
}, },
{ {
prop: 'name', prop: 'contacts',
type: 'input', type: 'input',
label: '联系人', label: '联系人',
attrs: { attrs: {
placeholder: '请输入联系人', placeholder: '请输入联系人',
width: '33%', width: '33.333%',
}, },
rules: [ rules: [
{ {
...@@ -214,12 +281,12 @@ const formConfig = computed<IFormConfig[]>(() => [ ...@@ -214,12 +281,12 @@ const formConfig = computed<IFormConfig[]>(() => [
], ],
}, },
{ {
prop: 'name', prop: 'contactsNumber',
type: 'input', type: 'input',
label: '联系电话', label: '联系电话',
attrs: { attrs: {
placeholder: '请输入联系电话', placeholder: '请输入联系电话',
width: '33%', width: '33.333%',
}, },
rules: [ rules: [
{ {
...@@ -229,7 +296,7 @@ const formConfig = computed<IFormConfig[]>(() => [ ...@@ -229,7 +296,7 @@ const formConfig = computed<IFormConfig[]>(() => [
], ],
}, },
{ {
prop: 'name', prop: 'address',
type: 'input', type: 'input',
label: '地址', label: '地址',
attrs: { attrs: {
...@@ -261,53 +328,61 @@ const formConfig = computed<IFormConfig[]>(() => [ ...@@ -261,53 +328,61 @@ const formConfig = computed<IFormConfig[]>(() => [
render: () => { render: () => {
return ( return (
<div style={{ width: '100%' }}> <div style={{ width: '100%' }}>
<div <div class="flex" style={{ justifyContent: 'space-between' }}>
style={{ <div
display: 'flex', style={{
flexWrap: 'nowrap', display: 'flex',
alignItems: 'center', flexWrap: 'nowrap',
gap: '20px', alignItems: 'center',
marginBottom: '20px', gap: '20px',
}} marginBottom: '20px',
>
<div>管理供应商品</div>
<el-input
v-model={searchSupplierGoods.value}
clearable
placeholder="九猫商品SPU"
style={{ width: '200px' }}
></el-input>
<el-popover
placement="right"
trigger="click"
width="1050"
v-slots={{
reference: () => (
<el-button
type="primary"
onclick={() => searchSupplyGoodsFn()}
>
查询
</el-button>
),
}} }}
> >
<CustomizeTable <div>管理供应商品</div>
v-loading={goodsLoading.value} <el-input
isShowCheckBox={false} v-model={searchSupplierGoods.value}
modelValue={goodsTableData.value} clearable
config={searchTableConfig.value} placeholder="九猫商品SPU"
{...{ height: '600px', align: 'center', border: true }} style={{ width: '200px' }}
style={{ marginBottom: '20px' }} ></el-input>
></CustomizeTable> <el-popover
</el-popover> placement="right"
trigger="click"
width="1050"
disabled={isShowSearch.value}
v-slots={{
reference: () => (
<el-button
type="primary"
onclick={() => searchSupplyGoodsFn()}
>
查询
</el-button>
),
}}
>
<CustomizeTable
v-loading={goodsLoading.value}
isShowCheckBox={false}
modelValue={goodsTableData.value}
config={searchTableConfig.value}
{...{ height: '600px', align: 'center', border: true }}
style={{ marginBottom: '20px' }}
></CustomizeTable>
</el-popover>
</div>
<div>
<el-button type="danger" onclick={() => deleteSupplyGoodsFn()}>
删除
</el-button>
</div>
</div> </div>
<CustomizeTable <CustomizeTable
modelValue={supplierTableData.value} modelValue={supplierTableData.value}
config={formTableConfig.value} config={formTableConfig.value}
{...{ height: '500px', align: 'center', border: true }} {...{ height: '400px', align: 'center', border: true }}
style={{ marginBottom: '20px' }} style={{ marginBottom: '20px' }}
onGetCheckboxRecords={(v) => getSupplierItem(v)}
></CustomizeTable> ></CustomizeTable>
</div> </div>
) )
...@@ -317,20 +392,20 @@ const formConfig = computed<IFormConfig[]>(() => [ ...@@ -317,20 +392,20 @@ const formConfig = computed<IFormConfig[]>(() => [
const tableConfig = ref<TableColumn[]>([ const tableConfig = ref<TableColumn[]>([
{ {
prop: 'name', prop: 'supplierName',
label: '供应商名称', label: '供应商名称',
}, },
{ {
prop: 'currency', prop: 'contacts',
label: '联系人', label: '联系人',
}, },
{ {
prop: 'type', prop: 'contactsNumber',
label: '联系电话', label: '联系电话',
}, },
{ {
prop: 'logisticsWayId', prop: 'address',
label: '地址', label: '地址',
}, },
...@@ -346,14 +421,14 @@ const tableConfig = ref<TableColumn[]>([ ...@@ -346,14 +421,14 @@ const tableConfig = ref<TableColumn[]>([
align: 'center', align: 'center',
}, },
render: { render: {
default: ({ row }: { row: AddDeclarationRuleObj }) => ( default: ({ row }: { row: IsupplierType }) => (
<div> <div>
<el-icon <el-icon
size="24" size="24"
title="编辑" title="编辑"
color="#EF6C00" color="#EF6C00"
style="cursor: pointer; vertical-align: middle" style="cursor: pointer; vertical-align: middle"
onclick={() => editSupplier(row)} onclick={() => editSupplier(row.id as string)}
> >
<Edit /> <Edit />
</el-icon> </el-icon>
...@@ -364,30 +439,42 @@ const tableConfig = ref<TableColumn[]>([ ...@@ -364,30 +439,42 @@ const tableConfig = ref<TableColumn[]>([
]) ])
const formTableConfig = ref<TableColumn[]>([ const formTableConfig = ref<TableColumn[]>([
{ {
prop: 'name', prop: 'productName',
label: '商品名称', label: '商品名称',
}, },
{ {
prop: 'currency', prop: 'productNo',
label: '款号', label: '款号',
}, },
{ {
prop: 'type', prop: 'productSpu',
label: '商品SPU', label: '商品SPU',
}, },
{ {
prop: 'logisticsWayId', prop: 'productImage',
label: '商品图片', label: '商品图片',
render: {
default: ({ row }: { row: IgoodsType }) => (
<div>
<el-image style="width: 50px; height: 50px" src={row.productImage} />
</div>
),
},
}, },
{ {
prop: 'remark', prop: 'currencyCode',
label: '币种', label: '币种',
}, },
{ {
prop: 'remark', prop: 'supplyPrice',
label: '供应价格', label: '供应价格',
render: {
default: ({ row }: { row: IgoodsType }) => (
<div>{getSupplyPrice(row)}</div>
),
},
}, },
{ {
...@@ -397,7 +484,7 @@ const formTableConfig = ref<TableColumn[]>([ ...@@ -397,7 +484,7 @@ const formTableConfig = ref<TableColumn[]>([
align: 'center', align: 'center',
}, },
render: { render: {
default: ({ row }: { row: AddDeclarationRuleObj }) => ( default: ({ row }: { row: IgoodsType }) => (
<el-button type="primary" onclick={() => addPice(row)} size="small"> <el-button type="primary" onclick={() => addPice(row)} size="small">
管理供应价格 管理供应价格
</el-button> </el-button>
...@@ -405,23 +492,31 @@ const formTableConfig = ref<TableColumn[]>([ ...@@ -405,23 +492,31 @@ const formTableConfig = ref<TableColumn[]>([
}, },
}, },
]) ])
const searchTableConfig = ref<TableColumn[]>([ const searchTableConfig = ref<TableColumn[]>([
{ {
prop: 'name', prop: 'name',
label: '商品名称', label: '商品名称',
}, },
{ {
prop: 'currency', prop: 'productNo',
label: '款号', label: '款号',
}, },
{ {
prop: 'type', prop: 'sku',
label: '商品SPU', label: '商品SPU',
}, },
{ {
prop: 'logisticsWayId', prop: 'imgUrl',
label: '商品图片', label: '商品图片',
render: {
default: ({ row }: { row: IgoodsType }) => (
<div>
<el-image style="width: 50px; height: 50px" src={row.imgUrl} />
</div>
),
},
}, },
{ {
prop: 'opeare', prop: 'opeare',
...@@ -431,7 +526,7 @@ const searchTableConfig = ref<TableColumn[]>([ ...@@ -431,7 +526,7 @@ const searchTableConfig = ref<TableColumn[]>([
width: '65px', width: '65px',
}, },
render: { render: {
default: ({ row }: { row: AddDeclarationRuleObj }) => ( default: ({ row }: { row: IgoodsType }) => (
<div> <div>
<el-icon <el-icon
size="24" size="24"
...@@ -447,28 +542,46 @@ const searchTableConfig = ref<TableColumn[]>([ ...@@ -447,28 +542,46 @@ const searchTableConfig = ref<TableColumn[]>([
}, },
}, },
]) ])
const supplyTableConfig = ref<TableColumn[]>([
const priceTableConfig = ref<TableColumn[]>([
{ {
prop: 'name', prop: 'productItemImage',
label: '商品名称', label: 'SKU图片',
render: {
default: ({ row }: { row: Iprice }) => (
<div>
<el-image
style="width: 50px; height: 50px"
src={row.productItemImage}
/>
</div>
),
},
}, },
{ {
prop: 'currency', prop: 'productItemSku',
label: 'SKU码', label: 'SKU码',
}, },
{ {
prop: 'opeare', prop: 'opeare',
label: '操作', label: '供应价格',
attrs: { attrs: {
align: 'center', align: 'center',
width: '230px', width: '230px',
}, },
render: { render: {
default: ({ row }: { row: any }) => ( default: ({ row }: { row: Iprice }) => (
<div> <div>
<el-input <el-input
modelValue={row.pice} modelValue={row.supplyPrice}
onInput={(v: string | number) => {
v = String(v).replace(/[^\d.]/g, '')
const parts = v.split('.')
if (parts.length > 2) v = parts[0] + '.' + parts.slice(1).join('')
row.supplyPrice = Number(v)
}}
clearable
placeholder="请输入更新价格" placeholder="请输入更新价格"
style="width: 200px; margin-right: 20px" style="width: 200px; margin-right: 20px"
size="small" size="small"
...@@ -486,6 +599,7 @@ const searchSupplierGoods = ref('') ...@@ -486,6 +599,7 @@ const searchSupplierGoods = ref('')
*/ */
function cancelFn() { function cancelFn() {
dialogVisible.value = false dialogVisible.value = false
searchSupplierGoods.value = ''
editFormRef.value?.resetFields() editFormRef.value?.resetFields()
resetEditForm() resetEditForm()
} }
...@@ -493,32 +607,107 @@ function cancelFn() { ...@@ -493,32 +607,107 @@ function cancelFn() {
* @description: 取消供应价格按钮 * @description: 取消供应价格按钮
*/ */
function cancelPiceFn() { function cancelPiceFn() {
piceDialogVisible.value = false priceDialogVisible.value = false
} }
function savePiceFn() {} function savePiceFn() {
if (!currentGoods.value.currencyCode) {
return ElMessage({
message: '请选择结算币种',
type: 'warning',
})
}
const hasEmptyPrice = pricetableData.value.some(
(item) =>
!item.supplyPrice ||
String(item.supplyPrice).trim() === '' ||
Number(item.supplyPrice) <= 0,
)
if (hasEmptyPrice) {
ElMessage.warning('供应价格为必填项,且必须大于0')
return
}
ElMessage({
message: '保存成功',
type: 'success',
})
supplierTableData.value.forEach((el) => {
if (el.id === currentGoods.value.id) {
el.currencyCode = currentGoods.value.currencyCode
el.currencyName = currentGoods.value.currencyName
el.supplierPriceItemList = [...pricetableData.value]
}
})
console.log('supplierTableData', supplierTableData.value)
cancelPiceFn()
}
/** /**
* @description: 检查数据 * @description: 检查数据
*/ */
async function checkData() { async function checkData(): Promise<{
const [isValid, postData] = await Promise.all([ isValid: boolean
new Promise<boolean>((resolve) => { postData: IsupplierType
editFormRef.value }> {
?.validate() try {
.then(() => resolve(true)) // 1. 表单验证
.catch((err) => { await editFormRef.value?.validate()
resolve(false)
console.log(err) // 2. 准备数据
}) const postData = { ...editForm.value }
}),
new Promise<AddDeclarationRuleObj>((resolve) => { const hasEmptyPriceItemList = supplierTableData.value?.some(
const params = { ...editForm.value } (product) => !product.supplierPriceItemList,
params.logisticsWay = params.logisticsWayId?.join(',') )
resolve(params)
}), if (hasEmptyPriceItemList) {
]) ElMessage({
return { isValid, postData } message: '请填写所有供应价格',
type: 'warning',
})
return { isValid: false, postData }
}
// 检查所有供应价格是否已填写
const hasEmptyPrice = supplierTableData.value?.some((product) =>
product.supplierPriceItemList?.some(
(item) => !item.supplyPrice && item.supplyPrice !== 0,
),
)
console.log(656, hasEmptyPrice)
// return
if (hasEmptyPrice) {
ElMessage({
message: '请填写所有供应价格',
type: 'warning',
})
return { isValid: false, postData }
}
if (supplierTableData.value?.length) {
postData.supplierProductInfoList = [...supplierTableData.value].map(
(el) => {
el.createTime && delete el.createTime
el.updateTime && delete el.updateTime
return {
...el,
}
},
)
}
// 所有验证通过
return { isValid: true, postData }
} catch (error) {
console.error('表单验证失败:', error)
// 表单验证失败时,仍然返回数据
const postData = { ...editForm.value }
return { isValid: false, postData }
}
} }
/** /**
...@@ -529,13 +718,13 @@ const saveSupplierFn = debounce(async () => { ...@@ -529,13 +718,13 @@ const saveSupplierFn = debounce(async () => {
if (isValid) { if (isValid) {
try { try {
if (!postData.id) { if (!postData.id) {
// await addLogisticsCustomsRule({ await addSupplierApi({
// ...postData, ...postData,
// }) })
} else { } else {
// await updateLogisticsCustomsRule({ await updateSupplierApi({
// ...postData, ...postData,
// }) })
} }
ElMessage({ ElMessage({
message: postData.id ? '更新成功' : '新增成功', message: postData.id ? '更新成功' : '新增成功',
...@@ -554,7 +743,6 @@ const saveSupplierFn = debounce(async () => { ...@@ -554,7 +743,6 @@ const saveSupplierFn = debounce(async () => {
* @description: 获取选中数据 * @description: 获取选中数据
*/ */
function handleCheckboxRecords(value: never[]) { function handleCheckboxRecords(value: never[]) {
console.log(351, value)
selection.value = value selection.value = value
} }
...@@ -598,28 +786,312 @@ async function deleteFn() { ...@@ -598,28 +786,312 @@ async function deleteFn() {
*/ */
async function showDialog() { async function showDialog() {
dialogVisible.value = true dialogVisible.value = true
supplierTableData.value = []
supplierSeletctions.value = []
} }
/** /**
* @description: 编辑供应商 * @description: 编辑供应商
*/ */
async function editSupplier() { async function editSupplier(id: string | number) {
dialogVisible.value = true dialogVisible.value = true
editLoading.value = true
supplierTableData.value = []
supplierSeletctions.value = []
try {
const { data } = await getSupplierDetailApi(id)
editForm.value = data
supplierTableData.value = cloneDeep(
editForm.value.supplierProductInfoList || [],
).map((product) => {
const customMap = new Map(
product.customProductInfo?.customProductItemList?.map((item) => [
item.id,
item,
]) || [],
)
product.supplierPriceItemList?.forEach((priceItem) => {
const customItem = customMap.get(priceItem.productItemId)
if (customItem) {
priceItem.propertyCode1 = customItem.propertyCode1
priceItem.propertyCode2 = customItem.propertyCode2
}
})
return product
})
console.log(728, editForm.value)
} catch (error) {
console.log(error)
} finally {
editLoading.value = false
}
} }
const goodsLoading = ref(false) const goodsLoading = ref(false)
function searchSupplyGoodsFn() { const isShowSearch = ref(false)
goodsLoading.value = true const supplierSeletctions = ref<IgoodsType[]>([])
console.log(604, searchSupplierGoods.value) async function searchSupplyGoodsFn() {
if (!searchSupplierGoods.value) {
ElMessage.warning('请输入SPU')
isShowSearch.value = true
return
} else {
isShowSearch.value = false
goodsLoading.value = true
try {
const { data } = await getProductInfoBySpuApi(searchSupplierGoods.value)
goodsTableData.value = [data]
console.log(604, data)
} catch (error) {
console.log(error)
} finally {
goodsLoading.value = false
}
}
}
function deleteSupplyGoodsFn() {
if (!supplierSeletctions.value.length) {
return ElMessage.warning(`请勾选商品`)
}
const deleteIds = supplierSeletctions.value.map((el) => el.productSpu)
supplierTableData.value = supplierTableData.value.filter(
(el) => !deleteIds.includes(el.productSpu),
)
}
function getSupplierItem(v: IgoodsType[]) {
supplierSeletctions.value = v
}
const priceLoading = ref(false)
const currencyOptions = ref<IcurrencyCode[]>([])
const colorList = ref<IcolorType[]>([])
const showColorList = ref<IcolorType[]>([])
const sizeList = ref<IsizeType[]>([])
const showSizeList = ref<IsizeType[]>([])
const currentGoods = ref()
async function addPice(product: IgoodsType) {
supplierPirce.value = ''
colorIndex.value = null
sizeIndex.value = null
priceDialogVisible.value = true
priceLoading.value = true
currentGoods.value = cloneDeep(product)
console.log('product', currentGoods.value)
const tempArr = product.supplierPriceItemList || product.customProductItemList
pricetableData.value = cloneDeep(
tempArr?.map((el) => {
return {
...el,
productItemImage: el.productItemImage || el.image,
productItemSku: el.productItemSku || el.sku,
productItemId: el.id || '',
}
}) || [],
)
try {
const [currencyResponse, propertyResponse] = await Promise.all([
getBaseCurrencyInfoApi(),
getPropertyByCateIdApi(
(product.categoryId || product.customProductInfo?.categoryId) as string,
),
])
currencyOptions.value = currencyResponse.data || []
const propertyData: IPropertyResponseItem[] = propertyResponse.data || []
colorList.value = propertyData.find((el) => el.id == 1)?.valueList || []
sizeList.value = propertyData.find((el) => el.id == 2)?.valueList || []
if (!currentGoods.value.propertyList?.length) {
currentGoods.value.propertyList = product.customProductInfo?.propertyList
}
// 创建属性映射
const propertyMap = createPropertyMap(currentGoods.value.propertyList || [])
console.log(758, propertyMap)
// 过滤显示的颜色和尺寸
showColorList.value = filterPropertyList(
colorList.value,
propertyMap.get(1) || [],
)
showSizeList.value = filterPropertyList(
sizeList.value,
propertyMap.get(2) || [],
)
} catch (error) {
console.log(error)
} finally {
priceLoading.value = false
}
}
// 辅助函数:创建属性映射
function createPropertyMap(propertyList: any[]): Map<number, number[]> {
const map = new Map<number, number[]>()
propertyList.forEach((item) => {
if (item.propertyId && item.valueId) {
if (!map.has(item.propertyId)) {
map.set(item.propertyId, [])
}
map.get(item.propertyId)!.push(item.valueId)
}
})
return map
} }
function addPice() { // 辅助函数:过滤属性列表
piceDialogVisible.value = true function filterPropertyList<T extends { id?: number | string }>(
sourceList: T[],
targetIds: number[],
): T[] {
return sourceList.filter((item) => {
const itemId = typeof item.id === 'string' ? parseInt(item.id) : item.id
return itemId !== undefined && targetIds.includes(itemId as number)
})
} }
function addGoods(data: any) { function addGoods(data: IgoodsType) {
// 使用 Set 进行快速查找(性能更好)
data.productImage = data.productImage || data.imgUrl || ''
data.productSpu = data.productSpu || data.sku || ''
data.productName = data.productName || data.name || ''
data.currencyCode = ''
data.currencyName = ''
data.productId = data.id || undefined
const idSet = new Set(supplierTableData.value.map((item) => item.productSpu))
if (idSet.has(data.productSpu)) {
ElMessage.warning(`商品已存在,请勿重复添加`)
return
}
supplierTableData.value.push(data) supplierTableData.value.push(data)
ElMessage.success('商品添加成功')
}
const selectPirceList = ref<Iprice[]>([])
const supplierPirce = ref('')
function selectPirce(value: never[]) {
console.log(351, value)
selectPirceList.value = value
}
function updatePrices() {
if (!supplierPirce.value) {
ElMessage.warning(`请输入供应价格`)
return
}
if (selectPirceList.value.length) {
selectPirceList.value.forEach((el) => {
el.supplyPrice = supplierPirce.value
})
} else {
ElMessage.warning(`请先勾选需要修改价格的商品`)
}
}
const colorIndex = ref()
const sizeIndex = ref()
const tableRef = ref()
const optionSelection = (row: IcolorType, type: 'color' | 'size') => {
// 更新选中索引
updateSelectionIndex(row, type)
// 更新表格选中状态
updateTableSelection()
}
// 更新选中索引的函数
const updateSelectionIndex = (row: IcolorType, type: 'color' | 'size') => {
if (type === 'color') {
colorIndex.value = colorIndex.value === row.code ? null : row.code
} else if (type === 'size') {
sizeIndex.value = sizeIndex.value === row.code ? null : row.code
}
}
// 更新表格选中状态的函数
const updateTableSelection = () => {
pricetableData.value.forEach((item) => {
tableRef.value.setCheckboxRow(item, false)
})
const rowsToSelect = getRowsToSelect()
rowsToSelect.forEach((item) => {
tableRef.value.setCheckboxRow(item, true)
})
console.log('rowsToSelect', rowsToSelect)
selection.value = [...(rowsToSelect || [])]
}
// 获取应该选中的行
const getRowsToSelect = () => {
return pricetableData.value.filter((item) => {
if (colorIndex.value && sizeIndex.value) {
return (
item.propertyCode1 === colorIndex.value &&
item.propertyCode2 === sizeIndex.value
)
}
if (colorIndex.value) {
return item.propertyCode1 === colorIndex.value
}
if (sizeIndex.value) {
return item.propertyCode2 === sizeIndex.value
}
return false
})
}
function getSupplyPrice(row: IgoodsType) {
let range = ''
const arr = row.supplierPriceItemList
if (arr?.length) {
const prices = arr
.map((item) => {
// 尝试转换为数字
const price = parseFloat(item.supplyPrice as string)
return isNaN(price) ? null : price
})
.filter((price) => price !== null) as number[]
if (prices.length === 0) {
range = '' // 或者根据需求返回默认值
}
// 计算最小值和最大值
const minPrice = Math.min(...prices)
const maxPrice = Math.max(...prices)
// 判断是否需要范围格式
if (minPrice === maxPrice) {
// 所有价格相同或只有一个价格
range = minPrice.toString()
} else {
// 价格有范围
range = `${minPrice}-${maxPrice}`
}
} else {
range = ''
}
row.supplyPriceRange = range as string
return range
} }
function updatePrices() {}
watch(
() => currentGoods.value,
(value) => {
console.log(946, value)
},
{ deep: true },
)
onMounted(() => { onMounted(() => {
getAllList() getAllList()
...@@ -643,4 +1115,10 @@ async function getAllList() {} ...@@ -643,4 +1115,10 @@ async function getAllList() {}
.dialog-footer { .dialog-footer {
text-align: center; text-align: center;
} }
.tabBox {
cursor: pointer;
}
.active {
border: 2px solid rgb(0, 140, 255);
}
</style> </style>
export interface DeclarationRuleList {
countries: string
createTime: string
currency: string
defaulted: string
fixedValue: string
fixedWeight: string
id: number
name: string
orderPercent: number
remark: string
shops: string
type: number
valueUp: number
wayIds: string
wayNames: string
weightPercent: number
weightUp: number
}
export interface AddDeclarationRuleObj {
currency?: string
fixedValue?: string
fixedWeight?: string
id?: number
limitAmountType?: string
limitWeightType?: string
name?: string
orderPercent?: number | string | null
remark?: string
shops?: string
type?: number
valueUp?: number | string | null
weightPercent?: number | string | null
weightUp?: number | string | null
logisticsWay?: string
logisticsWayId?: (string | number)[]
}
export interface IListPage {
pageSize: number | string
currentPage: number | string
}
export interface IgoodsType {
imgUrl?: string
productImage?: string
sku?: string
productSpu?: string
productNo?: string
id?: string
productId?: string
categoryId?: string
currencyCode?: string
currencyName?: string
name?: string
productName?: string
createTime?: string
updateTime?: string
customProductItemList?: Iprice[]
supplierPriceItemList?: Iprice[]
customProductInfo?: IgoodsType
propertyList?: []
supplyPriceRange?: string
}
export interface IsupplierType {
supplierName?: string
contacts?: string
contactsNumber?: string
address?: string
id?: string
supplierProductInfoList?: IgoodsType[]
remark?: string
}
export interface IcurrencyCode {
currencyName?: string
currencyCode?: string
id?: string
}
export interface IcolorType {
bgColor?: string
code?: string
fontColor?: string
cnname?: string
enname?: string
id?: string | number
}
export interface IsizeType {
code?: string
cnname?: string
id?: string | number
}
export interface IPropertyResponseItem {
id: string | number
valueList: IcolorType[] | IsizeType[]
}
export interface Iprice {
productItemSku?: string
productItemImage?: string
sku?: string
id?: string
image?: string
supplyPrice?: string | number
propertyCode1?: string
propertyCode2?: string
productItemId?: string
}
export interface IListPage {
pageSize: number | string
currentPage: number | string
}
export interface LogisticsMethod {
id?: number | string
name?: string
warehouseId?: number
warehouseName?: string
companyId?: number
company?: string
serviceCode?: string
siteUrl?: string
status: number | string
platformList: platformObj[]
ruleRef: ruleRefObj
ruleId?: string | number
ruleList?: ruleRefObj[]
uinuinWarehouseId?: number | string | null
companyWarehouseCode?: number | string | null
}
export interface LogisticsMethodList {
name?: string
status?: number | string
serviceCode?: number | string
pageSize: number | string
currentPage: number | string
}
export interface UpdateLogisticsMethodStatus {
id?: number | string
status?: number | string
}
export interface LogisticsResponse {
code: number
data: {
total: number
size: number
current: number
records: LogisticsMethod[]
}
message: string
}
export interface platformObj {
platform: string
logisticsName: string | number
showPlatform: (string | number)[]
}
interface ruleRefObj {
ruleId: string | number
ruleName: string | number
}
export interface LogisticsTrackingTree {
name: string
status: number
num: number
}
export interface LogisticsTrackingParams {
trackNumber?: number | string
shopNumber?: string | number
trackingStatus?: number
}
export interface LogisticsPartitionObj {
zoneName?: string
logistics?: string
codePrefix?: string
logisticsId?: string
}
export interface LogisticsQuotation {
factoryId?: number
id?: number
logistics?: string
logisticsId?: number
rate?: number
rateG?: number
rateKg?: number
rateType?: string
seq?: number
unit?: string
zone1?: string
zone2?: string
zone3?: string
zone4?: string
zone5?: string
zone6?: string
zone7?: string
zone8?: string
zone9?: string
logisticsQuotationList?: LogisticsQuotation[]
}
export interface ShippingAddressObj {
addressLine1?: string
addressLine2?: string
addressLine3?: string
city?: string
cityCode?: string
countryCode?: string
countryName?: string
createTime?: string
cspAccount?: string
district?: string
districtCode?: string
factoryId?: number
id?: number
phoneNumber?: string
postalCode?: string
rfcTaxId?: string
shipperName?: string
stateProvince?: string
stateProvinceAbbr?: string
swDefault?: boolean
updateTime?: string
checked?: boolean
}
export interface ICountryObj {
aeCountryCode: string
continentCode: string
countryCode: string
id: number
nameCn: string
nameEn: string
needProviceAbbr: number
}
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