Commit edb4d03b by linjinhong

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

parent 80ed9f46
......@@ -12,7 +12,7 @@
"rules": {
"vue/require-default-prop": "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" } }]
}
......@@ -9,6 +9,8 @@ declare global {
const ElLoading: typeof import('element-plus/es')['ElLoading']
const ElMessage: typeof import('element-plus/es')['ElMessage']
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 computed: typeof import('vue')['computed']
const createApp: typeof import('vue')['createApp']
......
......@@ -28,6 +28,7 @@ declare module 'vue' {
ElFormItem: typeof import('element-plus/es')['ElFormItem']
ElIcon: typeof import('element-plus/es')['ElIcon']
ElInput: typeof import('element-plus/es')['ElInput']
ElLink: typeof import('element-plus/es')['ElLink']
ElMenu: typeof import('element-plus/es')['ElMenu']
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
ElOption: typeof import('element-plus/es')['ElOption']
......@@ -56,10 +57,13 @@ declare module 'vue' {
Select: typeof import('./src/components/Form.vue/Select.vue')['default']
ShipmentOrderDetail: typeof import('./src/components/ShipmentOrderDetail.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']
TableRightMenu: typeof import('./src/components/TableRightMenu.vue')['default']
TableView: typeof import('./src/components/TableView.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']
}
export interface ComponentCustomProperties {
......
......@@ -21,7 +21,8 @@
"vue": "^3.4.19",
"vue-dompurify-html": "^5.1.0",
"vue-router": "^4.3.0",
"vue-tsc": "^2.1.10"
"vue-tsc": "^2.1.10",
"vxe-table": "^4.13.31"
},
"devDependencies": {
"@types/splitpanes": "^2.2.6",
......@@ -2826,6 +2827,19 @@
"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": {
"version": "1.1.7",
"resolved": "https://registry.npmmirror.com/@wangeditor/basic-modules/-/basic-modules-1.1.7.tgz",
......@@ -3477,6 +3491,12 @@
"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": {
"version": "3.0.0",
"resolved": "https://registry.npmmirror.com/dom7/-/dom7-3.0.0.tgz",
......@@ -6342,6 +6362,24 @@
"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": {
"version": "0.6.2",
"resolved": "https://registry.npmmirror.com/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz",
......@@ -6388,6 +6426,12 @@
"dev": true,
"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": {
"version": "4.0.0",
"resolved": "https://registry.npmmirror.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz",
......
......@@ -23,7 +23,8 @@
"vue": "^3.4.19",
"vue-dompurify-html": "^5.1.0",
"vue-router": "^4.3.0",
"vue-tsc": "^2.1.10"
"vue-tsc": "^2.1.10",
"vxe-table": "^4.13.31"
},
"devDependencies": {
"@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(
},
)
const emits = defineEmits<{
(e: 'update:modelValue', data: string | number)
(e: 'input', data: string | number)
(e: 'update:modelValue', data: string | number | null): void
(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 bool = isNumFloat(e) // 只允许输入数字和最多一个小数点
if (e === '') {
val.value = null
emits('input', '')
} else {
if (bool) {
const parts = e.split('.') // 将输入的数字按小数点分割成整数部分和小数部分
......@@ -63,7 +64,7 @@ const iptFn = (e) => {
}
}
}
emits('input', val.value)
emits('input', val.value || '')
}
watch(
......
......@@ -27,7 +27,7 @@ const props = withDefaults(
isNeedTime: false,
isrowDate: false,
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 AmountInput from './Form.vue/AmountInput.vue' // 金额输入框
......@@ -22,8 +30,6 @@ const componentDataTypeMap = new Map([
[EComponenetType.select, EDataType.undefined],
])
type ComponentType = Component
const componentConfig: Record<EComponenetType, ComponentType> = {
......@@ -39,14 +45,14 @@ const styles = {
position: 'relative',
},
searchFormHeader: {
display: 'flex', marginBottom: '10px'
display: 'flex',
marginBottom: '10px',
},
wrapper: {
display: 'flex',
flexDirection: 'row',
alignItems: 'flex-start',
justifyContent: 'space-between',
},
} as const
......@@ -55,62 +61,54 @@ export default defineComponent({
props: {
config: {
type: Array as PropType<ISeachFormConfig[]>,
default: () => []
default: () => [],
},
labelWidth: {
type: [String, Number],
default: 58
default: 58,
},
modelValue: {
type: Object as PropType<ISearchForm>,
default: () => ({})
default: () => ({}),
},
getTablelist: {
type: Function as PropType<(...args: unknown[]) => Promise<unknown>>,
default: null
default: null,
},
size: {
type: String as PropType<'large' | 'default' | 'small'>,
default: 'default'
default: 'default',
},
addPermissionName: {
type: [String, Array] as PropType<string | string[]>,
default: ''
default: '',
},
isSearchBtn: {
type: Boolean,
default: true
default: true,
},
isResetBtn: {
type: Boolean,
default: true
default: true,
},
isAddBtn: {
type: Boolean,
default: true
default: true,
},
isDeleteBtn: {
type: Boolean,
default: true
}
default: true,
},
},
emits: [
'search',
'reset',
'fold',
'headerPrimaryBtn',
'headerDefaultBtn',
'update:modelValue',
'clear',
'update:tableFieldForm',
'getExcelData'
],
emits: ['search', 'reset', 'fold', 'add', 'delete', 'update:modelValue'],
setup(props, { emit, slots, attrs }) {
const formRef = ref()
const formWrapperRef = ref()
const searchForm = reactive<ISearchForm>(props.modelValue || {})
const searchForm = ref<ISearchForm>(props.modelValue || {})
const selectResult = ref<ISeachFormConfig[]>([])
const componentWidth = computed(() => props.size === 'default' ? 200 : 220)
const componentWidth = computed(() =>
props.size === 'default' ? 200 : 220,
)
function initSeachForm() {
props.config.forEach((item, index) => {
......@@ -133,12 +131,12 @@ export default defineComponent({
},
{
immediate: true,
}
},
)
// 获取form表单的属性
const getFormAttrs = computed(() => ({
...attrs
...attrs,
}))
// 获取组件项传进来的attrs
......@@ -146,14 +144,6 @@ export default defineComponent({
return item.attrs ? { ...item.attrs } : {}
}
const searchFn = () => {
emit('search', searchForm)
}
const resetFn = () => {
emit('reset', {})
}
const handleSubmit = (e: Event) => {
e.preventDefault()
}
......@@ -167,14 +157,14 @@ export default defineComponent({
<div style={styles.wrapper} class="mt-15">
<ElForm
{...getFormAttrs.value}
ref={formRef}
ref={el=>formRef.value=el}
class="w-full"
inline
model={searchForm}
model={searchForm.value}
labelPosition="left"
size={props.size}
{...{
onSubmit: handleSubmit
onSubmit: handleSubmit,
}}
>
<div ref={formWrapperRef} class="formItemWrapper w-full">
......@@ -184,20 +174,19 @@ export default defineComponent({
key={index}
prop={item.prop}
label={item.label}
style={{marginBottom:'0px'}}
style={{ marginBottom: '0px' }}
>
{item.render ? (
item.render()
) : (
h(componentConfig[item.type as EComponenetType], {
modelValue: searchForm[item.prop],
'onUpdate:modelValue': (value: unknown) => {
searchForm[item.prop] = value
},
...getComponentAttrs(item),
style: { width: `${componentWidth.value}px` }
})
)}
{item.render
? item.render()
: h(componentConfig[item.type as EComponenetType], {
modelValue: searchForm.value[item.prop],
'onUpdate:modelValue': (value: unknown) => {
searchForm.value[item.prop] = value
},
...getComponentAttrs(item),
style: { width: `${componentWidth.value}px` },
})}
</ElFormItem>
))}
</div>
......@@ -207,21 +196,28 @@ export default defineComponent({
)}
<div style={{ display: 'flex', alignItems: 'center' }}>
{props.isSearchBtn && (
<ElButton class="btn" type="primary" onClick={searchFn}>
<ElButton
class="btn"
type="primary"
onClick={() => emit('search', searchForm.value)}
>
查询
</ElButton>
)}
{props.isResetBtn && (
<ElButton class="btn" onClick={resetFn}>
<ElButton
class="btn"
onClick={() => {
formRef.value?.resetFields()
// emit('update:modelValue', {})
emit('reset')
}}
>
重置
</ElButton>
)}
{props.isAddBtn && (
<ElButton
class="btn"
type="success"
onClick={() => emit('headerPrimaryBtn')}
>
<ElButton class="btn" type="success" onClick={() => emit('add')}>
新增
</ElButton>
)}
......@@ -229,7 +225,7 @@ export default defineComponent({
<ElButton
class="btn"
type="danger"
onClick={() => emit('headerDefaultBtn')}
onClick={() => emit('delete')}
>
删除
</ElButton>
......@@ -239,5 +235,5 @@ export default defineComponent({
</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'
import router from './router'
import store from './store'
import './styles/index.scss'
import VxeUITable from 'vxe-table'
import 'vxe-table/lib/style.css'
// 确保在渲染用户提供的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({
{
path: '/logistics/logisticsMethod',
meta: {
title: '物流公司',
title: '物流方式',
},
component: () => import('@/views/logistics/logisticsMethod.vue'),
},
{
path: '/logistics/logisticsCompany',
path: '/logistics/shippingAddress',
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[] = [
label: '物流方式',
},
{
index: '/logistics/logisticsCompany',
id: 2,
label: '物流公司',
index: '/logistics/shippingAddress',
id: 1,
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>) {
pageSize.value = res.size
data.value = res.records
}
console.log(7, data.value)
} catch (error) {
console.error(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]
}
<template>
<div class="user-page flex-column card h-100 overflow-hidden">
<div class="header-filter-form">
<ElForm :model="searchForm" inline>
<ElFormItem label="用户名" prop="account">
<ElInput
v-model="searchForm.account"
placeholder="请输入用户名"
clearable
style="width: 200px"
/>
</ElFormItem>
<ElFormItem label="状态" prop="status">
<ElSelect
v-model="searchForm.status"
clearable
style="width: 200px"
placeholder="请选择状态"
>
<ElOption label="启用" :value="1"></ElOption>
<ElOption label="禁用" :value="0"></ElOption>
</ElSelect>
</ElFormItem>
<ElFormItem>
<ElButton type="primary" @click="search">查询</ElButton>
</ElFormItem>
<ElFormItem>
<ElButton @click="resetSearchForm">重置</ElButton>
</ElFormItem>
<ElFormItem>
<ElButton type="success" @click="addUser">新增</ElButton>
</ElFormItem>
<ElFormItem>
<ElButton type="danger" @click="deleteUser">删除</ElButton>
</ElFormItem>
</ElForm>
</div>
<div class="user-content flex-1 flex-column overflow-hidden">
<div class="user-list flex-1 overflow-hidden">
<ElTable
:data="tableData"
default-expand-all
style="width: 100%; height: 100%"
@selection-change="handleSelectionChange"
>
<ElTableColumn
type="selection"
header-align="center"
align="center"
width="55"
/>
<ElTableColumn
type="index"
header-align="center"
align="center"
label="序号"
width="55"
/>
<ElTableColumn prop="account" header-align="center" label="用户名">
<template #default="scope">
<div>
<span
>{{ scope.row.account }}
<ElTooltip
v-if="scope.row.supperMark === 1"
content="超级管理员"
placement="top"
>
<Icon
v-if="scope.row.supperMark === 1"
name="zu54"
style="vertical-align: super; cursor: pointer"
>
</Icon>
</ElTooltip>
</span>
</div>
</template>
</ElTableColumn>
<ElTableColumn
prop="factoryCode"
header-align="center"
label="工厂"
align="center"
/>
<ElTableColumn
prop="status"
header-align="center"
align="center"
label="状态"
>
<template #default="scope">
<el-tooltip
:content="scope.row.status ? '启用' : '禁用'"
placement="top"
>
<ElSwitch
v-model="scope.row.status"
:active-value="1"
:inactive-value="0"
@change="(e:number) => onChangeStatus(e, scope.row)"
></ElSwitch>
</el-tooltip>
</template>
</ElTableColumn>
<ElTableColumn
label="操作"
width="130"
header-align="center"
align="center"
>
<template #default="scope">
<el-icon
size="24"
title="编辑"
color="#EF6C00"
style="cursor: pointer; vertical-align: middle"
@click="editUser(scope.row)"
>
<Edit />
</el-icon>
</template>
</ElTableColumn>
</ElTable>
</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>
<ElDialog
v-model="dialogVisible"
:title="editId ? '编辑用户' : '新增用户'"
width="600px"
:close-on-click-modal="false"
@opened="onOpenedUserForm"
>
<div class="dialog-form">
<ElForm
ref="editFormRef"
:model="editForm"
:rules="rules"
label-width="90px"
>
<ElFormItem label="用户名" prop="account">
<ElInput
v-model="editForm.account"
placeholder="请输入用户名"
clearable
/>
</ElFormItem>
<ElFormItem v-if="!editId" label="密码" prop="password">
<ElInput
v-model="editForm.password"
placeholder="请输入密码"
clearable
type="password"
autocomplete="off"
:show-password="true"
/>
</ElFormItem>
<ElFormItem label="状态" prop="status">
<ElCheckbox
v-model="editForm.status"
label="启用"
true-value="1"
false-value="0"
></ElCheckbox>
</ElFormItem>
<ElFormItem label="角色" prop="supperMark">
<ElCheckbox
v-model="editForm.supperMark"
label="超级管理员"
true-value="1"
false-value="0"
></ElCheckbox>
</ElFormItem>
</ElForm>
</div>
<template #footer>
<div class="dialog-footer">
<ElButton @click="dialogVisible = false"> 取消 </ElButton>
<ElButton type="primary" @click="save">保存</ElButton>
</div>
</template>
</ElDialog>
</template>
<script setup lang="ts">
import {
getUserList,
addUserApi,
updateUserApi,
deleteUserApi,
getDetailsByIdApi,
changeUserStatusApi,
} from '@/api/auth'
import Icon from '@/components/Icon.vue'
import { UserEditForm, userData, userSearchForm } from '@/types/api/user'
import usePageList from '@/utils/hooks/usePageList'
import { useValue } from '@/utils/hooks/useValue'
import { showConfirm } from '@/utils/ui'
import { Edit } from '@element-plus/icons-vue'
import type { FormRules } from 'element-plus'
import { reactive, ref } from 'vue'
const [searchForm, resetSearchForm] = useValue<userSearchForm>({})
const [editForm, resetEditForm] = useValue<UserEditForm>({
account: '',
password: '',
supperMark: '0',
status: '1',
})
const {
currentPage,
pageSize,
total,
data: tableData,
refresh: search,
onCurrentPageChange: handleCurrentChange,
onPageSizeChange: handleSizeChange,
} = usePageList({
query: (page, pageSize) =>
getUserList(searchForm.value, page, pageSize).then((res) => res.data),
})
const dialogVisible = ref(false)
const editFormRef = ref()
const selection = ref<userData[]>([])
const rules = reactive<FormRules<UserEditForm>>({
account: [
{
required: true,
message: '请输入用户名',
},
],
password: [
{
required: true,
message: '请输入密码',
},
],
})
const editId = ref<number | undefined>(undefined)
const addUser = () => {
editId.value = undefined
dialogVisible.value = true
resetEditForm()
}
const deleteUser = async () => {
if (!selection.value.length) {
return ElMessage({
message: '请选择用户',
type: 'warning',
offset: window.innerHeight / 2,
})
}
try {
await showConfirm('是否删除用户', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
})
} catch {
return
}
try {
const ids = selection.value.map((item) => item.id).join(',')
await deleteUserApi(ids)
ElMessage({
message: '删除成功',
type: 'success',
offset: window.innerHeight / 2,
})
search()
} catch (e) {
search()
// showError(e)
}
}
const editUser = async (item: userData) => {
editId.value = item.id
try {
const res = await getDetailsByIdApi(item.id)
res.data.supperMark = res.data.supperMark + ''
res.data.status = res.data.status + ''
editForm.value = res.data
dialogVisible.value = true
} catch (e) {
//showError(e)
}
}
const save = async () => {
try {
await editFormRef.value.validate()
} catch {
return
}
try {
if (!editId.value) {
await addUserApi({
...editForm.value,
supperMark: Number(editForm.value.supperMark),
status: Number(editForm.value.status),
})
} else {
await updateUserApi({
...editForm.value,
supperMark: Number(editForm.value.supperMark),
status: Number(editForm.value.status),
})
}
ElMessage({
message: '保存成功',
type: 'success',
offset: window.innerHeight / 2,
})
dialogVisible.value = false
search()
} catch (e) {
return
}
}
const onOpenedUserForm = async () => {
editFormRef.value?.clearValidate()
}
const handleSelectionChange = (s: userData[]) => {
selection.value = s
}
const onChangeStatus = async (value: number, item: userData) => {
try {
const res = await changeUserStatusApi(value, item.id)
ElMessage({
message: res.message,
type: 'success',
offset: window.innerHeight / 2,
})
search()
} catch (e) {
//showError(e)
}
}
</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"></SearchForm>
<SearchForm
:config="searchConfig"
@search="search"
@add="addDialog"
@delete="deleteFn"
v-model="searchForm"
></SearchForm>
</div>
<div class="user-content flex-1 flex-column overflow-hidden">
<div class="user-list flex-1 overflow-hidden">
<ElTable
:data="tableData"
default-expand-all
style="width: 100%; height: 100%"
@selection-change="handleSelectionChange"
>
<ElTableColumn
type="selection"
header-align="center"
align="center"
width="55"
/>
<ElTableColumn
type="index"
header-align="center"
align="center"
label="序号"
width="55"
/>
<ElTableColumn prop="account" header-align="center" label="用户名">
<template #default="scope">
<div>
<span
>{{ scope.row.account }}
<ElTooltip
v-if="scope.row.supperMark === 1"
content="超级管理员"
placement="top"
>
<Icon
v-if="scope.row.supperMark === 1"
name="zu54"
style="vertical-align: super; cursor: pointer"
>
</Icon>
</ElTooltip>
</span>
</div>
</template>
</ElTableColumn>
<ElTableColumn
prop="factoryCode"
header-align="center"
label="工厂"
align="center"
/>
<ElTableColumn
prop="status"
header-align="center"
align="center"
label="状态"
>
<template #default="scope">
<el-tooltip
:content="scope.row.status ? '启用' : '禁用'"
placement="top"
>
<ElSwitch
v-model="scope.row.status"
:active-value="1"
:inactive-value="0"
@change="(e:number) => onChangeStatus(e, scope.row)"
></ElSwitch>
</el-tooltip>
</template>
</ElTableColumn>
<ElTableColumn
label="操作"
width="130"
header-align="center"
align="center"
>
<template #default="scope">
<el-icon
size="24"
title="编辑"
color="#EF6C00"
style="cursor: pointer; vertical-align: middle"
@click="editUser(scope.row)"
>
<Edit />
</el-icon>
</template>
</ElTableColumn>
</ElTable>
<div class="user-list flex-1 overflow-hidden" v-loading="loading">
<CustomizeTable
v-model="tableData"
:config="tableConfig"
@getCheckboxRecords="handleCheckboxRecords"
></CustomizeTable>
</div>
<ElPagination
v-model:current-page="currentPage"
......@@ -104,88 +30,69 @@
></ElPagination>
</div>
</div>
<ElDialog
<LogDialog
:title="editForm.id ? '编辑物流方式' : '新增物流方式'"
dialogWidth="1000px"
v-model="dialogVisible"
:title="editId ? '编辑用户' : '新增用户'"
width="600px"
:close-on-click-modal="false"
@opened="onOpenedUserForm"
@close="cancelFn"
>
<div class="dialog-form">
<ElForm
ref="editFormRef"
:model="editForm"
:rules="rules"
label-width="90px"
>
<ElFormItem label="用户名" prop="account">
<ElInput
v-model="editForm.account"
placeholder="请输入用户名"
clearable
/>
</ElFormItem>
<ElFormItem v-if="!editId" label="密码" prop="password">
<ElInput
v-model="editForm.password"
placeholder="请输入密码"
clearable
type="password"
autocomplete="off"
:show-password="true"
/>
</ElFormItem>
<ElFormItem label="状态" prop="status">
<ElCheckbox
v-model="editForm.status"
label="启用"
true-value="1"
false-value="0"
></ElCheckbox>
</ElFormItem>
<ElFormItem label="角色" prop="supperMark">
<ElCheckbox
v-model="editForm.supperMark"
label="超级管理员"
true-value="1"
false-value="0"
></ElCheckbox>
</ElFormItem>
</ElForm>
</div>
<CustomizeForm
ref="editFormRef"
v-model="editForm"
:config="formConfig"
formItemWidth="100%"
>
</CustomizeForm>
<template #footer>
<div class="dialog-footer">
<ElButton @click="dialogVisible = false"> 取消 </ElButton>
<div style="text-align: center">
<ElButton @click="cancelFn">取消</ElButton>
<ElButton type="primary" @click="save">保存</ElButton>
</div>
</template>
</ElDialog>
</LogDialog>
<LogDialog
title="操作日志"
v-model="logDialogVisible"
@close="logDialogVisible = false"
>
<div v-for="item in logList" :key="item.id" style="margin-bottom: 8px">
<span style="margin-right: 10px">{{ item.createTime }}</span>
<span>{{ item.description }}</span>
</div>
</LogDialog>
</template>
<script setup lang="tsx">
import {
getUserList,
addUserApi,
updateUserApi,
deleteUserApi,
getDetailsByIdApi,
changeUserStatusApi,
} from '@/api/auth'
import Icon from '@/components/Icon.vue'
getLogisticsWayList,
addLogisticsWay,
updateLogisticsWay,
updateStatusLogisticsWay,
deleteLogisticsWay,
getWarehouseList,
getRuleList,
getPlatformList,
getLogisticsLog,
} from '@/api/logistics'
import type { LogisticsMethod } from '@/types/api/logistics'
import SearchForm from '@/components/SearchForm.tsx'
import { UserEditForm, userData, userSearchForm } from '@/types/api/user'
import LogDialog from './components/LogDialog.tsx'
import CustomizeForm from '@/components/CustomizeForm.tsx'
import CustomizeTable from '@/components/VxeTable.tsx'
import { IFormConfig } from '@/components/CustomizeForm.tsx'
import usePageList from '@/utils/hooks/usePageList'
import { useValue } from '@/utils/hooks/useValue'
import { useValue } from './hooks/useValue'
import { showConfirm } from '@/utils/ui'
import { Edit } from '@element-plus/icons-vue'
import type { FormRules } from 'element-plus'
import { reactive, ref } from 'vue'
const [searchForm, resetSearchForm] = useValue<userSearchForm>({})
const [editForm, resetEditForm] = useValue<UserEditForm>({
account: '',
password: '',
supperMark: '0',
status: '1',
import { Edit, Delete, List } from '@element-plus/icons-vue'
const [searchForm] = useValue({})
const [editForm, resetEditForm] = useValue<LogisticsMethod>({
platformList: [{ platform: '', logisticsName: '', showPlatform: [] }],
ruleRef: { ruleId: '', ruleName: '' },
status: 1,
})
const {
currentPage,
......@@ -197,74 +104,377 @@ const {
onPageSizeChange: handleSizeChange,
} = usePageList({
query: (page, pageSize) =>
getUserList(searchForm.value, page, pageSize).then((res) => res.data),
getLogisticsWayList({
...searchForm.value,
pageSize: pageSize,
currentPage: page,
}).then(({ data }) => {
console.log(130, data)
return data
}),
})
const dialogVisible = ref(false)
const editFormRef = ref()
const selection = ref<userData[]>([])
const logDialogVisible = ref(false)
const editFormRef = ref(null)
const selection = ref([])
const searchConfig = ref([
{
prop: 'project_id',
prop: 'name',
type: 'input',
label: '物流方式',
attrs: {
placeholder: '请输入物流方式',
},
},
{
prop: 'status',
type: 'select',
label: '启用状态',
attrs: {
placeholder: '请选择启用状态',
options: [
{ label: '启用', value: 1 },
{ label: '禁用', value: 0 },
],
},
},
{
prop: 'serviceCode',
type: 'input',
label: '物流编码',
attrs: {
placeholder: '请输入物流编码',
},
},
])
const platformList = ref([])
const warehouseList = ref([])
const ruleNameList = ref([])
const formConfig = ref<IFormConfig[]>([
{ title: '物流基础信息' },
{
prop: 'name',
type: 'input',
label: '物流名称',
attrs: {
placeholder: '请输入物流名称',
},
rules: [
{
required: true,
message: '请输入物流名称',
},
],
},
{
prop: 'warehouseId',
type: 'select',
label: '调入项目',
label: '仓库名称',
attrs: {
placeholder: '请选择调入项目名称',
label: 'project_name',
placeholder: '请选择仓库名称',
label: 'name',
value: 'id',
options: [],
onChange: (value) => {
editForm.value.warehouseName = value.name
},
},
rules: [
{
required: true,
message: '请选择仓库名称',
},
],
},
{
prop: 'name_and_spce',
prop: 'ruleId',
type: 'select',
label: '申报规则',
attrs: {
placeholder: '请选择申报规则',
label: 'name',
value: 'id',
options: [],
onChange: (value) => {
editForm.value.ruleRef.ruleId = value.id
editForm.value.ruleRef.ruleName = value.name
},
},
rules: [
{
required: true,
message: '请选择申报规则',
},
],
},
{
prop: 'serviceCode',
type: 'input',
label: '材料名称及规格',
label: '物流编码',
attrs: {
placeholder: '请输入物流编码',
},
rules: [
{
required: true,
message: '请输入物流编码',
},
],
},
{
prop: 'siteUrl',
type: 'input',
label: '查询网址',
attrs: {
placeholder: '请输入材料名称及规格',
placeholder: '请输入查询网址',
},
rules: [
{
required: true,
message: '请输入物流编码',
},
],
},
{
prop: 'status',
type: 'datePicker',
label: '审批状态',
type: 'switch',
label: '启用状态',
attrs: {
hasAllOptions: true,
isList: true,
options: [],
activeValue: 1,
inactiveValue: 0,
},
render: (item) => <span>{11111}</span>,
},
{
title: '平台物流名称',
render: (item, formData, that) =>
formData.platformList?.map((item, index) => (
<div style="display: flex; width:100%">
<el-form-item
key={index}
class="renderItem"
label="平台名称"
style="flex:50%"
prop={`platformList.${index}.showPlatform`}
rules={[
{
required: true,
message: '请选择平台名称',
trigger: 'blur',
},
]}
>
<el-select
multiple
collapse-tags
collapse-tags-tooltip
v-model={item['showPlatform']}
>
{platformList.value?.map((el, idx) => (
<el-option label={el} value={el} key={idx}></el-option>
))}
</el-select>
</el-form-item>
<el-form-item
key={index}
class="renderItem"
label="物流名称"
style="display: flex;flex:50%"
prop={`platformList.${index}.logisticsName`}
rules={[
{
required: true,
message: '请输入物流名称',
trigger: 'blur',
},
]}
>
<el-input
v-model={item.logisticsName}
placeholder="请输入物流名称"
/>
</el-form-item>
<div style="display: flex;flex:20%">
{formData.platformList.length - 1 === index && (
<el-button
style="margin-left: 10px"
type="primary"
onClick={() => addCol()}
>
新增
</el-button>
)}
{index >= 1 && (
<el-button
style="margin-left: 10px"
type="primary"
onClick={() => deleteCol(index)}
>
删除
</el-button>
)}
</div>
</div>
)),
},
])
const rules = reactive<FormRules<UserEditForm>>({
account: [
{
required: true,
message: '请输入用户名',
const tableConfig = ref([
{
prop: 'name',
label: '物流名称',
},
{
prop: 'warehouseName',
label: '发货仓库',
},
{
prop: 'serviceCode',
label: '物流编码',
},
{
prop: 'status',
label: '状态',
render: {
default: ({ row }: { row: LogisticsMethod }) => (
<div>
<el-switch
v-model={row.status}
style="--el-switch-on-color: #13ce66; --el-switch-off-color: #ff4949"
inline-prompt
active-text="启用"
inactive-text="禁用"
active-value={1}
inactive-value={0}
onClick={() => changeStatus(row)}
/>
</div>
),
},
],
password: [
{
required: true,
message: '请输入密码',
},
{
prop: 'status1',
label: '平台状态',
attrs: { width: '400px', align: 'center' },
render: {
default: ({ row }: { row: LogisticsMethod }) =>
row.platformList?.map((el) => (
<div>
<span style="margin-right:18px;">
<span>{'平台:'}</span>
<span class="logistics-name">{el.platform}</span>
</span>
<span>
<span>{'物流名称:'}</span>
<span class="logistics-name">{el.logisticsName}</span>
</span>
</div>
)),
},
],
},
{
prop: 'status2',
label: '申报规则',
render: {
default: ({ row }: { row: LogisticsMethod }) => (
<div>{row.ruleList?.[0].ruleName}</div>
),
},
},
{
prop: 'siteUrl',
label: '查询网址',
attrs: { width: '200px', align: 'center' },
render: {
default: ({ row }: { row: LogisticsMethod }) => (
<div>
<el-link href={row.siteUrl} target="_blank">
{row.siteUrl}
</el-link>
</div>
),
},
},
{
prop: 'opeare',
label: '操作',
attrs: {
align: 'center',
},
render: {
default: ({ row }: { row: LogisticsMethod }) => (
<div>
<el-icon
size="24"
title="编辑"
color="#EF6C00"
style="cursor: pointer; vertical-align: middle"
onclick={() => editWay(row)}
>
<Edit />
</el-icon>
<el-icon
size="24"
title="删除"
color="#f56c6c"
style="cursor: pointer; vertical-align: middle"
onclick={() => deleteWay(row)}
>
<Delete />
</el-icon>
<el-icon
size="24"
title="日志"
color="#008aff"
style="cursor: pointer; vertical-align: middle"
onclick={() => showLog(row)}
>
<List />
</el-icon>
</div>
),
},
},
])
onMounted(() => {
getAllList()
})
const editId = ref<number | undefined>(undefined)
const addUser = () => {
editId.value = undefined
dialogVisible.value = true
resetEditForm()
}
const deleteUser = async () => {
if (!selection.value.length) {
return ElMessage({
message: '请选择用户',
type: 'warning',
offset: window.innerHeight / 2,
const loading = ref(false)
async function changeStatus(data: LogisticsMethod) {
console.log(data)
loading.value = true
try {
const params = {
id: data.id,
status: data.status,
}
await updateStatusLogisticsWay(params)
ElMessage({
message: '更改状态成功',
type: 'success',
})
search()
} catch (error) {
console.log(error)
// data.status
} finally {
loading.value = false
}
}
function cancelFn() {
dialogVisible.value = false
editFormRef.value?.resetFields()
resetEditForm()
}
async function deleteWay(item) {
try {
await showConfirm('是否删除用户', {
await showConfirm('是否删除物流方式', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
......@@ -273,12 +483,11 @@ const deleteUser = async () => {
return
}
try {
const ids = selection.value.map((item) => item.id).join(',')
await deleteUserApi(ids)
const ids = [item.id].join(',')
await deleteLogisticsWay(ids)
ElMessage({
message: '删除成功',
type: 'success',
offset: window.innerHeight / 2,
})
search()
} catch (e) {
......@@ -286,69 +495,181 @@ const deleteUser = async () => {
// showError(e)
}
}
const editUser = async (item: userData) => {
editId.value = item.id
async function editWay(item: LogisticsMethod) {
try {
const res = await getDetailsByIdApi(item.id)
res.data.supperMark = res.data.supperMark + ''
res.data.status = res.data.status + ''
editForm.value = res.data
editForm.value = { ...item }
editForm.value.ruleId = item.ruleList[0].ruleId
editForm.value.platformList = item.platformList.map((el) => {
el.showPlatform = el.platform.split(',')
return el
})
console.log(493, editForm.value)
dialogVisible.value = true
} catch (e) {
//showError(e)
console.log(e)
}
}
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 }
if (params.platformList?.length) {
params.platformList.forEach((el) => {
el.platform = el.showPlatform.join()
})
}
resolve(params)
}),
])
console.log(isValid, postData)
return { isValid, postData }
}
async function save() {
const { isValid, postData } = await checkData()
if (isValid) {
try {
if (!postData.id) {
await addLogisticsWay({
...postData,
})
} else {
await updateLogisticsWay({
...postData,
})
}
ElMessage({
message: '保存成功',
type: 'success',
offset: window.innerHeight / 2,
})
cancelFn()
search()
} catch (e) {
return
}
}
}
const save = async () => {
function addDialog() {
dialogVisible.value = true
}
async function deleteFn() {
if (!selection.value.length) {
return ElMessage({
message: '请选择用户',
type: 'warning',
})
}
try {
await editFormRef.value.validate()
await showConfirm('是否删除用户', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
})
} catch {
return
}
try {
if (!editId.value) {
await addUserApi({
...editForm.value,
supperMark: Number(editForm.value.supperMark),
status: Number(editForm.value.status),
})
} else {
await updateUserApi({
...editForm.value,
supperMark: Number(editForm.value.supperMark),
status: Number(editForm.value.status),
})
}
const ids = selection.value.map((item) => item.id).join(',')
await deleteLogisticsWay(ids)
ElMessage({
message: '保存成功',
message: '删除成功',
type: 'success',
offset: window.innerHeight / 2,
})
dialogVisible.value = false
search()
} catch (e) {
return
search()
// showError(e)
}
}
const onOpenedUserForm = async () => {
editFormRef.value?.clearValidate()
function handleCheckboxRecords(value: never[]) {
console.log(351, value)
selection.value = value
}
const handleSelectionChange = (s: userData[]) => {
selection.value = s
function addCol() {
editForm.value.platformList?.push({
platform: '',
logisticsName: '',
showPlatform: [],
})
}
const onChangeStatus = async (value: number, item: userData) => {
function deleteCol(index: number) {
editForm.value.platformList?.splice(index, 1)
}
async function getAllList() {
try {
const res = await changeUserStatusApi(value, item.id)
ElMessage({
message: res.message,
type: 'success',
offset: window.innerHeight / 2,
const res = await Promise.all([
getWarehouseList(),
getRuleList(),
getPlatformList(),
])
res.forEach((item, index) => {
if (item.code === 200) {
if (index == 0) {
warehouseList.value = item.data
} else if (index == 1) {
ruleNameList.value = item.data || []
} else if (index == 2) {
platformList.value = item.data
}
}
})
search()
} catch (e) {
//showError(e)
if (
formConfig.value[2] &&
formConfig.value[3] &&
'attrs' in formConfig.value[2]
) {
// 非空断言强制类型
;(formConfig.value[2]!.attrs as Record<string, any>).options = [
...warehouseList.value,
]
;(formConfig.value[3]!.attrs as Record<string, any>).options = [
...ruleNameList.value,
]
}
console.log(545, res)
} catch (error) {
console.log(error)
}
}
const logList = ref([])
async function showLog(row) {
logDialogVisible.value = true
try {
const { data } = await getLogisticsLog({
logType: 'logistics_way',
relaId: row.id,
})
logList.value = data
} catch (error) {
console.log(error)
}
}
watch(
editForm,
(val) => {
// console.log(442, val)
},
{ deep: true, immediate: true },
)
</script>
<style lang="scss" scoped>
......
<template>
<div class="user-page flex-column card h-100 overflow-hidden">
<div class="header-filter-form">
<SearchForm
:config="searchConfig"
@search="search"
@add="addDialog"
@delete="deleteFn"
v-model="searchForm"
>
<template #ontherBtn>
<div style="margin-left: 10px">
<ElButton @click="downloadExcel" type="info" plain
>下载模版</ElButton
>
<ElButton type="primary" plain @click="exportExcel"
>新增导入</ElButton
>
<ElButton type="primary" plain @click="exportExcel"
>修改导入</ElButton
>
</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">
<CustomizeTable
v-model="tableData"
:config="tableConfig"
:merge-cells="mergeCells"
@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 {
getlogisticsWayAllList,
getlogisticsQuotationList,
addLogisticsQuotation,
updateLogisticsQuotation,
deleteLogisticsQuotation,
importLogisticsQuotation,
exportLogisticsQuotationTemplate,
} 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 usePageList from '@/utils/hooks/usePageList'
import { useValue } from './hooks/useValue'
import { showConfirm } from '@/utils/ui'
import { Edit, Delete, List } from '@element-plus/icons-vue'
const [searchForm] = useValue({})
const [editForm, resetEditForm] = useValue<LogisticsMethod>({
platformList: [{ platform: [], logisticsName: '' }],
ruleRef: [{ ruleId: '', ruleName: '' }],
status: 1,
})
const {
currentPage,
pageSize,
total,
data: tableData,
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
// }),
(() => {})(),
})
const dialogVisible = ref(false)
const editFormRef = ref(null)
const selection = ref([])
const searchConfig = ref([
{
prop: 'name',
type: 'select',
label: '物流方式',
attrs: {
placeholder: '请选择物流方式',
options: [],
},
},
])
const formConfig = ref([
{ title: '物流基础信息' },
{
prop: 'name',
type: 'input',
label: 'ZONE 1',
attrs: {
placeholder: '请输入物流名称',
},
rules: [
{
required: true,
message: '请输入物流名称',
},
],
},
{
prop: 'warehouseName',
type: 'select',
label: 'ZONE 2',
attrs: {
placeholder: '请选择仓库名称',
options: [
{ label: '仓库1', value: 1 },
{ label: '仓库2', value: 2 },
],
},
rules: [
{
required: true,
message: '请选择仓库名称',
},
],
},
{
prop: 'ruleRefObj',
type: 'input',
label: 'ZONE 3',
attrs: {
placeholder: '请选择申报规则',
options: [
{ label: '仓库1', value: 1 },
{ label: '仓库2', value: 2 },
],
},
rules: [
{
required: true,
message: '请选择申报规则',
},
],
},
{
prop: 'serviceCode',
type: 'input',
label: 'ZONE 4',
attrs: {
placeholder: '请输入物流编码',
},
rules: [
{
required: true,
message: '请输入物流编码',
},
],
},
{
prop: 'siteUrl',
type: 'input',
label: 'ZONE 5',
attrs: {
placeholder: '请输入查询网址',
},
rules: [
{
required: true,
message: '请输入物流编码',
},
],
},
{
prop: 'status',
type: 'input',
label: 'ZONE 6',
},
{
prop: 'status',
type: 'input',
label: 'ZONE 7',
},
{
prop: 'status',
type: 'input',
label: 'ZONE 8',
},
{
prop: 'status',
type: 'input',
label: 'ZONE 9',
},
])
const tableConfig = ref([
{
prop: 'name',
label: 'Rate(oz/lb)',
},
{
prop: 'warehouseName',
label: 'Rate(g)',
},
{
prop: 'serviceCode',
label: 'Rate(kg)',
},
{
prop: 'status',
label: 'Logistics',
},
{
prop: 'status1',
label: 'ZONE 1',
},
{
prop: 'status2',
label: 'ZONE 2',
},
{
prop: 'status2',
label: 'ZONE 3',
},
{
prop: 'siteUrl',
label: 'ZONE 4',
},
{
prop: 'siteUrl',
label: 'ZONE 5',
},
{
prop: 'siteUrl',
label: 'ZONE 6',
},
{
prop: 'siteUrl',
label: 'ZONE 7',
},
{
prop: 'siteUrl',
label: 'ZONE 8',
},
{
prop: 'siteUrl',
label: 'ZONE 9',
},
{
prop: 'opeare',
label: '操作',
attrs: {
align: 'center',
},
render: {
default: ({ row }: { row: LogisticsMethod }) => (
<div>
<el-icon
size="24"
title="编辑"
color="#EF6C00"
style="cursor: pointer; vertical-align: middle"
onclick={() => editWay(row)}
>
<Edit />
</el-icon>
<el-icon
size="24"
title="删除"
color="#f56c6c"
style="cursor: pointer; vertical-align: middle"
onclick={() => deleteWay(row)}
>
<Delete />
</el-icon>
</div>
),
},
},
])
onMounted(() => {
// getAllList()
})
const loading = ref(false)
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 }
dialogVisible.value = true
} catch (e) {
console.log(e)
}
}
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 addLogisticsQuotation({
...postData,
})
} else {
await updateLogisticsQuotation({
...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 ids = selection.value.map((item) => item.id).join(',')
await deleteLogisticsQuotation(ids)
ElMessage({
message: '删除成功',
type: 'success',
})
search()
} catch (e) {
search()
// showError(e)
}
}
function handleCheckboxRecords(value: never[]) {
console.log(351, value)
selection.value = value
}
async function getAllList() {
try {
const res = await Promise.all([getlogisticsWayAllList()])
searchConfig.value[0].attrs.options = []
console.log(545, res)
} catch (error) {
console.log(error)
}
}
function downloadExcel() {}
function exportExcel() {}
watch(
editForm,
(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>
<template>
<div class="user-page flex-column card h-100 overflow-hidden">
<div class="header-filter-form">
<el-button type="success" @click="addDialog">
{{ '新增' }}
</el-button>
<el-button
:disabled="tableData.filter((el) => el.checked).length === 0"
type="danger"
@click="
deleteSection(tableData.filter((el) => el.checked).map((el) => el.id))
"
>
{{ '删除' }}
</el-button>
</div>
<div class="user-content flex-1 flex-column overflow-hidden">
<div class="user-list flex-1 overflow-hidden" v-loading="loading">
<el-row :gutter="20">
<el-col v-for="(item, index) in tableData" :key="index" :span="12">
<div class="address-item">
<div class="check">
<el-checkbox v-model="item.checked"></el-checkbox>
</div>
<div class="left">
<div class="name">
<b>{{ item.shipperName }}</b>
<p
:title="
[item.phoneNumber, item.email, item.postalCode].join(' ')
"
>
{{
[item.phoneNumber, item.email, item.postalCode].join(' ')
}}
</p>
<el-tag type="success" v-if="item.swDefault">
{{ '默认' }}
</el-tag>
</div>
<p
:title="
[
item.countryName,
// item.countryCode,
item.stateProvince,
// item.stateProvinceAbbr,
item.district,
// item.districtCode,
item.city,
// item.cityCode,
]
.filter((el) => el != null && el !== '')
.join(' ')
"
class="address"
>
{{
[
item.countryName,
// item.countryCode,
item.stateProvince,
// item.stateProvinceAbbr,
item.district,
// item.districtCode,
item.city,
// item.cityCode,
]
.filter((el) => el != null && el !== '')
.join(' ')
}}
</p>
<p
:title="
[item.addressLine1, item.addressLine2, item.addressLine3]
.filter((el) => el != null && el !== '')
.join(' ')
"
class="address address-en"
>
{{
[item.addressLine1, item.addressLine2, item.addressLine3]
.filter((el) => el != null && el !== '')
.join(' ')
}}
</p>
</div>
<div class="action">
<el-icon
size="24"
title="编辑"
color="#EF6C00"
style="cursor: pointer; vertical-align: middle"
@click="editAddress(item)"
>
<Edit />
</el-icon>
<el-icon
size="24"
title="删除"
color="#f56c6c"
style="cursor: pointer; vertical-align: middle"
@click="deleteAddress(item)"
>
<Delete />
</el-icon>
<el-icon
size="24"
title="日志"
color="#008aff"
style="cursor: pointer; vertical-align: middle"
@click="showLog(item)"
>
<List />
</el-icon>
</div>
</div>
</el-col>
</el-row>
</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 ? '编辑发货地址' : '新增发货地址'"
v-model="dialogVisible"
dialogWidth="1000px"
@close="cancelFn"
>
<CustomizeForm
ref="editFormRef"
v-model="editForm"
:config="formConfig"
formItemWidth="50%"
>
</CustomizeForm>
<template #footer>
<div style="text-align: center">
<ElButton @click="cancelFn">取消</ElButton>
<ElButton type="primary" @click="save">保存</ElButton>
</div>
</template>
</LogDialog>
<LogDialog
title="日志"
v-model="logDialogVisible"
@close="logDialogVisible = false"
>
<div v-for="item in logList" :key="item.id" style="margin-bottom: 8px">
<span style="margin-right: 10px">{{ item.createTime }}</span>
<span>{{ item.description }}</span>
</div>
</LogDialog>
</template>
<script setup lang="tsx">
import {
getAddressByIdList,
addAddress,
updateAddress,
getAddressById,
getLogisticsLog,
deleteAddressByIds,
} from '@/api/logistics'
import type { LogisticsMethod } from '@/types/api/logistics'
import { IFormConfig } from '@/components/CustomizeForm.tsx'
import { LogisticsQuotation } from './types/logisticsQuotation.ts'
import LogDialog from './components/LogDialog.tsx'
import CustomizeForm from '@/components/CustomizeForm.tsx'
import { Edit, Delete, List } from '@element-plus/icons-vue'
import usePageList from '@/utils/hooks/usePageList'
import { useValue } from './hooks/useValue'
import { showConfirm } from '@/utils/ui'
const [searchForm] = useValue({})
const [editForm, resetEditForm] = useValue<LogisticsQuotation>({
swDefault: false,
})
const {
currentPage,
pageSize,
total,
data: tableData,
refresh: search,
onCurrentPageChange: handleCurrentChange,
onPageSizeChange: handleSizeChange,
} = usePageList({
query: (page, pageSize) =>
getAddressByIdList({
...searchForm.value,
pageSize: pageSize,
currentPage: page,
}).then(({ data }) => {
console.log(130, data)
return data
}),
})
const dialogVisible = ref(false)
const logDialogVisible = ref(false)
const editFormRef = ref(null)
const selection = ref([])
const formConfig = ref<IFormConfig[]>([
{
prop: 'shipperName',
type: 'input',
label: '发货人姓名',
attrs: {
placeholder: '请输入发货人姓名',
},
rules: [{ required: true, message: '请输入发货人姓名' }],
},
{
prop: 'addressLine1',
type: 'input',
label: '地址 1',
attrs: {
width: '100%',
placeholder: '请输入地址 1',
},
rules: [{ required: true, message: '请输入地址1' }],
},
{
prop: 'addressLine2',
type: 'input',
label: '地址 2',
attrs: {
width: '100%',
placeholder: '请输入地址 2',
},
},
{
prop: 'addressLine3',
type: 'input',
label: '地址 3',
attrs: {
width: '100%',
placeholder: '请输入地址 3',
},
},
{
prop: 'city',
type: 'input',
label: '城市',
attrs: {
// options: [],
placeholder: '请输入城市',
},
rules: [{ required: true, message: '请输入城市' }],
},
{
prop: 'cityCode',
type: 'input',
label: '城市编码',
attrs: {
// options: [],
placeholder: '请输入城市编码',
},
},
{
prop: 'district',
type: 'input',
label: '区',
attrs: {
// options: [],
placeholder: '请选择区',
},
},
{
prop: 'districtCode',
type: 'input',
label: '区编码',
attrs: {
// options: [],
placeholder: '请选择区',
},
},
{
prop: 'cspAccount',
type: 'input',
label: 'CSP账号',
attrs: {
placeholder: '请输入CSP账号',
},
},
{
prop: 'postalCode',
type: 'input',
label: '邮编',
attrs: {
placeholder: '请输入邮编',
},
},
{
prop: 'stateProvince',
type: 'input',
label: '州/省',
attrs: {
// options: [],
placeholder: '请输入州/省',
},
},
{
prop: 'stateProvinceAbbr',
type: 'input',
label: '州/省简称',
attrs: {
// options: [],
placeholder: '请输入州/省简称',
},
},
{
prop: 'countryName',
type: 'input',
label: '国家名称',
attrs: {
// options: [],
placeholder: '请输入国家名称',
},
rules: [{ required: true, message: '请输入国家名称' }],
},
{
prop: 'countryCode',
type: 'input',
label: '国家代码',
attrs: {
// options: [],
placeholder: '请输入国家代码',
},
},
{
prop: 'phoneNumber',
type: 'input',
label: '电话',
attrs: {
placeholder: '请输入电话',
},
},
{
prop: 'rfcTaxId',
type: 'input',
label: 'RFC税号',
attrs: {
placeholder: '请输入RFC税号',
},
},
{
prop: 'swDefault',
type: 'switch',
label: '是否默认',
},
])
const loading = ref(false)
function cancelFn() {
dialogVisible.value = false
editFormRef.value?.resetFields()
resetEditForm()
}
async function deleteAddress(item) {
try {
await showConfirm('是否删除发货地址', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
})
} catch {
return
}
try {
const ids = { ids: [item.id].join(',') }
console.log(381, ids)
await deleteAddressByIds(ids)
ElMessage({
message: '删除成功',
type: 'success',
})
search()
} catch (e) {
search()
// showError(e)
}
}
async function editAddress(item) {
try {
editForm.value = { ...item }
console.log(395, editForm.value)
dialogVisible.value = true
} catch (e) {
console.log(e)
}
}
//表格校验&&更改parasm数据
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 addAddress({
...postData,
})
} else {
await updateAddress({
...postData,
})
}
ElMessage({
message: '保存成功',
type: 'success',
offset: window.innerHeight / 2,
})
cancelFn()
search()
} catch (e) {
return
}
}
}
function addDialog() {
dialogVisible.value = true
}
async function deleteSection(arr) {
try {
await showConfirm('是否删除发货地址', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
})
} catch {
return
}
try {
const ids = { ids: arr.join(',') }
await deleteAddressByIds(ids)
ElMessage({
message: '删除成功',
type: 'success',
})
search()
} catch (e) {
search()
// showError(e)
}
}
const logList = ref([])
async function showLog(row) {
logDialogVisible.value = true
try {
const { data } = await getLogisticsLog({
logType: 'logistics_address',
relaId: row.id,
})
logList.value = data
} catch (error) {
console.log(error)
}
}
</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;
}
:deep() {
.rowClass {
background-color: red;
}
}
.address-item {
box-sizing: border-box;
padding: 10px;
margin-bottom: 10px;
border: 1px solid #ececec;
display: flex;
align-items: center;
justify-content: space-between;
.check {
margin-right: 10px;
}
.left {
flex: 1;
flex-shrink: 0;
overflow: hidden;
margin-right: 10px;
}
.address {
margin-bottom: 10px;
font-size: 14px;
overflow: hidden;
/* 确保超出的文本被裁剪 */
white-space: nowrap;
/* 确保文本在一行内显示 */
text-overflow: ellipsis;
/* 超出的文本显示为省略号 */
width: 100%;
}
.action {
.iconView {
}
}
.name {
display: flex;
align-items: center;
margin-bottom: 10px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
p {
flex: 1;
flex-shrink: 0;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
margin: 0 10px;
font-size: 14px;
}
b {
font-size: 17px;
}
span {
margin: 0 10px;
font-size: 14px;
}
}
}
</style>
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 AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
......@@ -13,12 +12,12 @@ export default defineConfig({
port: 9527,
host: true,
proxy: {
"/api": {
target: "http://10.168.1.210:8060"
}
}
'/api': {
target: 'http://10.168.31.194:8060',
},
},
},
plugins: [
vue(),
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