Commit edb4d03b by linjinhong

添加物流方式和发货地址页面

parent 80ed9f46
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
"rules": { "rules": {
"vue/require-default-prop": "off", "vue/require-default-prop": "off",
"vue/multi-word-component-names": "off", "vue/multi-word-component-names": "off",
"no-console": "warn","no-unused-vars": "off" "no-console": "off","no-unused-vars": "off",
}, },
"overrides": [{ "files": "*.vue", "rules": { "no-undef": "off" } }] "overrides": [{ "files": "*.vue", "rules": { "no-undef": "off" } }]
} }
...@@ -9,6 +9,8 @@ declare global { ...@@ -9,6 +9,8 @@ declare global {
const ElLoading: typeof import('element-plus/es')['ElLoading'] const ElLoading: typeof import('element-plus/es')['ElLoading']
const ElMessage: typeof import('element-plus/es')['ElMessage'] const ElMessage: typeof import('element-plus/es')['ElMessage']
const ElMessageBox: typeof import('element-plus/es')['ElMessageBox'] const ElMessageBox: typeof import('element-plus/es')['ElMessageBox']
const ElOption: typeof import('element-plus/es')['ElOption']
const ElSelect: typeof import('element-plus/es')['ElSelect']
const acceptHMRUpdate: typeof import('pinia')['acceptHMRUpdate'] const acceptHMRUpdate: typeof import('pinia')['acceptHMRUpdate']
const computed: typeof import('vue')['computed'] const computed: typeof import('vue')['computed']
const createApp: typeof import('vue')['createApp'] const createApp: typeof import('vue')['createApp']
......
...@@ -28,6 +28,7 @@ declare module 'vue' { ...@@ -28,6 +28,7 @@ declare module 'vue' {
ElFormItem: typeof import('element-plus/es')['ElFormItem'] ElFormItem: typeof import('element-plus/es')['ElFormItem']
ElIcon: typeof import('element-plus/es')['ElIcon'] ElIcon: typeof import('element-plus/es')['ElIcon']
ElInput: typeof import('element-plus/es')['ElInput'] ElInput: typeof import('element-plus/es')['ElInput']
ElLink: typeof import('element-plus/es')['ElLink']
ElMenu: typeof import('element-plus/es')['ElMenu'] ElMenu: typeof import('element-plus/es')['ElMenu']
ElMenuItem: typeof import('element-plus/es')['ElMenuItem'] ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
ElOption: typeof import('element-plus/es')['ElOption'] ElOption: typeof import('element-plus/es')['ElOption']
...@@ -56,10 +57,13 @@ declare module 'vue' { ...@@ -56,10 +57,13 @@ declare module 'vue' {
Select: typeof import('./src/components/Form.vue/Select.vue')['default'] Select: typeof import('./src/components/Form.vue/Select.vue')['default']
ShipmentOrderDetail: typeof import('./src/components/ShipmentOrderDetail.vue')['default'] ShipmentOrderDetail: typeof import('./src/components/ShipmentOrderDetail.vue')['default']
SplitDiv: typeof import('./src/components/splitDiv/splitDiv.vue')['default'] SplitDiv: typeof import('./src/components/splitDiv/splitDiv.vue')['default']
'Switch ': typeof import('./src/components/Form.vue/Switch .vue')['default']
Swtich: typeof import('./src/components/Form.vue/Swtich.vue')['default']
TableList: typeof import('./src/components/TableList.vue')['default'] TableList: typeof import('./src/components/TableList.vue')['default']
TableRightMenu: typeof import('./src/components/TableRightMenu.vue')['default'] TableRightMenu: typeof import('./src/components/TableRightMenu.vue')['default']
TableView: typeof import('./src/components/TableView.vue')['default'] TableView: typeof import('./src/components/TableView.vue')['default']
UploadImage: typeof import('./src/components/UploadImage.vue')['default'] UploadImage: typeof import('./src/components/UploadImage.vue')['default']
VxeTable: typeof import('./src/components/VxeTable.vue')['default']
WangEditor: typeof import('./src/components/WangEditor.vue')['default'] WangEditor: typeof import('./src/components/WangEditor.vue')['default']
} }
export interface ComponentCustomProperties { export interface ComponentCustomProperties {
......
...@@ -21,7 +21,8 @@ ...@@ -21,7 +21,8 @@
"vue": "^3.4.19", "vue": "^3.4.19",
"vue-dompurify-html": "^5.1.0", "vue-dompurify-html": "^5.1.0",
"vue-router": "^4.3.0", "vue-router": "^4.3.0",
"vue-tsc": "^2.1.10" "vue-tsc": "^2.1.10",
"vxe-table": "^4.13.31"
}, },
"devDependencies": { "devDependencies": {
"@types/splitpanes": "^2.2.6", "@types/splitpanes": "^2.2.6",
...@@ -2826,6 +2827,19 @@ ...@@ -2826,6 +2827,19 @@
"url": "https://github.com/sponsors/antfu" "url": "https://github.com/sponsors/antfu"
} }
}, },
"node_modules/@vxe-ui/core": {
"version": "4.1.4",
"resolved": "https://registry.npmmirror.com/@vxe-ui/core/-/core-4.1.4.tgz",
"integrity": "sha512-XjT7gyKwiThAQ06rwmsZA+WEvr6eX2XuaMPzArPOS41gTasgvbVT777y3QZUvzkjuy+JnGH9zY98cydEF5W0ng==",
"license": "MIT",
"dependencies": {
"dom-zindex": "^1.0.6",
"xe-utils": "^3.7.4"
},
"peerDependencies": {
"vue": "^3.2.0"
}
},
"node_modules/@wangeditor/basic-modules": { "node_modules/@wangeditor/basic-modules": {
"version": "1.1.7", "version": "1.1.7",
"resolved": "https://registry.npmmirror.com/@wangeditor/basic-modules/-/basic-modules-1.1.7.tgz", "resolved": "https://registry.npmmirror.com/@wangeditor/basic-modules/-/basic-modules-1.1.7.tgz",
...@@ -3477,6 +3491,12 @@ ...@@ -3477,6 +3491,12 @@
"node": ">=6.0.0" "node": ">=6.0.0"
} }
}, },
"node_modules/dom-zindex": {
"version": "1.0.6",
"resolved": "https://registry.npmmirror.com/dom-zindex/-/dom-zindex-1.0.6.tgz",
"integrity": "sha512-FKWIhiU96bi3xpP9ewRMgANsoVmMUBnMnmpCT6dPMZOunVYJQmJhSRruoI0XSPoHeIif3kyEuiHbFrOJwEJaEA==",
"license": "MIT"
},
"node_modules/dom7": { "node_modules/dom7": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmmirror.com/dom7/-/dom7-3.0.0.tgz", "resolved": "https://registry.npmmirror.com/dom7/-/dom7-3.0.0.tgz",
...@@ -6342,6 +6362,24 @@ ...@@ -6342,6 +6362,24 @@
"typescript": ">=5.0.0" "typescript": ">=5.0.0"
} }
}, },
"node_modules/vxe-pc-ui": {
"version": "4.6.12",
"resolved": "https://registry.npmmirror.com/vxe-pc-ui/-/vxe-pc-ui-4.6.12.tgz",
"integrity": "sha512-57sRB/ksP8ip4l0hPcph5qXt/qGlrCjO2/Y6ZL4sHkGdb+CBWgbzvUPcq3GYgSSPdZg+Ae++UcGqgRGMZss+RQ==",
"license": "MIT",
"dependencies": {
"@vxe-ui/core": "^4.1.4"
}
},
"node_modules/vxe-table": {
"version": "4.13.31",
"resolved": "https://registry.npmmirror.com/vxe-table/-/vxe-table-4.13.31.tgz",
"integrity": "sha512-ibSM7jXYwJyY+eqXoRy/yXEVLENGFzL96cOEwtnFjBYbbaZV6/ptlM3tsyewGFBCUo5AtIyM+98hswxfjyXxMA==",
"license": "MIT",
"dependencies": {
"vxe-pc-ui": "^4.6.0"
}
},
"node_modules/webpack-virtual-modules": { "node_modules/webpack-virtual-modules": {
"version": "0.6.2", "version": "0.6.2",
"resolved": "https://registry.npmmirror.com/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", "resolved": "https://registry.npmmirror.com/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz",
...@@ -6388,6 +6426,12 @@ ...@@ -6388,6 +6426,12 @@
"dev": true, "dev": true,
"license": "ISC" "license": "ISC"
}, },
"node_modules/xe-utils": {
"version": "3.7.4",
"resolved": "https://registry.npmmirror.com/xe-utils/-/xe-utils-3.7.4.tgz",
"integrity": "sha512-9yuCHLOU+og4OEkPWWtzrYk1Zt1hgN66U/NCJ0+vYJSx1MplBtoQRz8aEA+2RmCr3leLru98vQxNpw/vJsu/sg==",
"license": "MIT"
},
"node_modules/xml-name-validator": { "node_modules/xml-name-validator": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmmirror.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz", "resolved": "https://registry.npmmirror.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz",
......
...@@ -23,7 +23,8 @@ ...@@ -23,7 +23,8 @@
"vue": "^3.4.19", "vue": "^3.4.19",
"vue-dompurify-html": "^5.1.0", "vue-dompurify-html": "^5.1.0",
"vue-router": "^4.3.0", "vue-router": "^4.3.0",
"vue-tsc": "^2.1.10" "vue-tsc": "^2.1.10",
"vxe-table": "^4.13.31"
}, },
"devDependencies": { "devDependencies": {
"@types/splitpanes": "^2.2.6", "@types/splitpanes": "^2.2.6",
......
import { BaseRespData } from '@/types/api'
import axios from './axios'
import {
LogisticsMethod,
LogisticsMethodList,
UpdateLogisticsMethodStatus,
} from '@/types/api/logistics'
interface Ikey {
[key: string]: unknown
}
interface ILogisticsParams {
logType: 'logistics_way' | 'logistics_address'
relaId: number | string
}
//获取日志
export function getLogisticsLog(params: ILogisticsParams) {
return axios.get<never, BaseRespData<never>>('logisticsLog/getList', {
params,
})
}
/**
* @description 物流方式
*/
//列表
export function getLogisticsWayList(data: LogisticsMethodList) {
return axios.post<never, BaseRespData<never>>('/logisticsWay/list_page', data)
}
//新增
export function addLogisticsWay(data: LogisticsMethod) {
return axios.post<Ikey, BaseRespData<never>>('/logisticsWay/add', data)
}
//修改
export function updateLogisticsWay(data: LogisticsMethod) {
return axios.post<Ikey, BaseRespData<never>>('/logisticsWay/update', data)
}
//更新状态
export function updateStatusLogisticsWay(params: UpdateLogisticsMethodStatus) {
return axios.get<Ikey, BaseRespData<never>>(
'/logisticsWay/updateStatusById',
{
params,
},
)
}
// 删除
export function deleteLogisticsWay(ids: string) {
return axios.get<Ikey, BaseRespData<never>>('/logisticsWay/delete', {
params: { ids: ids },
})
}
//获取申报规则列表
export function getRuleList() {
return axios.get<never, BaseRespData<never>>(
'/logisticsCustomsRule/getRuleList',
)
}
//获取仓库列表
export function getWarehouseList() {
return axios.get<never, BaseRespData<never>>('/factoryWarehouseInfo/getAll')
}
//获取平台列表
export function getPlatformList() {
return axios.get<never, BaseRespData<never>>('/logisticsWay/platform')
}
/**
* @description 发货地址
*/
//列表
export function getAddressByIdList(params: any) {
return axios.get<never, BaseRespData<never>>('/logisticsAddress/list_page', {
params,
})
}
//根据id查询地址
export function getAddressById(params: any) {
return axios.get<never, BaseRespData<never>>(
'/logisticsAddress/getAddressById',
{
params,
},
)
}
//新增
export function addAddress(params: any) {
return axios.post<never, BaseRespData<never>>(
'/logisticsAddress/addAddress',
params,
)
}
//修改
export function updateAddress(params: any) {
return axios.post<never, BaseRespData<never>>(
'/logisticsAddress/updateAddress',
params,
)
}
//删除
export function deleteAddressByIds(params: any) {
return axios.get<never, BaseRespData<never>>(
'/logisticsAddress/deleteAddressByIds',
{
params,
},
)
}
/**
* @description 物流报价
*/
//物流方式列表
export function getlogisticsWayAllList() {
return axios.get<never, BaseRespData<never>>('/logisticsWay/all_list', {})
}
//物流报价列表
export function getlogisticsQuotationList() {
return axios.get<never, BaseRespData<never>>(
'/logistics/logisticsQuotation/list_page',
{},
)
}
//新增
export function addLogisticsQuotation(params: any) {
return axios.get<never, BaseRespData<never>>(
'/logistics/logisticsQuotation/add',
params,
)
}
//修改
export function updateLogisticsQuotation(params: any) {
return axios.post<never, BaseRespData<never>>(
'/logistics/logisticsQuotation/update',
params,
)
}
//删除
export function deleteLogisticsQuotation(params: any) {
return axios.post<never, BaseRespData<never>>(
'/logistics/logisticsQuotation/delete',
params,
)
}
//导入
export function importLogisticsQuotation(params: any) {
return axios.get<never, BaseRespData<never>>(
'/logistics/logisticsQuotation/import',
{ params },
)
}
//下载模版
export function exportLogisticsQuotationTemplate(params: any) {
return axios.get<never, BaseRespData<never>>(
'/logistics/logisticsQuotation/exportExcel',
{ params },
)
}
//根据ID查对象
export function getLogisticsQuotationByID(params: any) {
return axios.get<never, BaseRespData<never>>(
'/logistics/logisticsQuotation/get',
{ params },
)
}
import { defineComponent, PropType, ref, computed, h } from 'vue'
import type { Component } from 'vue'
import { ElInput, ElForm, ElFormItem } from 'element-plus'
import type { FormInstance } from 'element-plus'
import AmountInput from './Form.vue/AmountInput.vue' // 金额输入框
import DateRangePicker from './Form.vue/DateRangePicker.vue' // 时间范围选择器
import DatePicker from './Form.vue/DatePicker.vue' // 单个日期选择器
import Select from './Form.vue/Select.vue' // 普通下拉选择框
import Switch from './Form.vue/Switch .vue'
import './Form.vue/form.scss'
import type { FormItemRule } from 'element-plus'
// 定义表单项配置接口
export interface IFormConfig {
title?: string
prop?: string
label?: string
type?: string
btn?: JSX.Element | (() => JSX.Element)
attrs?: Record<string, unknown>
rules?: FormItemRule | FormItemRule[]
render?: (
item?: IFormConfig,
formData?: Record<string, any>,
index?: number,
) => JSX.Element
}
// 定义组件类型
type ComponentType = Component
// 支持的组件映射
const componentConfig: Record<string, ComponentType> = {
input: ElInput,
amountInput: AmountInput,
dateRangePicker: DateRangePicker,
datePicker: DatePicker,
select: Select,
switch: Switch,
// 这里可以根据需要添加更多组件类型
}
export default defineComponent({
name: 'CustomizeForm',
props: {
config: {
type: Array as PropType<IFormConfig[]>,
default: () => [],
},
labelWidth: {
type: [String, Number],
default: 100,
},
formItemWidth: {
type: [String, Number],
default: '50%',
},
modelValue: {
type: Object as PropType<Record<string, unknown>>,
default: () => ({}),
},
size: {
type: String as PropType<'large' | 'default' | 'small'>,
default: 'default',
},
inline: {
type: Boolean,
default: false,
},
labelPosition: {
type: String as PropType<'left' | 'right' | 'top'>,
default: 'right',
},
},
emits: ['update:modelValue', 'validate'],
setup(props, { emit, attrs }) {
const formRef = ref<FormInstance>()
const formData = ref<Record<string, unknown>>(props.modelValue)
// 监听表单数据变化
watch(
() => formData.value,
(val) => {
emit('update:modelValue', val)
},
{ deep: true },
)
// 监听外部数据变化
watch(
() => props.modelValue,
(val) => {
// console.log(84, val)
formData.value = val
},
{ immediate: true, deep: true },
)
// 获取form表单的属性
const getFormAttrs = computed(() => ({
...attrs,
style: { width: '100%' },
}))
// 获取组件项的属性
const getComponentAttrs = (item: IFormConfig) => {
return item.attrs ? { ...item.attrs } : {}
}
// 表单验证方法
const validate = async () => {
if (!formRef.value) return false
return await formRef.value.validate()
}
// 重置表单
const resetFields = async () => {
await formRef.value?.resetFields()
}
return {
formRef,
formData,
validate,
resetFields,
getFormAttrs,
getComponentAttrs,
}
},
render() {
return (
<ElForm
ref="formRef"
model={this.formData}
labelWidth={this.labelWidth}
size={this.size}
inline={this.inline}
labelPosition={this.labelPosition}
{...this.getFormAttrs}
style={{ display: 'flex', flexWrap: 'wrap' }}
class="customForm"
>
{this.config.map((item, index) =>
item.title ? (
<div
style={{
display: 'flex',
width: '100%',
flexWrap: 'wrap',
}}
>
<div
style={{
width: '100%',
textAlign: 'center',
fontWeight: 700,
marginBottom: '20px',
fontSize: '16px',
}}
>
{item.title}
</div>
{item.render && item.render(item, this.formData, this)}
</div>
) : (
<ElFormItem
key={index}
prop={item.prop}
label={item.label}
style={{
width: item.attrs?.width || this.formItemWidth || '50%',
}}
rules={item.rules}
>
{item.render
? item.render(item)
: h(componentConfig[item.type] || ElInput, {
modelValue: this.formData[item.prop],
'onUpdate:modelValue': (value: unknown) => {
this.formData[item.prop] = value
},
...this.getComponentAttrs(item),
})}
{item.btn && item.btn()}
</ElFormItem>
),
)}
</ElForm>
)
},
})
...@@ -32,16 +32,17 @@ const props = withDefaults( ...@@ -32,16 +32,17 @@ const props = withDefaults(
}, },
) )
const emits = defineEmits<{ const emits = defineEmits<{
(e: 'update:modelValue', data: string | number) (e: 'update:modelValue', data: string | number | null): void
(e: 'input', data: string | number) (e: 'input', data: string | number): void
}>() }>()
const val = ref<string | number>(props.modelValue) const val = ref<string | number | null>(props.modelValue)
const iptFn = (e) => { const iptFn = (e) => {
const bool = isNumFloat(e) // 只允许输入数字和最多一个小数点 const bool = isNumFloat(e) // 只允许输入数字和最多一个小数点
if (e === '') { if (e === '') {
val.value = null val.value = null
emits('input', '')
} else { } else {
if (bool) { if (bool) {
const parts = e.split('.') // 将输入的数字按小数点分割成整数部分和小数部分 const parts = e.split('.') // 将输入的数字按小数点分割成整数部分和小数部分
...@@ -63,7 +64,7 @@ const iptFn = (e) => { ...@@ -63,7 +64,7 @@ const iptFn = (e) => {
} }
} }
} }
emits('input', val.value) emits('input', val.value || '')
} }
watch( watch(
......
...@@ -27,7 +27,7 @@ const props = withDefaults( ...@@ -27,7 +27,7 @@ const props = withDefaults(
isNeedTime: false, isNeedTime: false,
isrowDate: false, isrowDate: false,
rowData: null, rowData: null,
newDisableDate: null, newDisableDate: undefined,
}, },
) )
// 用于存储动态的禁用日期逻辑 // 用于存储动态的禁用日期逻辑
......
<template>
<el-switch
v-model="model"
class="ml-2"
:style="{
'--el-switch-on-color': props.activeColor,
'--el-switch-off-color': props.inactiveColor,
}"
v-bind="$attrs"
/>
</template>
<script lang="ts" setup>
const model = ref()
const props = withDefaults(
defineProps<{
activeColor?: string
inactiveColor?: string
modelValue?: any
}>(),
{
activeColor: '#13ce66',
inactiveColor: ' #ff4949',
modelValue: null,
},
)
watch(
() => props.modelValue,
(val) => {
console.log(32, val)
model.value = val
},
{ immediate: true },
)
watch(model, (val) => {
emits('update:modelValue', val)
})
const emits = defineEmits<{
(e: 'update:modelValue', data: string | number | undefined)
}>()
</script>
<style lang="scss" scoped></style>
.customForm {
.el-form-item {
.el-form-item__content {
flex-wrap: nowrap;
}
}
}
\ No newline at end of file
import { defineComponent, PropType, ref, computed, reactive, watch, h } from 'vue' import {
defineComponent,
PropType,
ref,
computed,
reactive,
watch,
h,
} from 'vue'
import type { Component } from 'vue' import type { Component } from 'vue'
import AmountInput from './Form.vue/AmountInput.vue' // 金额输入框 import AmountInput from './Form.vue/AmountInput.vue' // 金额输入框
...@@ -22,8 +30,6 @@ const componentDataTypeMap = new Map([ ...@@ -22,8 +30,6 @@ const componentDataTypeMap = new Map([
[EComponenetType.select, EDataType.undefined], [EComponenetType.select, EDataType.undefined],
]) ])
type ComponentType = Component type ComponentType = Component
const componentConfig: Record<EComponenetType, ComponentType> = { const componentConfig: Record<EComponenetType, ComponentType> = {
...@@ -39,14 +45,14 @@ const styles = { ...@@ -39,14 +45,14 @@ const styles = {
position: 'relative', position: 'relative',
}, },
searchFormHeader: { searchFormHeader: {
display: 'flex', marginBottom: '10px' display: 'flex',
marginBottom: '10px',
}, },
wrapper: { wrapper: {
display: 'flex', display: 'flex',
flexDirection: 'row', flexDirection: 'row',
alignItems: 'flex-start', alignItems: 'flex-start',
justifyContent: 'space-between', justifyContent: 'space-between',
}, },
} as const } as const
...@@ -55,62 +61,54 @@ export default defineComponent({ ...@@ -55,62 +61,54 @@ export default defineComponent({
props: { props: {
config: { config: {
type: Array as PropType<ISeachFormConfig[]>, type: Array as PropType<ISeachFormConfig[]>,
default: () => [] default: () => [],
}, },
labelWidth: { labelWidth: {
type: [String, Number], type: [String, Number],
default: 58 default: 58,
}, },
modelValue: { modelValue: {
type: Object as PropType<ISearchForm>, type: Object as PropType<ISearchForm>,
default: () => ({}) default: () => ({}),
}, },
getTablelist: { getTablelist: {
type: Function as PropType<(...args: unknown[]) => Promise<unknown>>, type: Function as PropType<(...args: unknown[]) => Promise<unknown>>,
default: null default: null,
}, },
size: { size: {
type: String as PropType<'large' | 'default' | 'small'>, type: String as PropType<'large' | 'default' | 'small'>,
default: 'default' default: 'default',
}, },
addPermissionName: { addPermissionName: {
type: [String, Array] as PropType<string | string[]>, type: [String, Array] as PropType<string | string[]>,
default: '' default: '',
}, },
isSearchBtn: { isSearchBtn: {
type: Boolean, type: Boolean,
default: true default: true,
}, },
isResetBtn: { isResetBtn: {
type: Boolean, type: Boolean,
default: true default: true,
}, },
isAddBtn: { isAddBtn: {
type: Boolean, type: Boolean,
default: true default: true,
}, },
isDeleteBtn: { isDeleteBtn: {
type: Boolean, type: Boolean,
default: true default: true,
} },
}, },
emits: [ emits: ['search', 'reset', 'fold', 'add', 'delete', 'update:modelValue'],
'search',
'reset',
'fold',
'headerPrimaryBtn',
'headerDefaultBtn',
'update:modelValue',
'clear',
'update:tableFieldForm',
'getExcelData'
],
setup(props, { emit, slots, attrs }) { setup(props, { emit, slots, attrs }) {
const formRef = ref() const formRef = ref()
const formWrapperRef = ref() const formWrapperRef = ref()
const searchForm = reactive<ISearchForm>(props.modelValue || {}) const searchForm = ref<ISearchForm>(props.modelValue || {})
const selectResult = ref<ISeachFormConfig[]>([]) const selectResult = ref<ISeachFormConfig[]>([])
const componentWidth = computed(() => props.size === 'default' ? 200 : 220) const componentWidth = computed(() =>
props.size === 'default' ? 200 : 220,
)
function initSeachForm() { function initSeachForm() {
props.config.forEach((item, index) => { props.config.forEach((item, index) => {
...@@ -133,12 +131,12 @@ export default defineComponent({ ...@@ -133,12 +131,12 @@ export default defineComponent({
}, },
{ {
immediate: true, immediate: true,
} },
) )
// 获取form表单的属性 // 获取form表单的属性
const getFormAttrs = computed(() => ({ const getFormAttrs = computed(() => ({
...attrs ...attrs,
})) }))
// 获取组件项传进来的attrs // 获取组件项传进来的attrs
...@@ -146,14 +144,6 @@ export default defineComponent({ ...@@ -146,14 +144,6 @@ export default defineComponent({
return item.attrs ? { ...item.attrs } : {} return item.attrs ? { ...item.attrs } : {}
} }
const searchFn = () => {
emit('search', searchForm)
}
const resetFn = () => {
emit('reset', {})
}
const handleSubmit = (e: Event) => { const handleSubmit = (e: Event) => {
e.preventDefault() e.preventDefault()
} }
...@@ -167,14 +157,14 @@ export default defineComponent({ ...@@ -167,14 +157,14 @@ export default defineComponent({
<div style={styles.wrapper} class="mt-15"> <div style={styles.wrapper} class="mt-15">
<ElForm <ElForm
{...getFormAttrs.value} {...getFormAttrs.value}
ref={formRef} ref={el=>formRef.value=el}
class="w-full" class="w-full"
inline inline
model={searchForm} model={searchForm.value}
labelPosition="left" labelPosition="left"
size={props.size} size={props.size}
{...{ {...{
onSubmit: handleSubmit onSubmit: handleSubmit,
}} }}
> >
<div ref={formWrapperRef} class="formItemWrapper w-full"> <div ref={formWrapperRef} class="formItemWrapper w-full">
...@@ -184,20 +174,19 @@ export default defineComponent({ ...@@ -184,20 +174,19 @@ export default defineComponent({
key={index} key={index}
prop={item.prop} prop={item.prop}
label={item.label} label={item.label}
style={{marginBottom:'0px'}} style={{ marginBottom: '0px' }}
> >
{item.render ? ( {item.render
item.render() ? item.render()
) : ( : h(componentConfig[item.type as EComponenetType], {
h(componentConfig[item.type as EComponenetType], { modelValue: searchForm.value[item.prop],
modelValue: searchForm[item.prop], 'onUpdate:modelValue': (value: unknown) => {
'onUpdate:modelValue': (value: unknown) => { searchForm.value[item.prop] = value
searchForm[item.prop] = value },
},
...getComponentAttrs(item), ...getComponentAttrs(item),
style: { width: `${componentWidth.value}px` } style: { width: `${componentWidth.value}px` },
}) })}
)}
</ElFormItem> </ElFormItem>
))} ))}
</div> </div>
...@@ -207,21 +196,28 @@ export default defineComponent({ ...@@ -207,21 +196,28 @@ export default defineComponent({
)} )}
<div style={{ display: 'flex', alignItems: 'center' }}> <div style={{ display: 'flex', alignItems: 'center' }}>
{props.isSearchBtn && ( {props.isSearchBtn && (
<ElButton class="btn" type="primary" onClick={searchFn}> <ElButton
class="btn"
type="primary"
onClick={() => emit('search', searchForm.value)}
>
查询 查询
</ElButton> </ElButton>
)} )}
{props.isResetBtn && ( {props.isResetBtn && (
<ElButton class="btn" onClick={resetFn}> <ElButton
class="btn"
onClick={() => {
formRef.value?.resetFields()
// emit('update:modelValue', {})
emit('reset')
}}
>
重置 重置
</ElButton> </ElButton>
)} )}
{props.isAddBtn && ( {props.isAddBtn && (
<ElButton <ElButton class="btn" type="success" onClick={() => emit('add')}>
class="btn"
type="success"
onClick={() => emit('headerPrimaryBtn')}
>
新增 新增
</ElButton> </ElButton>
)} )}
...@@ -229,7 +225,7 @@ export default defineComponent({ ...@@ -229,7 +225,7 @@ export default defineComponent({
<ElButton <ElButton
class="btn" class="btn"
type="danger" type="danger"
onClick={() => emit('headerDefaultBtn')} onClick={() => emit('delete')}
> >
删除 删除
</ElButton> </ElButton>
...@@ -239,5 +235,5 @@ export default defineComponent({ ...@@ -239,5 +235,5 @@ export default defineComponent({
</div> </div>
</div> </div>
) )
} },
}) })
\ No newline at end of file
<!--
配置说明:
hasHeader 控制是否使用头部额外的区域
插槽 header-content 头部内容区域
插槽 header-btn 头部左侧按钮区域 默认: 新增、导出按钮
新增按钮事件: @headerPrimaryBtn
导出按钮事件:@headerDefaultBtn
搜索事件: @search 接收一个搜索对象
重置事件: @reset 接收一个空对象
折叠事件: @fold 接收一个布尔值 true折叠 false 展开
表单组件类型:
input 普通输入框
amountInput 金额输入框
dateRangePicker 时间范围选择器
datePicker 单个日期选择器
select 普通下拉选择框
必传属性:
prop 字段名称
type 表单组件类型
label 该项标题名称
可选属性:
defaultValue 设置初始默认值 v-model传入的时候是无效的,用户可以自行设置默认值
attrs 表单组件的配置属性
表单组件 input 可以配置el-input 的属性 如:placeholder
注意:amountRangeInput 不要设置attrs 属性配置对象
selectV2 和 select 必须要在attrs 中传递一个 options 选项数组
selectV2 和 select 还有三个可选选项:
hasAllOptions 在选项第一个,配置一个全部选项 true为使用,默认为false
value 为选项值的字段名称 默认字段名称为 'value'
label 为选项描述的字段名称 默认字段名称为 'label'
** 注意:select的options也可以通过组件属性的形式进行传入,字段名称为prop+options 的字符串,
如果options是异步的,推荐作为组件的属性进行传入,第一因为响应式数据的更新通过组件的属性会自动传给子组件,
二是可以避免因为频繁修改配置文件中的值而导致组件被重复渲染。
** 可以通过v-model传递一个对象,但是不太推荐,因为即时使用了v-model还是需要指明配置对应的prop,否则prop为undefined
-->
<template>
<div class="searchForm">
<div class="searchForm-header pb-15">
<slot v-if="$slots['header-content']" name="header-content"></slot>
<template v-else>
<div class="wrapper mt-15">
<el-form
v-bind="getFormAttrs"
ref="formRef"
class="w-full"
inline
:model="searchForm"
label-position="left"
:size="props.size"
@submit.prevent
>
<div ref="formWrapperRef" class="formItemWrapper w-full">
<div class="mt--18 flex align-center flex-wrap">
<el-form-item
v-for="(item, index) in selectResult"
:key="index"
ref="formItemRef"
:prop="item.prop"
:label="item.label"
>
<template v-if="item.render">
{{ item.render() }}
</template>
<template v-else>
<component
:is="getConfigComponent(item.type)"
v-model="searchForm[item.prop]"
v-bind="{
...getComponentAttrs(item),
}"
:style="`width: ${componentWidth}px`"
/>
</template>
</el-form-item>
</div>
</div>
</el-form>
</div>
</template>
<div class="flex align-center">
<el-button
v-if="isSearchBtn"
class="btn"
type="primary"
@click="searchFn"
>
查询
</el-button>
<el-button v-if="isResetBtn" class="btn" @click="searchFn">
重置
</el-button>
<el-button
v-if="isDeleteBtn"
class="btn"
type="success"
@click="searchFn"
>
新增
</el-button>
<el-button v-if="deleteBtn" class="btn" type="danger" @click="searchFn">
删除
</el-button>
</div>
<slot name="ontherBtn"></slot>
</div>
</div>
</template>
<script setup lang="tsx" name="SearchForm">
import AmountInput from './Form.vue/AmountInput.vue' // 金额输入框
import DateRangePicker from './Form.vue/DateRangePicker.vue' // 时间范围选择器
import DatePicker from './Form.vue/DatePicker.vue' // 单个日期选择器
import Select from './Form.vue/Select.vue' // 普通下拉选择框
import { ElInput } from 'element-plus'
const attrs = useAttrs()
import {
ISearchForm,
EComponenetType,
ISeachFormConfig,
EDataType,
} from '@/types/searchType.ts'
import { IConfig } from '@/components/TenantTable/types/index'
defineOptions({
name: 'SearchForm',
inheritAttrs: false,
})
// 组件 - 数据类型 映射
const componentDataTypeMap = new Map([
[EComponenetType.amountInput, EDataType.string],
[EComponenetType.input, EDataType.string],
[EComponenetType.dateRangePicker, EDataType.array],
[EComponenetType.datePicker, EDataType.string],
[EComponenetType.select, EDataType.undefined],
])
const props = withDefaults(
defineProps<{
config: Array<ISeachFormConfig>
labelWidth?: string | number
modelValue?: ISearchForm
getTablelist?: (...args: any[]) => Promise<any>
size?: 'large' | 'default' | 'small'
addPermissionName?: string | Array<string> // 新增按钮的权限名称
additionalConfig?: Array<IConfig>
isSearchBtn?: boolean
isResetBtn?: boolean
isAddBtn?: boolean
isDeleteBtn?: boolean
}>(),
{
config: () => [],
labelWidth: 58,
modelValue: () => ({} as ISearchForm),
getTablelist: null,
size: 'default',
addPermissionName: '',
additionalConfig: null,
isSearchBtn: true,
isResetBtn: true,
isAddBtn: true,
isDeleteBtn: true,
},
)
// 获取组件
const getConfigComponent = (type: string) => {
const component = componentConfig[type]
if (component) {
return component
} else {
return null
}
}
const searchForm = reactive(props.modelValue || {})
const selectResult = ref([])
const componentWidth = props.size === 'default' ? 200 : 220 // 组件宽度
selectResult.value = props.config
const componentConfig = {
amountInput: AmountInput,
input: ElInput,
dateRangePicker: DateRangePicker,
datePicker: DatePicker,
select: Select,
}
function initSeachForm() {
props.config.forEach((item, index) => {
if (componentDataTypeMap.has(item.type as EComponenetType)) {
const prop = item.prop || `value${index}`
if (!item.prop) {
item.prop = prop
}
}
})
}
watch(
() => props.config,
(val) => {
if (val.length) {
initSeachForm()
}
},
{
immediate: true,
},
)
// 获取form表单的属性
const getFormAttrs = computed(() => {
const paramsAttrs = {
...attrs,
}
return paramsAttrs
})
function searchFn(params) {}
// 获取组件项传进来的attrs
const getComponentAttrs = (item: ISeachFormConfig) => {
const params = item.attrs
? {
...item.attrs,
}
: {}
return Object.assign({}, params)
}
const emits = defineEmits<{
(e: 'search', data: ISearchForm)
(e: 'reset', dataL: ISearchForm)
(e: 'fold', data: boolean)
(e: 'headerPrimaryBtn') // 头部新增按钮
(e: 'headerDefaultBtn', data, data2, searchData) // 头部导出按钮
(e: 'update:modelValue', data: ISearchForm)
(e: 'clear', data: ISearchForm) // 点击clearable触发的
(e: 'update:tableFieldForm', data: ISearchForm)
(e: 'getExcelData', data) // 头部新增按钮
}>()
</script>
<style lang="scss" scoped>
.ml-12 {
margin-left: 12px;
}
.searchForm {
position: relative;
&-header {
display: flex;
}
.filterBtn {
// position: absolute;
// top: -15px;
// z-index: 100;
}
}
.setTableFields {
margin-left: 12px;
}
.wrapper {
display: flex;
flex-direction: row;
align-items: flex-start;
justify-content: space-between;
}
.handle {
display: flex;
align-items: center;
justify-content: center;
flex: 0 0 auto;
margin-left: 10px;
// width: 32px;
// height: 32px;
& > div {
display: flex;
align-items: center;
}
.filterBtn {
&:hover {
opacity: 0.6;
}
}
}
// .el-form--inline {
// .el-form-item {
// & > .el-input, .el-select {
// width: v-bind(componentWidth)px;
// }
// }
// }
</style>
import { VNode } from 'vue'
interface ColumnAttrs {
field?: string
title?: string
width?: string | number
[key: string]: unknown
}
interface TableColumn {
prop: string
label: string
attrs?: ColumnAttrs
render?: {
edit?: (params: { row: TableRowData }) => VNode | JSX.Element
default?: (params: { row: TableRowData }) => VNode | JSX.Element
[key: string]:
| ((params: { row: TableRowData }) => VNode | JSX.Element)
| undefined
}
}
interface TableRowData {
[key: string]: unknown
}
type SlotFunction = (scope: { row: TableRowData }) => VNode | JSX.Element
export default defineComponent({
name: 'CustomizeTable',
props: {
config: {
type: Array as PropType<TableColumn[]>,
default: () => [],
},
tableEditConfig: {
type: Object,
default: () => ({}),
},
modelValue: {
type: Object,
default: () => ({}),
},
getTablelist: {
type: Function as PropType<(...args: unknown[]) => Promise<unknown>>,
default: null,
},
isShowCheckBox: {
type: Boolean,
default: true,
},
},
emits: ['update:modelValue', 'checkbox-change', 'getCheckboxRecords'],
setup(props, { emit ,attrs}) {
const tableRef = ref(null)
const tableData = ref<Record<string, unknown>[]>([])
const tableColumns = ref<TableColumn[]>([])
const editConfig = computed(() => {
return {
trigger: 'dblclick',
mode: 'cell',
enabled: false,
...props.tableEditConfig,
}
})
watch(
() => props.config,
(val) => {
if (val?.length) {
tableColumns.value = val
}
},
{
immediate: true,
},
)
watch(
() => props.modelValue,
(val) => {
tableData.value = Array.isArray(val) ? val : []
},
{
immediate: true,
},
)
async function getList() {
if (props.getTablelist) {
try {
const data = await props.getTablelist()
console.log(78, data)
} catch (error) {
console.log(error)
}
}
}
const getSelectEvent = () => {
const $table = tableRef.value
if ($table) {
const selectRecords = $table.getCheckboxRecords()
emit('getCheckboxRecords', selectRecords)
}
}
onMounted(() => {
getList()
})
return {
tableRef,
tableData,
tableColumns,
editConfig,
getSelectEvent,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-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)
}
},
)
}
return slots
})(),
}}
</vxe-column>
))}
</vxe-table>
)
},
})
...@@ -4,7 +4,16 @@ import App from './App.vue' ...@@ -4,7 +4,16 @@ import App from './App.vue'
import router from './router' import router from './router'
import store from './store' import store from './store'
import './styles/index.scss' import './styles/index.scss'
import VxeUITable from 'vxe-table'
import 'vxe-table/lib/style.css'
// 确保在渲染用户提供的HTML内容时,不会执行任何潜在的恶意脚本,从而提高应用的安全性 // 确保在渲染用户提供的HTML内容时,不会执行任何潜在的恶意脚本,从而提高应用的安全性
import vueDomPurifyHTMLPlugin from 'vue-dompurify-html' import vueDomPurifyHTMLPlugin from 'vue-dompurify-html'
createApp(App).use(vueDomPurifyHTMLPlugin).use(router).use(store).mount('#app') createApp(App)
.use(vueDomPurifyHTMLPlugin)
.use(router)
.use(store)
.use(VxeUITable)
.mount('#app')
...@@ -109,17 +109,25 @@ const router = createRouter({ ...@@ -109,17 +109,25 @@ const router = createRouter({
{ {
path: '/logistics/logisticsMethod', path: '/logistics/logisticsMethod',
meta: { meta: {
title: '物流公司', title: '物流方式',
}, },
component: () => import('@/views/logistics/logisticsMethod.vue'), component: () => import('@/views/logistics/logisticsMethod.vue'),
}, },
{ {
path: '/logistics/logisticsCompany', path: '/logistics/shippingAddress',
meta: { meta: {
title: '物流方式', title: '发货地址',
},
component: () => import('@/views/logistics/shippingAddress.vue'),
},
{
path: '/logistics/logisticsQuotation',
meta: {
title: '物流报价',
}, },
component: () => import('@/views/logistics/logisticsCompany.vue'), component: () => import('@/views/logistics/logisticsQuotation.vue'),
}, },
], ],
}, },
// 登录 // 登录
......
...@@ -97,10 +97,16 @@ const menu: MenuItem[] = [ ...@@ -97,10 +97,16 @@ const menu: MenuItem[] = [
label: '物流方式', label: '物流方式',
}, },
{ {
index: '/logistics/logisticsCompany', index: '/logistics/shippingAddress',
id: 2, id: 1,
label: '物流公司', label: '发货地址',
},
{
index: '/logistics/logisticsQuotation',
id: 1,
label: '物流报价',
}, },
], ],
}, },
// { // {
......
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
}
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
}
interface platformObj {
platform: string
logisticsName: string | number
showPlatform: (string | number)[]
}
interface ruleRefObj {
ruleId: string | number
ruleName: string | number
}
...@@ -44,6 +44,7 @@ export default function usePageList<T>(options: UsePageListOptions<T>) { ...@@ -44,6 +44,7 @@ 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)
......
import { defineComponent, ref } from 'vue'
import { ElDialog } from 'element-plus'
export default defineComponent({
name: 'CustomizeForm',
props: {
modelValue: {
type: Boolean,
default: false,
},
title: {
type: String,
default: '',
},
dialogWidth: {
type: String,
default: '600px',
},
},
emits: ['update:modelValue', 'close'],
setup(props, { emit, attrs, slots }) {
const formRef = ref(null)
const isShow = ref(false)
watch(
() => props.modelValue,
(val) => {
isShow.value = val
},
{ immediate: true },
)
return () => {
return (
<ElDialog
ref={(el) => (formRef.value = el)}
v-model={isShow.value}
title={props.title}
width={props.dialogWidth}
onClose={() => {
emit('close')
}}
close-on-click-modal={false}
{...attrs}
>
<div class="dialog-form">
{slots.default?.()}
{slots.footer?.()}
</div>
</ElDialog>
)
}
},
})
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]
}
export interface LogisticsQuotation {
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
}
import { defineConfig } from 'vite' import { defineConfig } from 'vite'
import AutoImport from 'unplugin-auto-import/vite' import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite' import Components from 'unplugin-vue-components/vite'
...@@ -13,12 +12,12 @@ export default defineConfig({ ...@@ -13,12 +12,12 @@ export default defineConfig({
port: 9527, port: 9527,
host: true, host: true,
proxy: { proxy: {
"/api": { '/api': {
target: "http://10.168.1.210:8060" target: 'http://10.168.31.194:8060',
} },
} },
}, },
plugins: [ plugins: [
vue(), vue(),
vueJsx(), vueJsx(),
......
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