Commit 7af87fdd by wuqian
parents 97ef1db0 3fa77032
...@@ -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-console": "off","no-unused-vars": "off",
}, },
"overrides": [{ "files": "*.vue", "rules": { "no-undef": "off" } }] "overrides": [{ "files": "*.vue", "rules": { "no-undef": "off" } }]
} }
...@@ -22,4 +22,5 @@ dist-ssr ...@@ -22,4 +22,5 @@ dist-ssr
*.njsproj *.njsproj
*.sln *.sln
*.sw? *.sw?
.eslintrc.*
!/src/views/warehouse/manage.vue !/src/views/warehouse/manage.vue
...@@ -5,7 +5,88 @@ ...@@ -5,7 +5,88 @@
// Generated by unplugin-auto-import // Generated by unplugin-auto-import
export {} export {}
declare global { declare global {
const EffectScope: typeof import('vue')['EffectScope']
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 computed: typeof import('vue')['computed']
const createApp: typeof import('vue')['createApp']
const createPinia: typeof import('pinia')['createPinia']
const customRef: typeof import('vue')['customRef']
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
const defineComponent: typeof import('vue')['defineComponent']
const defineStore: typeof import('pinia')['defineStore']
const effectScope: typeof import('vue')['effectScope']
const getActivePinia: typeof import('pinia')['getActivePinia']
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
const getCurrentScope: typeof import('vue')['getCurrentScope']
const h: typeof import('vue')['h']
const inject: typeof import('vue')['inject']
const isProxy: typeof import('vue')['isProxy']
const isReactive: typeof import('vue')['isReactive']
const isReadonly: typeof import('vue')['isReadonly']
const isRef: typeof import('vue')['isRef']
const mapActions: typeof import('pinia')['mapActions']
const mapGetters: typeof import('pinia')['mapGetters']
const mapState: typeof import('pinia')['mapState']
const mapStores: typeof import('pinia')['mapStores']
const mapWritableState: typeof import('pinia')['mapWritableState']
const markRaw: typeof import('vue')['markRaw']
const nextTick: typeof import('vue')['nextTick']
const onActivated: typeof import('vue')['onActivated']
const onBeforeMount: typeof import('vue')['onBeforeMount']
const onBeforeRouteLeave: typeof import('vue-router')['onBeforeRouteLeave']
const onBeforeRouteUpdate: typeof import('vue-router')['onBeforeRouteUpdate']
const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
const onDeactivated: typeof import('vue')['onDeactivated']
const onErrorCaptured: typeof import('vue')['onErrorCaptured']
const onMounted: typeof import('vue')['onMounted']
const onRenderTracked: typeof import('vue')['onRenderTracked']
const onRenderTriggered: typeof import('vue')['onRenderTriggered']
const onScopeDispose: typeof import('vue')['onScopeDispose']
const onServerPrefetch: typeof import('vue')['onServerPrefetch']
const onUnmounted: typeof import('vue')['onUnmounted']
const onUpdated: typeof import('vue')['onUpdated']
const onWatcherCleanup: typeof import('vue')['onWatcherCleanup']
const provide: typeof import('vue')['provide']
const reactive: typeof import('vue')['reactive']
const readonly: typeof import('vue')['readonly']
const ref: typeof import('vue')['ref']
const resolveComponent: typeof import('vue')['resolveComponent']
const setActivePinia: typeof import('pinia')['setActivePinia']
const setMapStoreSuffix: typeof import('pinia')['setMapStoreSuffix']
const shallowReactive: typeof import('vue')['shallowReactive']
const shallowReadonly: typeof import('vue')['shallowReadonly']
const shallowRef: typeof import('vue')['shallowRef']
const storeToRefs: typeof import('pinia')['storeToRefs']
const toRaw: typeof import('vue')['toRaw']
const toRef: typeof import('vue')['toRef']
const toRefs: typeof import('vue')['toRefs']
const toValue: typeof import('vue')['toValue']
const triggerRef: typeof import('vue')['triggerRef']
const unref: typeof import('vue')['unref']
const useAttrs: typeof import('vue')['useAttrs']
const useCssModule: typeof import('vue')['useCssModule']
const useCssVars: typeof import('vue')['useCssVars']
const useId: typeof import('vue')['useId']
const useLink: typeof import('vue-router')['useLink']
const useModel: typeof import('vue')['useModel']
const useRoute: typeof import('vue-router')['useRoute']
const useRouter: typeof import('vue-router')['useRouter']
const useSlots: typeof import('vue')['useSlots']
const useTemplateRef: typeof import('vue')['useTemplateRef']
const watch: typeof import('vue')['watch']
const watchEffect: typeof import('vue')['watchEffect']
const watchPostEffect: typeof import('vue')['watchPostEffect']
const watchSyncEffect: typeof import('vue')['watchSyncEffect']
}
// for type re-export
declare global {
// @ts-ignore
export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
import('vue')
} }
...@@ -7,7 +7,10 @@ export {} ...@@ -7,7 +7,10 @@ export {}
declare module 'vue' { declare module 'vue' {
export interface GlobalComponents { export interface GlobalComponents {
AmountInput: typeof import('./src/components/Form.vue/AmountInput.vue')['default']
CommonCard: typeof import('./src/components/CommonCard.vue')['default'] CommonCard: typeof import('./src/components/CommonCard.vue')['default']
DatePicker: typeof import('./src/components/Form.vue/DatePicker.vue')['default']
DateRangePicker: typeof import('./src/components/Form.vue/DateRangePicker.vue')['default']
ElButton: typeof import('element-plus/es')['ElButton'] ElButton: typeof import('element-plus/es')['ElButton']
ElCard: typeof import('element-plus/es')['ElCard'] ElCard: typeof import('element-plus/es')['ElCard']
ElCarousel: typeof import('element-plus/es')['ElCarousel'] ElCarousel: typeof import('element-plus/es')['ElCarousel']
...@@ -28,6 +31,7 @@ declare module 'vue' { ...@@ -28,6 +31,7 @@ declare module 'vue' {
ElImage: typeof import('element-plus/es')['ElImage'] ElImage: typeof import('element-plus/es')['ElImage']
ElInput: typeof import('element-plus/es')['ElInput'] ElInput: typeof import('element-plus/es')['ElInput']
ElInputNumber: typeof import('element-plus/es')['ElInputNumber'] ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
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']
...@@ -46,6 +50,7 @@ declare module 'vue' { ...@@ -46,6 +50,7 @@ declare module 'vue' {
ElTag: typeof import('element-plus/es')['ElTag'] ElTag: typeof import('element-plus/es')['ElTag']
ElTooltip: typeof import('element-plus/es')['ElTooltip'] ElTooltip: typeof import('element-plus/es')['ElTooltip']
ElTree: typeof import('element-plus/es')['ElTree'] ElTree: typeof import('element-plus/es')['ElTree']
ElUpload: typeof import('element-plus/es')['ElUpload']
Icon: typeof import('./src/components/Icon.vue')['default'] Icon: typeof import('./src/components/Icon.vue')['default']
ImageView: typeof import('./src/components/ImageView.vue')['default'] ImageView: typeof import('./src/components/ImageView.vue')['default']
LogList: typeof import('./src/components/LogList.vue')['default'] LogList: typeof import('./src/components/LogList.vue')['default']
...@@ -53,8 +58,10 @@ declare module 'vue' { ...@@ -53,8 +58,10 @@ declare module 'vue' {
RenderColumn: typeof import('./src/components/RenderColumn.vue')['default'] RenderColumn: typeof import('./src/components/RenderColumn.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink'] RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView'] RouterView: typeof import('vue-router')['RouterView']
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']
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']
UploadExcel: typeof import('./src/components/UploadExcel.vue')['default'] UploadExcel: typeof import('./src/components/UploadExcel.vue')['default']
......
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
"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",
"xlsx": "^0.18.5" "xlsx": "^0.18.5"
}, },
"devDependencies": { "devDependencies": {
...@@ -31,7 +32,7 @@ ...@@ -31,7 +32,7 @@
"@typescript-eslint/eslint-plugin": "^7.1.1", "@typescript-eslint/eslint-plugin": "^7.1.1",
"@typescript-eslint/parser": "^7.1.1", "@typescript-eslint/parser": "^7.1.1",
"@vitejs/plugin-vue": "^5.0.4", "@vitejs/plugin-vue": "^5.0.4",
"@vitejs/plugin-vue-jsx": "^4.1.0", "@vitejs/plugin-vue-jsx": "^4.2.0",
"@vue/eslint-config-typescript": "^12.0.0", "@vue/eslint-config-typescript": "^12.0.0",
"eslint": "^8.57.0", "eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0", "eslint-config-prettier": "^9.1.0",
......
...@@ -11,11 +11,11 @@ import { ...@@ -11,11 +11,11 @@ import {
import axios from './axios' import axios from './axios'
import { PodMakeOrderData } from '@/types/api/podMakeOrder' import { PodMakeOrderData } from '@/types/api/podMakeOrder'
export interface LogisticsData { export interface LogisticsData {
logisticsWayName: string; // 物流名称 logisticsWayName: string // 物流名称
warehouseName: string; // 发货仓库 warehouseName: string // 发货仓库
status: boolean; status: boolean
logisticsWayCode: string; // 物流编码 logisticsWayCode: string // 物流编码
partition: string; // 所在分区 partition: string // 所在分区
} }
export function getOrderTabData() { export function getOrderTabData() {
return axios.get<never, BaseRespData<Tab[]>>( return axios.get<never, BaseRespData<Tab[]>>(
...@@ -50,7 +50,12 @@ export function getCardOrderList( ...@@ -50,7 +50,12 @@ export function getCardOrderList(
}, },
) )
} }
export function confirmOrderApi(data: number[], productionClient: string,type:string,logisticsTrialCalculation?:LogisticsData) { export function confirmOrderApi(
data: number[],
productionClient: string,
type: string,
logisticsTrialCalculation?: LogisticsData | null,
) {
return axios.post<never, BaseRespData<never>>( return axios.post<never, BaseRespData<never>>(
'factory/podJomallOrderUs/confirmOrders', 'factory/podJomallOrderUs/confirmOrders',
{ {
...@@ -128,27 +133,19 @@ export function printProductionOrderApi(orderIds: number[]) { ...@@ -128,27 +133,19 @@ export function printProductionOrderApi(orderIds: number[]) {
orderIds, orderIds,
) )
} }
export function printPrintOrderApi( export function printPrintOrderApi(orderIds: number[]) {
orderIds: number[],
productionClient: string,
) {
return axios.post<never, BaseRespData<string>>( return axios.post<never, BaseRespData<string>>(
'factory/podJomallOrderUs/printPickPdf', 'factory/podJomallOrderUs/printPickPdf',
{ {
ids: orderIds.join(','), ids: orderIds.join(',')
productionClient,
}, },
) )
} }
export function printPickingOrderApi( export function printPickingOrderApi(orderIds: number[]) {
orderIds: number[],
productionClient: string,
) {
return axios.post<never, BaseRespData<string>>( return axios.post<never, BaseRespData<string>>(
'factory/podJomallOrderUs/pickingComplete', 'factory/podJomallOrderUs/pickingComplete',
{ {
ids: orderIds.join(','), ids: orderIds.join(','),
productionClient,
}, },
) )
} }
...@@ -238,7 +235,7 @@ export function updateRemarkApi(id: number, content: string) { ...@@ -238,7 +235,7 @@ export function updateRemarkApi(id: number, content: string) {
export function getLogisticsCalculation(id: number) { export function getLogisticsCalculation(id: number) {
return axios.get<never, BaseRespData<never>>( return axios.get<never, BaseRespData<never>>(
'factory/podJomallOrderUs/getLogisticsCalculation', 'factory/podJomallOrderUs/getLogisticsCalculation',
{ params:{id} }, { params: { id } },
) )
} }
export function loadWarehouseListApi() { export function loadWarehouseListApi() {
...@@ -246,3 +243,9 @@ export function loadWarehouseListApi() { ...@@ -246,3 +243,9 @@ export function loadWarehouseListApi() {
'factoryWarehouseInfo/getAll', 'factoryWarehouseInfo/getAll',
) )
} }
export function refreshMaterialApi(orderIds: string) {
return axios.post<never, BaseRespData<never>>(
'factory/podJomallOrderProductUs/refreshDesignImages ',
{ orderIds },
)
}
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'
type SimpleFormData = Record<string, unknown>
// 定义表单项配置接口
export interface IFormConfig {
fixed?: string
title?: string
prop?: string
label?: string
type?: string
btn?: JSX.Element | (() => JSX.Element)
attrs?: SimpleFormData
isIncludeProp?: boolean
rules?: FormItemRule | FormItemRule[]
render?: (
item?: IFormConfig,
formData?: SimpleFormData,
index?: number,
) => VNode | VNode[] | 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,
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)
const tableConfig = shallowRef<IFormConfig[]>([])
watch(
() => props.config,
(val) => {
tableConfig.value = val
},
{ immediate: true, deep: true },
)
// 监听表单数据变化
watch(
() => formData.value,
(val) => {
emit('update:modelValue', val)
},
{ deep: true },
)
// 监听外部数据变化
watch(
() => props.modelValue,
(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 clearValidate = async (clearFields?: string[]) => {
if (!formRef.value) return false
return await formRef.value.clearValidate(clearFields)
}
// 重置表单
const resetFields = async () => {
await formRef.value?.resetFields()
}
function refashConfig(showFields?: string[]) {
if (showFields?.length) {
const filterArr = props.config.filter((item) =>
showFields.includes(item.prop as string),
)
tableConfig.value = [
...props.config.filter((item) => !item.isIncludeProp),
...filterArr,
]
.filter((item) => !item.fixed)
.concat(tableConfig.value.filter((item) => item.fixed))
} else {
tableConfig.value = [
...props.config.filter((item) => !item.isIncludeProp),
]
console.log(tableConfig.value)
}
}
return {
formRef,
formData,
validate,
resetFields,
getFormAttrs,
getComponentAttrs,
refashConfig,
tableConfig,
clearValidate,
}
},
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.tableConfig.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)}
</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 as keyof typeof componentConfig
] || ElInput,
{
modelValue: this.formData[item.prop as string],
'onUpdate:modelValue': (value: unknown) => {
this.formData[item.prop as string] = value
},
disabled:
typeof item.attrs?.disabled === 'function'
? item.attrs.disabled()
: item.attrs?.disabled,
...this.getComponentAttrs(item),
},
)}
{item.btn &&
(typeof item.btn === 'function' ? item.btn() : item.btn)}
</ElFormItem>
),
)}
</ElForm>
)
},
})
<template>
<el-input
v-model.trim="val"
type="text"
:placeholder="placeholder"
class="amountInput"
:size="size"
:disabled="props.isDisabled"
@input="iptFn"
>
<template v-if="hasSuffix" #suffix>{{ suffix }}</template>
<template v-if="hasAppend" #append>
<component :is="appendRender" />
</template>
</el-input>
</template>
<script lang="tsx" setup>
import { isNumFloat } from '@/utils/validate'
const props = withDefaults(
defineProps<{
modelValue?: string | number
suffix?: string
placeholder?: string
size?: '' | 'default' | 'small' | 'large'
hasSuffix?: boolean
hasAppend?: boolean
appendRender?: () => JSX.Element
isDisabled?: boolean
}>(),
{
modelValue: '',
suffix: '元',
placeholder: '请输入费用',
size: 'default',
hasSuffix: true,
isDisabled: false,
hasAppend: false,
appendRender: undefined,
},
)
const emits = defineEmits<{
(e: 'update:modelValue', data: string | number | null): void
(e: 'input', data: string | number): void
}>()
const val = ref<string | number | null>(props.modelValue)
const iptFn = (e: string) => {
const bool = isNumFloat(e) // 只允许输入数字和最多一个小数点
if (e === '') {
val.value = null
emits('input', '')
} else {
if (bool) {
const parts = e.split('.') // 将输入的数字按小数点分割成整数部分和小数部分
if (parts[1] && parts[1].length > 2) {
// 如果小数部分超过两位,则只保留两位小数
val.value = `${parts[0]}.${parts[1].slice(0, 2)}`
} else {
val.value = e
}
} else {
const decimalCount = (e.match(/\./g) || []).length // 计算小数点的个数
if (decimalCount > 1) {
// 如果小数点个数大于1,则将多余的小数点替换为空字符串
val.value = e.replace(/\./g, (match: string, offset: number) => {
console.log(67, match)
return offset === e.lastIndexOf('.') ? '.' : ''
})
} else {
val.value = e.replace(/[^\d.]/g, '')
}
}
}
emits('input', val.value || '')
}
watch(
() => val.value,
(newVal) => {
if (newVal === '') {
emits('update:modelValue', null)
} else {
emits('update:modelValue', newVal)
}
},
)
watch(
() => props.modelValue,
(newVal: string | number) => {
if (newVal !== undefined) {
val.value = newVal
}
},
{
immediate: true,
},
)
</script>
<style lang="scss" scoped>
:deep() {
.el-form-item__content {
line-height: 0 !important;
}
}
</style>
<template>
<el-date-picker
ref="dateRef"
:type="props.type"
:placeholder="props.placeholder"
value-format="YYYY-MM-DD"
class="datePicker"
:popper-class="!props.isNeedTime ? '' : 'popperClass'"
:teleported="true"
:disabled-date="disabledDate"
/>
</template>
<script lang="ts" setup>
const dateRef = ref(null)
const props = withDefaults(
defineProps<{
placeholder?: string
type?: string
isNeedTime?: boolean
rowData?: unknown
isrowDate?: boolean
newDisableDate?: (...arg: unknown[]) => boolean
}>(),
{
placeholder: '请选择日期',
type: 'date',
isNeedTime: false,
isrowDate: false,
rowData: undefined,
newDisableDate: undefined,
},
)
// 用于存储动态的禁用日期逻辑
function disabledDate(time: Date) {
if (props.isrowDate && props.newDisableDate) {
return props.newDisableDate(time, props.rowData)
}
}
</script>
<style lang="scss" scoped>
.popperClass .el-time-spinner__wrapper {
width: 100% !important;
}
.popperClass .el-scrollbar:nth-of-type(2) {
display: none !important;
}
</style>
<template>
<el-date-picker
ref="dateRangePickerRef"
class="dateRangePicker"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="YYYY-MM-DD"
style="width: 220px"
:popper-class="!props.isNeedTime ? '' : 'popperClass'"
:teleported="true"
v-bind="attrs"
/>
</template>
<script lang="ts" setup>
const props = defineProps({
isNeedTime: {
type: Boolean,
default: false,
},
})
const attrs = useAttrs()
const dateRangePickerRef = ref(null)
defineExpose({
dateRangePickerRef,
})
</script>
<style lang="scss">
.popperClass .el-time-spinner__wrapper {
width: 100% !important;
}
.popperClass .el-scrollbar:nth-of-type(2) {
display: none !important;
}
</style>
<template>
<!-- <el-row style="flex-wrap: nowrap"> -->
<el-select
v-model="model"
class="select"
placeholder="请选择"
:empty-values="[null, undefined, '']"
loading-text="加载中..."
v-bind="{
...getEvent,
}"
:loading="loading"
@change="changeFn"
>
<el-option
v-for="item in getOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
<!-- </el-row> -->
</template>
<script lang="ts" setup>
import { cloneDeep } from 'lodash-es'
interface IOption {
[key: string]: unknown
}
const props = withDefaults(
defineProps<{
options: Array<IOption>
value?: string
label?: string
hasAllOptions?: boolean
labelIsValue?: boolean // value 使用label字段
initChange?: boolean // 首次是否触发change事件
modelValue?: string | number | string[] | number[]
isRefresh?: boolean
isValueKey?: boolean
load?: (arg: (val: boolean) => void) => void // focus后再加载数据的
}>(),
{
options: () => [],
value: 'value', // 值字段名 默认字段为value
label: 'label', // 描述字段名 默认字段为label
hasAllOptions: false, // 使用全部下拉选项
labelIsValue: false, // value 是 label
initChange: false,
modelValue: '',
isRefresh: false,
isValueKey: false,
},
)
const emits = defineEmits<{
(e: 'change', option: IOption, value: string | number): void
(
e: 'update:modelValue',
data: string | number | string[] | number[] | undefined,
): void
(e: 'updatedOption'): void
}>()
const loading = ref<boolean>(false)
const model = ref(props.modelValue)
const allOption = {
value: '',
label: '全部',
}
const fields = reactive({
value: props.value,
label: props.label,
})
// 选项处理
const getOptions = computed(() => {
const value = props.labelIsValue ? fields.label : fields.value
const arr = cloneDeep(props.options)
const data = arr.map((item) => {
!item.value && (item.value = eval(`item.${value}`))
!item.label && (item.label = eval(`item.${fields.label}`))
return item
})
if (props.hasAllOptions) {
data.unshift(allOption)
return data
}
return data
})
const changeFn = (id: string | number) => {
const value = props.labelIsValue ? fields.label : fields.value
const findItem = props.options.find((item) => item[value] === id)
if (findItem) {
emits('change', findItem, id)
}
}
watch(model, (val) => {
emits('update:modelValue', val)
})
if (props.initChange) {
watch([() => props.modelValue, getOptions], ([val, getOptionsVal]) => {
if (val !== 0) {
model.value = val
getOptionsVal.length && changeFn(val as string | number)
}
})
} else {
watch(
() => props.modelValue,
(val) => {
// if (val !== 0) {
model.value = val
// }
},
)
}
const FocusFn = () => {
try {
props.load &&
props.load((val: boolean) => {
loading.value = val
})
} catch (err) {
console.log(err)
}
}
const getEvent = computed(() => {
let result = {}
if (props.load) {
result = {
onFocus: FocusFn,
}
}
return result
})
</script>
<style lang="scss" scoped>
.select {
width: 100%;
height: 100%;
}
.iConFont {
margin-left: 8px;
cursor: pointer;
&:hover {
color: #2f9bff;
}
}
</style>
<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?: unknown
}>(),
{
activeColor: '#13ce66',
inactiveColor: ' #ff4949',
modelValue: undefined,
},
)
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): void
}>()
</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, watch, h } from 'vue'
import type { Component } from 'vue'
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, ElForm, ElFormItem, ElButton } from 'element-plus'
import {
ISearchForm,
EComponenetType,
ISeachFormConfig,
EDataType,
} from '@/types/searchType'
// 组件 - 数据类型 映射
const componentDataTypeMap = new Map([
[EComponenetType.amountInput, EDataType.string],
[EComponenetType.input, EDataType.string],
[EComponenetType.dateRangePicker, EDataType.array],
[EComponenetType.datePicker, EDataType.string],
[EComponenetType.select, EDataType.undefined],
])
type ComponentType = Component
const componentConfig: Record<EComponenetType, ComponentType> = {
[EComponenetType.amountInput]: AmountInput,
[EComponenetType.input]: ElInput,
[EComponenetType.dateRangePicker]: DateRangePicker,
[EComponenetType.datePicker]: DatePicker,
[EComponenetType.select]: Select,
}
const styles = {
searchForm: {
position: 'relative',
},
searchFormHeader: {
display: 'flex',
marginBottom: '10px',
},
wrapper: {
display: 'flex',
flexDirection: 'row',
alignItems: 'flex-start',
justifyContent: 'space-between',
},
} as const
export default defineComponent({
name: 'SearchForm',
props: {
config: {
type: Array as PropType<ISeachFormConfig[]>,
default: () => [],
},
labelWidth: {
type: [String, Number],
default: 58,
},
modelValue: {
type: Object as PropType<ISearchForm>,
default: () => ({}),
},
getTablelist: {
type: Function as PropType<(...args: unknown[]) => Promise<unknown>>,
default: null,
},
size: {
type: String as PropType<'large' | 'default' | 'small'>,
default: 'default',
},
addPermissionName: {
type: [String, Array] as PropType<string | string[]>,
default: '',
},
isSearchBtn: {
type: Boolean,
default: true,
},
isResetBtn: {
type: Boolean,
default: true,
},
isAddBtn: {
type: Boolean,
default: true,
},
isDeleteBtn: {
type: Boolean,
default: true,
},
},
emits: ['search', 'reset', 'fold', 'add', 'delete', 'update:modelValue'],
setup(props, { emit, slots, attrs }) {
const formRef = ref()
const formWrapperRef = ref()
const searchForm = ref<ISearchForm>({})
const selectResult = ref<ISeachFormConfig[]>([])
const componentWidth = computed(() =>
props.size === 'default' ? 200 : 220,
)
watch(
() => props.modelValue,
(val) => {
searchForm.value = val
},
{
immediate: true,
deep: true,
},
)
function initSeachForm() {
props.config.forEach((item, index) => {
if (componentDataTypeMap.has(item.type as EComponenetType)) {
const prop = item.prop || `value${index}`
if (!item.prop) {
item.prop = prop
}
}
})
}
watch(
() => props.config,
(val) => {
if (val.length) {
initSeachForm()
selectResult.value = val
}
},
{
immediate: true,
},
)
// 获取form表单的属性
const getFormAttrs = computed(() => ({
...attrs,
}))
// 获取组件项传进来的attrs
const getComponentAttrs = (item: ISeachFormConfig) => {
return item.attrs ? { ...item.attrs } : {}
}
const handleSubmit = (e: Event) => {
e.preventDefault()
}
return () => (
<div style={styles.searchForm}>
<div style={styles.searchFormHeader} class="pb-15">
{slots['header-content'] ? (
slots['header-content']()
) : (
<div style={styles.wrapper} class="mt-15">
<ElForm
ref={(el) => (formRef.value = el)}
class="w-full"
inline
model={searchForm.value}
labelPosition="left"
size={props.size}
{...{
onSubmit: handleSubmit,
}}
{...getFormAttrs.value}
>
<div ref={formWrapperRef} class="formItemWrapper w-full">
<div class="mt--18 flex align-center flex-wrap">
{selectResult.value.map((item, index) => (
<ElFormItem
key={index}
prop={item.prop}
label={item.label}
style={{ marginBottom: '0px' }}
>
{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>
</div>
</ElForm>
</div>
)}
<div style={{ display: 'flex', alignItems: 'center' }}>
{props.isSearchBtn && (
<ElButton
class="btn"
type="primary"
onClick={() => emit('search', searchForm.value)}
>
查询
</ElButton>
)}
{props.isResetBtn && (
<ElButton
class="btn"
onClick={() => {
formRef.value?.resetFields()
// emit('update:modelValue', {})
emit('reset')
}}
>
重置
</ElButton>
)}
{props.isAddBtn && (
<ElButton class="btn" type="success" onClick={() => emit('add')}>
新增
</ElButton>
)}
{props.isDeleteBtn && (
<ElButton
class="btn"
type="danger"
onClick={() => emit('delete')}
>
删除
</ElButton>
)}
</div>
{slots.ontherBtn?.()}
</div>
</div>
)
},
})
import { VNode } from 'vue'
import { VxeTableInstance } from 'vxe-table'
interface ColumnAttrs {
field?: string
title?: string
width?: string | number
[key: string]: unknown
}
export interface TableColumn {
prop: string
label: string
attrs?: ColumnAttrs
render?: {
edit?: (params: { row: TableRowData }) => VNode | VNode[] | JSX.Element
default?: (params: { row: TableRowData }) => VNode | VNode[] | JSX.Element
[key: string]:
| ((params: { row: TableRowData }) => VNode | VNode[] | JSX.Element)
| undefined
}
}
interface TableRowData {
[key: string]: unknown
}
type SlotFunction = (scope: {
row: TableRowData
}) => VNode | 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<VxeTableInstance | null>(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)
}
}
//设置高亮行
const selectRowEvent = (row: TableRowData) => {
const $table = tableRef.value
if ($table) {
$table.setCurrentRow(row)
}
}
onMounted(() => {
getList()
})
return {
tableRef,
tableData,
tableColumns,
editConfig,
getSelectEvent,
selectRowEvent,
attrs,
}
},
render() {
return (
<vxe-table
ref={(el: VxeTableInstance) => (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}
>
{{
...(() => {
// 创建基础插槽配置
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>
)
},
})
# CustomizeForm(待优化)
## CustomizeForm 属性 (Props)
| 参数 | 说明 | 类型 | 默认值 | 必填 |
| :-----------: | :------------------------------------------: | :---------------------------: | :-------: | :--: |
| config | 表单配置项 | IFormConfig[] | [] | ✓ |
| modelValue | 表单数据对象 | Record<string, unknown> | {} | ✕ |
| labelWidth | 表单标签宽度 | string \|number | 100 | ✕ |
| formItemWidth | 表单项宽度 | string \|number | 50% | ✕ |
| size | 组件尺寸 | 'large'\| 'default'\| 'small' | 'default' | ✕ |
| inline | 是否行内布局 | boolean | false | ✕ |
| labelPosition | 标签位置 | 'left'\| 'right'\| 'top' | 'right' | ✕ |
| ... | 其余属性与 element-plus 文档中 Form API 相同 | ... | ... | ... |
- **CustomizeForm 说明**:
- CustomizeForm 属性 (Props) 可自行添加,添加属性与 element-plus 文档中 form Props 相同
- 可参考文件 src\views\logistics\logisticsMethod.vue
---
### IFormConfig 配置项
| 属性 | 说明 | 类型 | 默认值 |
| :-----------: | :------------------------------------------------------: | :-----------------------------------: | :----: |
| prop | 表单字段名 | string | - |
| label | 表单标签 | string | - |
| title | 分组标题 | string | - |
| type | 控件类型 | string | - |
| attrs | 控件属性配置 | SimpleFormData | - |
| rules | 验证规则 | FormItemRule\| FormItemRule[] | - |
| render | 自定义渲染函数 | () => VNode \| VNode[] \| JSX.Element | - |
| btn | 操作按钮 | JSX.Element \|(() => JSX.Element) | - |
| isIncludeProp | 是否包含在 prop 属性中, 需搭配 refashConfig()方法使用 | boolean | - |
| fixed | 固定位置 | string | - |
- attrs 中 element-plus 文档中 FormItem API 相同
- 可参考文件 src\views\logistics\declarationRule.vue
---
### 控件类型 (SimpleFormData)
| 类型值 | 对应组件 | 说明 |
| :-------------: | :-------------: | :------------: |
| input | ElInput | 普通输入框 |
| amountInput | AmountInput | 金额输入框 |
| dateRangePicker | DateRangePicker | 日期范围选择器 |
| datePicker | DatePicker | 日期选择器 |
| select | Select | 下拉选择框 |
| switch | Switch | 开关组件 |
- form 表格如需动态渲染则需要传入计算属性 config
```tsx **config 示例**
const editForm = ref({})
const config = [
//含有title属性的对象独占一行,目前最好只做标题使用,也可以用render自定义内容但是可能有bug待优化
{
title: '个人信息',
render: () => VNode,
},
{
prop: 'name',
label: '姓名',
type: 'input',
attrs: {
placeholder: '请输入姓名',
clearable: true,
},
rules: { required: true, message: '姓名不能为空' },
},
{
prop: 'birthday',
label: '出生日期',
type: 'datePicker',
attrs: {
format: 'YYYY-MM-DD',
valueFormat: 'YYYY-MM-DD',
placeholder: '选择出生日期',
},
},
{
prop: 'gender',
label: '性别',
type: 'select',
attrs: {
options: [
{ label: '男', value: 1 },
{ label: '女', value: 2 },
{ label: '其他', value: 3 },
],
},
},
{
title: '联系信息',
},
{
prop: 'salary',
label: '期望薪资',
type: 'amountInput',
attrs: {
min: 0,
max: 100000,
precision: 0,
step: 1000,
controlsPosition: 'right',
//appendRender: amountInput组件插槽用法,需注意不可以直接使用v-model绑定
appendRender: () => {
return h(
ElSelect,
{
modelValue: editForm.value['unit'],
'onUpdate:modelValue': (val) => (editForm.value['unit'] = val),
disabled: editForm.value['id'] ? true : false,
placeholder: 'Select',
style: { width: '115px' },
},
() => [
h(ElOption, { label: 'oz', value: 'oz' }),
h(ElOption, { label: 'LB', value: 'LB' }),
],
)
},
},
},
{
prop: 'agree',
label: '用户协议',
type: 'switch',
},
]
```
---
## 事件 (Events)
| 事件名 | 说明 | 回调参数 |
| :---------------: | :----------: | :------------------: |
| update:modelValue | 表单数据更新 | 更新后的表单数据对象 |
---
## 可用方法
| 方法名 | 说明 | 参数 |
| :-----------: | :--------------------------------------------: | :-------------------: |
| validate | 表单验证 | 无 |
| clearValidate | 清除验证信息 | 无 |
| resetFields | 重置表单字段 | 无 |
| refashConfig | 刷新表单配置,需要搭配 isIncludeProp 字段 使用 | showFields?: string[] |
- **说明**: 可新增方法自行添加
- showFields 字段是一个由 config 中 设置为 isIncludeProp:true 的对象值组成的数组。例如['prop1', 'prop2']
```vue
<template>
<CustomizeForm ref="formRef" ... />
</template>
<script setup>
import { ref } from 'vue'
const formRef = ref()
// 验证表单
const validateForm = async () => {
try {
await formRef.value.validate()
// 验证通过逻辑
} catch (error) {
// 验证失败处理
}
}
// 重置表单
const resetForm = () => {
formRef.value.resetFields()
}
// 刷新配置
const refreshFormConfig = () => {
formRef.value.refashConfig(['prop1', 'prop2'])
}
</script>
```
# SearchForm(待优化)
## SearchForm 属性 (Props)
| 参数 | 说明 | 类型 | 默认值 | 必填 |
| :---------------: | :--------------------------------------------: | :---------------------------: | :-------: | :--: |
| config | 表单配置项 | ISeachFormConfig[] | [] | ✓ |
| modelValue | 表单数据对象 | ISearchForm | {} | ✕ |
| labelWidth | 表单标签宽度 | string | number | ✕ |
| size | 组件尺寸 | 'large'\| 'default' \|'small' | 'default' | ✕ |
| addPermissionName | 新增按钮权限名(目前没有作用待优化) | string \|string[] | '' | ✕ |
| isSearchBtn | 是否显示查询按钮 | boolean | true | ✕ |
| isResetBtn | 是否显示重置按钮 | boolean | true | ✕ |
| isAddBtn | 是否显示新增按钮 | boolean | true | ✕ |
| isDeleteBtn | 是否显示删除按钮 | boolean | true | ✕ |
| ... | 其余属性与 element-plus 文档中 form Props 相同 | ... | ... | ... |
- **Form 表格说明**:
- form 属性 (Props) 可自行添加
- 可参考文件 src\views\logistics\logisticsMethod.vue
---
### ISeachFormConfig 配置项
| 属性 | 说明 | 类型 | 默认值 |
| :----: | :------------: | :---------------------: | :----: |
| type | 控件类型 | EComponenetType | - |
| prop | 表单字段名 | string | - |
| label | 表单标签 | string | - |
| attrs | 控件属性配置 | Record<string, unknown> | - |
| render | 自定义渲染函数 | () => VNode | - |
- attrs 中 element-plus 文档中 FormItem API 相同
---
### 控件类型 (EComponenetType)
| 类型值 | 对应组件 | 说明 | 数据类型 |
| :-------------: | :-------------: | :------------: | :-------: |
| amountInput | AmountInput | 金额输入框 | string |
| input | ElInput | 普通输入框 | string |
| dateRangePicker | DateRangePicker | 日期范围选择器 | array |
| datePicker | DatePicker | 日期选择器 | string |
| select | Select | 下拉选择框 | undefined |
- form 表格如需动态渲染则需要传入计算属性 config
```tsx **config 示例**
const config = [
// 普通输入框
{
type: 'input',
prop: 'orderId',
label: '订单号',
attrs: {
placeholder: '请输入订单号',
clearable: true
}
},
// 下拉选择框
{
type: 'select',
prop: 'payStatus',
label: '支付状态',
attrs: {
options: [
{ label: '全部', value: '' },
{ label: '已支付', value: 1 },
{ label: '未支付', value: 0 }
],
style: 'width: 180px'
}
},
// 金额输入框
{
type: 'amountInput',
prop: 'amountRange',
label: '金额区间',
attrs: {
min: 0,
max: 10000,
precision: 2,
placeholder: '请输入金额'
}
},
// 日期范围选择
{
type: 'dateRangePicker',
prop: 'createTime',
label: '创建时间',
attrs: {
startPlaceholder: '开始日期',
endPlaceholder: '结束日期',
style: 'width: 320px'
}
},
// 自定义渲染
{
prop: 'customRender',
label: '自定义组件',
render: () => (
<div class="custom-render">
<el-switch active-text="开" inactive-text="关" />
<el-input-number :min="1" :max="10" />
</div>
)
}
]
```
---
## 事件 (Events)
| 事件名 | 说明 | 回调参数 |
| :---------------: | :----------: | :------------------: |
| update:modelValue | 表单数据更新 | 更新后的表单数据对象 |
| search | 点击查询按钮 | 无 |
| reset | 点击重置按钮 | 无 |
| add | 点击新增按钮 | 无 |
| delete | 点击删除按钮 | 无 |
---
## 插槽 (Slots)
| 插槽名 | 说明 | 作用域参数 |
| :------------: | :--------------------------: | :--------: |
| header-content | 替换整个表单头部内容 | - |
| ontherBtn | 在操作按钮区域添加额外的按钮 | - |
```vue **插槽示例**
<SearchForm :config="formConfig">
<template #header-content>
<div class="custom-header">
<h3>高级搜索面板</h3>
<p>使用以下条件进行精确搜索</p>
</div>
</template>
<template #ontherBtn>
<el-button type="warning" @click="exportData">导出数据</el-button>
</template>
</SearchForm>
```
---
## 可用方法
| 方法名 | 说明 | 参数 |
| :------------: | :------------: | :------: |
| getSelectEvent | 获取选中行数据 | 无 |
| selectRowEvent | 设置高亮行 | row: any |
- **可用方法说明**: 可自行添加
# vxeTable(待优化)
## Table 属性 (Props)
| 参数 | 说明 | 类型 | 默认值 |
| :-------------: | :---------------------------------------: | :-----------: | :----: |
| config | 表格列配置 | TableColumn[] | [] |
| tableEditConfig | 表格编辑配置 | Object | {} |
| modelValue | 表格数据 | Object[] | [] |
| getTablelist | 数据加载方法 | Function | null |
| isShowCheckBox | 是否显示复选框 | Boolean | true |
| ... | 其余属性与 vxetable 文档中 api Props 相同 | ... | ... |
- **Table 表格说明**:
- table 表格如需动态渲染则需要传入计算属性 config
- Table 属性 (Props) 可自行添加,添加属性与 vxetable 文档中 api Props 相同
- 可参考文件 src\views\logistics\logisticsMethod.vue
```tsx **config 示例**
const config = [
{
prop: 'name',
label: '姓名',
attrs: {
width: 120,
align: 'center',
fixed: 'left',
resizable: false,
className: 'status-column',
},
},
{
prop: 'status',
label: '状态',
render: {
default: ({ row }) => (
<Tag color={row.status === 1 ? 'success' : 'error'}>
{row.status === 1 ? '正常' : '禁用'}
</Tag>
),
edit: ({ row }) => (
<Select v-model={row.status}>
<Option value={1}>正常</Option>
<Option value={0}>禁用</Option>
</Select>
),
},
},
]
```
---
## TableColumn 结构
| 参数 | 说明 | 类型 |
| :----: | :------------: | :-------------: |
| prop | 列字段名 | string |
| label | 列标题 | string |
| attrs | 额外属性 | ColumnAttrs() |
| render | 自定义渲染函数 | RenderFunctions |
- **TableColumn 表格说明**:
- attrs 中属性与 vxetable 文档中 Column Props 相同
- RenderFunctions 默认使用 default,如要开启单元格编辑 edit 需在 Table 中传入 tableEditConfig.enabled=true
```ts
interface ColumnAttrs {
width?: string | number
[key: string]: unknown
}
interface RenderFunctions {
// 默认单元格渲染
default?: (params: { row: TableRowData }) => VNode | VNode[] | JSX.Element
// 编辑状态渲染
edit?: (params: { row: TableRowData }) => VNode | VNode[] | JSX.Element
// 其他自定义渲染
[key: string]:
| ((params: { row: TableRowData }) => VNode | VNode[] | JSX.Element)
| undefined
}
```
---
## 事件 (Events)
| 事件名 | 说明 | 回调参数 |
| :----------------: | :------------: | :--------------------------: |
| update:modelValue | 表格数据变化 | 表格数据数组 |
| checkbox-change | 复选框选择变化 | 当前选中行数据 |
| getCheckboxRecords | 获取选中行数据 | 当前选中行数据(含全选状态) |
---
## 可用方法
| 方法名 | 说明 | 参数 |
| :------------: | :------------: | :------: |
| getSelectEvent | 获取选中行数据 | 无 |
| selectRowEvent | 设置高亮行 | row: any |
- **说明**: 可新增方法自行添加
...@@ -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')
...@@ -121,6 +121,54 @@ const router = createRouter({ ...@@ -121,6 +121,54 @@ const router = createRouter({
component: TypeseetingManagement, component: TypeseetingManagement,
}, },
{ {
path: '/logistics/logisticsMethod',
meta: {
title: '物流方式',
},
component: () => import('@/views/logistics/logisticsMethod.vue'),
},{
path: '/logistics/logisticsCompany',
meta: {
title: '物流公司',
},
component: () => import('@/views/logistics/logisticsCompany.vue'),
},
{
path: '/logistics/shippingAddress',
meta: {
title: '发货地址',
},
component: () => import('@/views/logistics/shippingAddress.vue'),
},
{
path: '/logistics/logisticsQuotation',
meta: {
title: '物流报价',
},
component: () => import('@/views/logistics/logisticsQuotation.vue'),
},
{
path: '/logistics/declarationRule',
meta: {
title: '申报规则',
},
component: () => import('@/views/logistics/declarationRule.vue'),
},
{
path: '/logistics/logisticsPartition',
meta: {
title: '物流分区',
},
component: () => import('@/views/logistics/logisticsPartition.vue'),
},
{
path: '/logistics/logisticsCalculate',
meta: {
title: '运费试算',
},
component: () => import('@/views/logistics/logisticsCalculate.vue'),
},
{
path: '/warehouse/manage', path: '/warehouse/manage',
meta: { meta: {
title: '仓库管理', title: '仓库管理',
...@@ -147,7 +195,86 @@ const router = createRouter({ ...@@ -147,7 +195,86 @@ const router = createRouter({
title: '仓库预警', title: '仓库预警',
}, },
component: WarehouseWarning, component: WarehouseWarning,
},{ },
{
path: '/warehouse/position',
meta: {
title: '库位管理',
},
component: WarehousePosition,
},
{
path: '/logistics/logisticsMethod',
meta: {
title: '物流方式',
},
component: () => import('@/views/logistics/logisticsMethod.vue'),
},
{
path: '/logistics/shippingAddress',
meta: {
title: '发货地址',
},
component: () => import('@/views/logistics/shippingAddress.vue'),
},
{
path: '/logistics/logisticsQuotation',
meta: {
title: '物流报价',
},
component: () => import('@/views/logistics/logisticsQuotation.vue'),
},
{
path: '/logistics/declarationRule',
meta: {
title: '申报规则',
},
component: () => import('@/views/logistics/declarationRule.vue'),
},
{
path: '/logistics/logisticsPartition',
meta: {
title: '物流分区',
},
component: () => import('@/views/logistics/logisticsPartition.vue'),
},
{
path: '/logistics/logisticsCalculate',
meta: {
title: '运费试算',
},
component: () => import('@/views/logistics/logisticsCalculate.vue'),
},
{
path: '/warehouse/manage',
meta: {
title: '仓库管理',
},
component: WarehouseManage,
},
{
path: '/warehouse/receipt-doc',
meta: {
title: '入库单',
},
component: receiptDoc,
},
// {
// path: '/warehouse/issue-doc',
// meta: {
// title: '出库单',
// },
// component: issueDoc,
// },
{
path: '/warehouse/warning',
meta: {
title: '仓库预警',
},
component: WarehouseWarning,
},
{
path: '/warehouse/position', path: '/warehouse/position',
meta: { meta: {
title: '库位管理', title: '库位管理',
......
...@@ -17,6 +17,48 @@ const menu: MenuItem[] = [ ...@@ -17,6 +17,48 @@ const menu: MenuItem[] = [
// label: '商品', // label: '商品',
// }, // },
{ {
index: '4',
id: 7,
label: '物流',
children: [
{
index: '/logistics/logisticsCompany',
id: 11,
label: '物流公司',
},
{
index: '/logistics/logisticsMethod',
id: 1,
label: '物流方式',
},
{
index: '/logistics/shippingAddress',
id: 2,
label: '发货地址',
},
{
index: '/logistics/logisticsQuotation',
id: 3,
label: '物流报价',
},
{
index: '/logistics/declarationRule',
id: 4,
label: '申报规则',
},
{
index: '/logistics/logisticsPartition',
id: 5,
label: '物流分区',
},
{
index: '/logistics/logisticsCalculate',
id: 6,
label: '运费试算',
},
],
},
{
index: '13', index: '13',
id: 13, id: 13,
label: '库存', label: '库存',
...@@ -49,6 +91,7 @@ const menu: MenuItem[] = [ ...@@ -49,6 +91,7 @@ const menu: MenuItem[] = [
}, },
], ],
}, },
{ {
index: '1', index: '1',
id: 2, id: 2,
...@@ -124,6 +167,7 @@ const menu: MenuItem[] = [ ...@@ -124,6 +167,7 @@ const menu: MenuItem[] = [
}, },
], ],
}, },
// { // {
// index: '', // index: '',
// id: 3, // id: 3,
......
...@@ -2,6 +2,7 @@ export interface BaseRespData<D> { ...@@ -2,6 +2,7 @@ export interface BaseRespData<D> {
code: number code: number
message?: string message?: string
data: D data: D
total?: number
} }
export interface PaginationData<D> { export interface PaginationData<D> {
...@@ -12,6 +13,7 @@ export interface PaginationData<D> { ...@@ -12,6 +13,7 @@ export interface PaginationData<D> {
size: number size: number
current: number current: number
records: D[] records: D[]
data?: D[]
} }
export interface Statistics<D> { export interface Statistics<D> {
sumNotPassNum: number sumNotPassNum: number
......
...@@ -79,6 +79,7 @@ export interface ProductList { ...@@ -79,6 +79,7 @@ export interface ProductList {
passNum?: number passNum?: number
notPassNum?: number notPassNum?: number
payAmount?: number payAmount?: number
status?: number
weight?: number | null weight?: number | null
diyId?: string diyId?: string
endProductId?: string endProductId?: string
......
/*
* @Author: Try
* @Date: 2023-10-17 18:25:26
* @LastEditTime: 2024-06-04 14:02:19
* @LastEditors: Seven
* @FilePath: \shulong_tenant_pc\src\components\SearchForm\type\index.ts
* @Description: 头部注释配置模板
*/
export interface ISearchForm {
[key: string]: any
}
export interface IOptions {
[key: string]: any
}
export enum EComponenetType {
amountInput = 'amountInput', // 金额输入框
input = 'input', // 普通输入框
dateRangePicker = 'dateRangePicker', // 时间范围选择器
datePicker = 'datePicker', // 单个日期选择器
select = 'select', // 普通下拉选择框
}
// EComponenetType 枚举转联合
export type TComponenetType = `${EComponenetType}`
export interface IAttrs {
options?: IOptions
hasAllOptions?: boolean // 使用全部选项
value?: string
label?: string
placeholder?: string
[key: string]: any
}
export interface ISlots{
formItemSlot?: boolean
}
export interface ISeachFormConfig {
prop: string
type?: EComponenetType | TComponenetType
label?: string
defaultValue?: string
attrs?: IAttrs
labelWidth?: number | string
slot?: ISlots
[key: string]: any
}
export interface IKeyBoolean {
[key: string]: boolean
}
export enum EDataType {
array = 'array',
string = 'string',
number = 'number',
boolean = 'boolean',
object = 'object',
undefined = 'undefined',
}
// 下载导入模板的配置
export interface IDownloadXlsConfig {
filename: string
url: string
}
/**
* @description 判读是否为外链
* @param path
* @returns {boolean}
*/
export function isExternal(path: string) {
return /^(https?:|mailto:|tel:|\/\/)/.test(path)
}
/**
* @description 判断是否为数字
* @param value
* @returns {boolean}
*/
export function isNumber(value: string) {
const reg = /^[0-9]+(\.[0-9]+)?$/
return reg.test(value)
}
/**
* @description 判断是否是名称
* @param value
* @returns {boolean}
*/
export function isName(value: string) {
const reg = /^[\u4e00-\u9fa5a-zA-Z0-9]+$/
return reg.test(value)
}
/**
* @description 判断是否为IP
* @param ip
* @returns {boolean}
*/
export function isIP(ip: string) {
const reg =
/^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/
return reg.test(ip)
}
/**
* @description 判断是否是传统网站
* @param url
* @returns {boolean}
*/
export function isUrl(url: string) {
const reg =
/^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/
return reg.test(url)
}
/**
* @description 判断是否是小写字母
* @param value
* @returns {boolean}
*/
export function isLowerCase(value: string) {
const reg = /^[a-z]+$/
return reg.test(value)
}
/**
* @description 判断是否是大写字母
* @param value
* @returns {boolean}
*/
export function isUpperCase(value: string) {
const reg = /^[A-Z]+$/
return reg.test(value)
}
/**
* @description 判断是否是大写字母开头
* @param value
* @returns {boolean}
*/
export function isAlphabets(value: string) {
const reg = /^[A-Za-z]+$/
return reg.test(value)
}
/**
* @description 判断是否是字符串
* @param value
* @returns {boolean}
*/
export function isString(value: unknown) {
return typeof value === 'string' || value instanceof String
}
/**
* @description 判断是否是数组
* @param arg
*/
export function isArray(arg: string | (string | number)[]) {
if (typeof Array.isArray === 'undefined') {
return Object.prototype.toString.call(arg) === '[object Array]'
}
return Array.isArray(arg)
}
/**
* @description 判断是否是对象
* @param arg
*/
export function isObject(arg: unknown) {
return Object.prototype.toString.call(arg) === '[object Object]'
}
/**
* @description 判断是否是端口号
* @param value
* @returns {boolean}
*/
export function isPort(value: string) {
const reg =
/^([0-9]|[1-9]\d|[1-9]\d{2}|[1-9]\d{3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$/
return reg.test(value)
}
/**
* @description 判断是否是手机号
* @param value
* @returns {boolean}
*/
export function isPhone(value: string) {
const reg = /^1[3-9]\d{9}$/
return reg.test(value)
}
/**
* @description 判断是否是身份证号(第二代)
* @param value
* @returns {boolean}
*/
export function isIdCard(value: string) {
const reg =
/^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/
return reg.test(value)
}
/**
* @description 判断是否是邮箱
* @param value
* @returns {boolean}
*/
export function isEmail(value: string) {
const reg = /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/
return reg.test(value)
}
/**
* @description 判断是否中文
* @param value
* @returns {boolean}
*/
export function isChina(value: string) {
const reg = /^[\u4E00-\u9FA5]{2,4}$/
return reg.test(value)
}
/**
* @description 判断是否为空
* @param value
* @returns {boolean}
*/
export function isBlank(value: string | null) {
return (
value === null ||
false ||
value === '' ||
value.trim() === '' ||
value.toLocaleLowerCase().trim() === 'null'
)
}
/**
* @description 判断是否为固话
* @param value
* @returns {boolean}
*/
export function isTel(value: string) {
const reg =
/^(400|800)([0-9\\-]{7,10})|(([0-9]{4}|[0-9]{3})([- ])?)?([0-9]{7,8})(([- 转])*([0-9]{1,4}))?$/
return reg.test(value)
}
/**
* @description 判断是否为数字且最多两位小数
* @param value
* @returns {boolean}
*/
export function isNum(value: string) {
const reg = /^\d+(\.\d{1,2})?$/
return reg.test(value)
}
/**
* @description 判断是否为数字且最多两位小数
* @param value
* @returns {boolean}
*/
export function isNumFloat(value: string) {
const reg = /^(0|([1-9][0-9]*))(\.[\d]+)?$/
return reg.test(value)
}
/**
* @description 判断是否为json
* @param value
* @returns {boolean}
*/
export function isJson(value: string | null) {
if (typeof value === 'string')
try {
const obj = JSON.parse(value)
return !!(typeof obj === 'object' && obj)
} catch (e) {
return false
}
return false
}
const decimal4Regex = /^\d+(\.\d{1,4})?$/ // 4位小数
const decimal2Regex = /^\d+(\.\d{1,2})?$/ // 2位小数
/**
* @description 判断是否超过小数点4位
* @param value
* @returns {boolean}
*/
export function isDecimal4(value: string) {
return decimal4Regex.test(value)
}
/**
* @description 判断是否超过小数点2位
* @param value
* @returns {boolean}
*/
export function isDecimal2(value: string) {
return decimal2Regex.test(value)
}
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<InstanceType<typeof ElDialog> | null>(null)
const isShow = ref(false)
watch(
() => props.modelValue,
(val) => {
isShow.value = val
},
{ immediate: true },
)
return () => {
return (
<ElDialog
ref={formRef}
v-model={isShow.value}
title={props.title}
width={props.dialogWidth}
onClose={() => {
emit('close')
}}
destroy-on-close={true}
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">
<SearchForm
:config="searchConfig"
:isSearchBtn="false"
:isAddBtn="false"
:isDeleteBtn="false"
v-model="searchForm"
>
<template #ontherBtn>
<div style="margin-left: 10px; display: flex; gap: 10px">
<ElButton @click="search" type="primary">计算</ElButton>
</div>
</template>
</SearchForm>
</div>
<div
v-loading="loading"
class="user-content flex-1 flex-column overflow-hidden"
>
<div class="user-list flex-1 overflow-hidden">
<CustomizeTable
ref="tableRef"
v-model="tableData"
:config="tableConfig"
:merge-cells="mergeCells"
highlight-current-row
:isShowCheckBox="false"
border="full"
></CustomizeTable>
</div>
</div>
</div>
</template>
<script setup lang="tsx">
defineOptions({
name: 'LogisticsCalculate',
})
import { getLogisticsTrialCalculation } from '@/api/logistics'
import SearchForm from '@/components/SearchForm.tsx'
import CustomizeTable from '@/components/VxeTable.tsx'
import type { VxeTablePropTypes } from 'vxe-table'
import { useValue } from './hooks/useValue'
import { ISeachFormConfig } from '@/types/searchType'
import { TableColumn } from '@/components/VxeTable'
const [searchForm] = useValue({ code: '', weight: '' })
const tableRef = ref<InstanceType<typeof CustomizeTable> | null>(null)
const mergeCells = ref<VxeTablePropTypes.MergeCells>([])
const tableData = ref([])
const searchConfig = ref<ISeachFormConfig[]>([
{
prop: 'code',
type: 'input',
label: '邮编',
attrs: {
clearable: true,
placeholder: '请输入邮编',
},
},
{
prop: 'weight',
type: 'amountInput',
label: '重量',
attrs: {
clearable: true,
placeholder: '请输入重量',
hasUnit: true,
suffix: 'g',
},
},
])
const tableConfig = ref<TableColumn[]>([
{
prop: 'logisticsWayName',
label: '物流名称',
attrs: {
align: 'center',
width: 100,
},
},
{
prop: 'warehouseName',
label: '发货仓库',
},
{
prop: 'logisticsWayCode',
label: '物流编码',
},
{
prop: 'partition',
label: '所在分区',
},
{
prop: 'status',
label: '状态',
render: {
default: ({ row }) => {
return (
<div style={{ color: row.status ? '#67c23a' : '#f56c6c' }}>
{row.status ? '成功' : '失败'}
</div>
)
},
},
},
{
prop: 'payFreight',
label: '预计运费($)',
},
])
/**
* @description: 计算按钮
*/
async function search() {
if (!searchForm.value.code) {
ElMessage.warning('请输入邮编')
return
}
if (!searchForm.value.weight) {
ElMessage.warning('请输入重量')
return
}
await getList(searchForm.value)
const itemIndex = tableData.value.findIndex(
(item: { status: boolean }) => item.status,
)
console.log(123, itemIndex)
if (itemIndex !== -1) {
nextTick(() => {
tableRef.value?.selectRowEvent(tableData.value[itemIndex])
})
}
}
/**
* @description: 获取列表数据
*/
const loading = ref(false)
async function getList(data?: { code: string; weight: string }) {
loading.value = true
try {
const res = await getLogisticsTrialCalculation({
...data,
})
tableData.value = [...res.data]
} catch (error) {
console.log(error)
} finally {
loading.value = false
}
}
</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>
export interface DeclarationRuleList {
countries: string
createTime: string
currency: string
defaulted: string
fixedValue: string
fixedWeight: string
id: number
name: string
orderPercent: number
remark: string
shops: string
type: number
valueUp: number
wayIds: string
wayNames: string
weightPercent: number
weightUp: number
}
export interface AddDeclarationRuleObj {
currency?: string
fixedValue?: string
fixedWeight?: string
id?: number
limitAmountType?: string
limitWeightType?: string
name?: string
orderPercent?: number | string | null
remark?: string
shops?: string
type?: number
valueUp?: number | string | null
weightPercent?: number | string | null
weightUp?: number | string | null
}
export interface LogisticsMethod {
id?: number | string
name?: string
warehouseId?: number
warehouseName?: string
companyId?: number
company?: string
serviceCode?: string
siteUrl?: string
status: number | string
platformList: platformObj[]
ruleRef: ruleRefObj
ruleId?: string | number
ruleList?: ruleRefObj[]
uinuinWarehouseId?: number | string
}
export interface LogisticsMethodList {
name?: string
status?: number | string
serviceCode?: number | string
pageSize: number | string
currentPage: number | string
}
export interface UpdateLogisticsMethodStatus {
id?: number | string
status?: number | string
}
export interface LogisticsResponse {
code: number
data: {
total: number
size: number
current: number
records: LogisticsMethod[]
}
message: string
}
export interface platformObj {
platform: string
logisticsName: string | number
showPlatform: (string | number)[]
}
interface ruleRefObj {
ruleId: string | number
ruleName: string | number
}
export interface LogisticsPartitionObj {
zoneName?: string
logistics?: string
codePrefix?: string
logisticsId?: string
}
export interface LogisticsQuotation {
factoryId?: number
id?: number
logistics?: string
logisticsId?: number
rate?: number
rateG?: number
rateKg?: number
rateType?: string
seq?: number
unit?: string
zone1?: string
zone2?: string
zone3?: string
zone4?: string
zone5?: string
zone6?: string
zone7?: string
zone8?: string
zone9?: string
logisticsQuotationList?: LogisticsQuotation[]
}
export interface ShippingAddressObj {
addressLine1?: string
addressLine2?: string
addressLine3?: string
city?: string
cityCode?: string
countryCode?: string
countryName?: string
createTime?: string
cspAccount?: string
district?: string
districtCode?: string
factoryId?: number
id?: number
phoneNumber?: string
postalCode?: string
rfcTaxId?: string
shipperName?: string
stateProvince?: string
stateProvinceAbbr?: string
swDefault?: boolean
updateTime?: string
checked?: boolean
}
...@@ -971,7 +971,7 @@ ...@@ -971,7 +971,7 @@
</el-dialog> </el-dialog>
<right-menu <right-menu
ref="rightMenuRef" ref="rightMenuRef"
:show_copy_shop_number=" :show-copy-shop-number="
['IN_PRODUCTION', 'TO_BE_CONFIRMED', 'WAIT_SHIPMENT'].includes(status) ['IN_PRODUCTION', 'TO_BE_CONFIRMED', 'WAIT_SHIPMENT'].includes(status)
" "
@change="rightChange" @change="rightChange"
......
...@@ -7,8 +7,8 @@ ...@@ -7,8 +7,8 @@
<button @click="$emit('change', 'clear_check')">取消选择</button> <button @click="$emit('change', 'clear_check')">取消选择</button>
<button @click="$emit('change', 'copy_code')">复制选中生产单号</button> <button @click="$emit('change', 'copy_code')">复制选中生产单号</button>
<button v-if="show_copy_shop_number" @click="$emit('change', 'copy_shopNumber')">复制选中店铺单号</button> <button v-if="showCopyShopNumber" @click="$emit('change', 'copy_shopNumber')">复制选中店铺单号</button>
<button v-if="show_copy_shop_number" @click="$emit('change', 'count')">统计数量</button> <button v-if="showCopyShopNumber" @click="$emit('change', 'count')">统计数量</button>
</div> </div>
<!-- </div> --> <!-- </div> -->
</template> </template>
...@@ -20,7 +20,7 @@ interface E{ ...@@ -20,7 +20,7 @@ interface E{
y:number y:number
} }
defineProps({ defineProps({
show_copy_shop_number:{ showCopyShopNumber:{
type:Boolean, type:Boolean,
default:true default:true
} }
......
...@@ -566,7 +566,6 @@ getWarehouse() ...@@ -566,7 +566,6 @@ getWarehouse()
::v-deep(.el-tabs) { ::v-deep(.el-tabs) {
height: 100%; height: 100%;
display: flex; display: flex;
flex-direction: column;
.el-tabs__content { .el-tabs__content {
flex: 1; flex: 1;
......
...@@ -3,13 +3,16 @@ import AutoImport from 'unplugin-auto-import/vite' ...@@ -3,13 +3,16 @@ import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite' import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers' import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
import vue from '@vitejs/plugin-vue' import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default defineConfig({ export default defineConfig({
base: '/', base: '/',
plugins: [ plugins: [
vue(), vue(),
vueJsx(),
AutoImport({ AutoImport({
imports: ['vue', 'vue-router', 'pinia'],
resolvers: [ElementPlusResolver()], resolvers: [ElementPlusResolver()],
}), }),
Components({ Components({
......
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