You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

321 lines
11 KiB
Vue

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<div>
<el-dialog v-model="dialog.visible" :title="'礼品选择'" width="80%" top="5vh" append-to-body destroy-on-close>
<el-row :gutter="20">
<el-col :span="24">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="100px">
<el-form-item label="礼品编码" prop="giftCode">
<el-input v-model="queryParams.giftCode" placeholder="请输入礼品编码" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="礼品名称" prop="giftName">
<el-input v-model="queryParams.giftName" placeholder="请输入礼品名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="规格型号" prop="specification">
<el-input v-model="queryParams.specification" placeholder="请输入规格型号" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="价格区间" prop="priceRange">
<div class="flex items-center" style="width: 260px">
<el-slider v-model="sliderRange" range :min="priceMin" :max="priceMax" :step="priceStep" :marks="priceMarks" style="flex: 1" />
<div class="ml-3 text-sm text-gray-600">{{ sliderRange[0] }} ~ {{ sliderRange[1] }}</div>
</div>
</el-form-item>
<el-form-item label="仅显示有库存" prop="hasStock">
<el-switch v-model="queryParams.hasStock" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="() => resetQuery()">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="hover">
<template v-if="prop.multiple" #header>
<el-tag v-for="gift in selectGiftList" :key="gift.giftId" closable style="margin: 2px" @close="handleCloseTag(gift)">
{{ gift.giftName }}
</el-tag>
</template>
<vxe-table
ref="tableRef"
height="420px"
border
show-overflow
:data="giftList"
:loading="loading"
:row-config="{ keyField: 'giftId', isHover: true }"
:checkbox-config="{ reserve: true, trigger: 'row', highlight: true, showHeader: prop.multiple }"
@checkbox-all="handleCheckboxAll"
@checkbox-change="handleCheckboxChange"
>
<vxe-column type="checkbox" width="48" align="center" />
<vxe-column key="giftCode" title="礼品编码" align="center" field="giftCode" width="108" show-overflow />
<vxe-column key="giftName" title="礼品名称" align="center" field="giftName" min-width="160" show-overflow />
<vxe-column key="specification" title="规格型号" align="center" field="specification" min-width="240" show-overflow="tooltip" />
<vxe-column key="unitPrice" title="采购单价(元)" align="center" field="unitPrice" width="116">
<template #default="scope">{{ scope.row.unitPrice ? '¥' + scope.row.unitPrice : '-' }}</template>
</vxe-column>
<vxe-column key="inventoryQuantity" title="库存数量" align="center" field="inventoryQuantity" width="96">
<template #default="scope">
<el-tag :type="inventoryTagType(scope.row.inventoryQuantity)" disable-transitions>{{ scope.row.inventoryQuantity ?? '-' }}</el-tag>
</template>
</vxe-column>
<vxe-column key="remark" title="备注" align="center" field="remark" min-width="132" show-overflow />
</vxe-table>
<pagination
v-show="total > 0"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
:total="total"
@pagination="pageList"
/>
</el-card>
</el-col>
</el-row>
<template #footer>
<el-button @click="close">取消</el-button>
<el-button type="primary" @click="confirm">确定</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { listCrmGiftInfo } from '@/api/oa/crm/crmGiftInfo';
import { CrmGiftInfoQuery, CrmGiftInfoVO } from '@/api/oa/crm/crmGiftInfo/types';
import { VxeTableInstance } from 'vxe-table';
import { computed, getCurrentInstance, nextTick, onMounted, reactive, ref, watch } from 'vue';
import type { FormInstance } from 'element-plus';
interface PropType {
modelValue?: CrmGiftInfoVO[] | CrmGiftInfoVO | undefined;
multiple?: boolean;
data?: string | number | (string | number)[] | undefined;
maxQuantity?: 'stock' | number;
}
const prop = withDefaults(defineProps<PropType>(), {
multiple: true,
modelValue: undefined,
data: undefined,
maxQuantity: 'stock'
});
const emit = defineEmits(['update:modelValue', 'confirmCallBack']);
// 获取全局proxy包含$modal、animate等全局挂载能力
const proxy = (getCurrentInstance()?.proxy || {}) as any;
const giftList = ref<CrmGiftInfoVO[]>([]);
const loading = ref(true);
const showSearch = ref(true);
const total = ref(0);
const selectGiftList = ref<CrmGiftInfoVO[]>([]);
const priceMin = 0;
const priceMax = 10000;
const priceStep = 10;
const priceMarks = {
0: '0',
2000: '2000',
5000: '5000',
8000: '8000',
10000: '10000'
};
const sliderRange = ref<[number, number]>([priceMin, priceMax]);
const queryFormRef = ref<FormInstance>();
const tableRef = ref<VxeTableInstance<CrmGiftInfoVO>>();
const dialog = reactive({
visible: false
});
// 使用reactive便于模板直接双向绑定并显式包含新增筛选字段
const queryParams = reactive<
CrmGiftInfoQuery & {
hasStock?: boolean;
unitPriceMin?: number;
unitPriceMax?: number;
}
>({
pageNum: 1,
pageSize: 10,
giftCode: undefined,
giftName: undefined,
specification: undefined,
unitPriceMin: undefined,
unitPriceMax: undefined,
hasStock: undefined,
params: {}
});
const defaultIds = computed(() => computedIds(prop.data));
const computedIds = (data: any) => {
if (data === '' || data === null || data === undefined) {
return [] as string[];
}
if (data instanceof Array) {
return data.map((item) => String(item));
} else if (typeof data === 'string') {
return data.split(',');
} else if (typeof data === 'number') {
return [String(data)];
} else {
console.warn('<GiftSelect> data type should be array or string or number');
return [];
}
};
/** 查询礼品列表 */
const getList = async () => {
loading.value = true;
const res = await listCrmGiftInfo(queryParams);
giftList.value = res.rows || [];
total.value = res.total || 0;
loading.value = false;
};
const pageList = async () => {
await getList();
const rows = giftList.value.filter((item) => selectGiftList.value.some((g) => String(g.giftId) === String(item.giftId)));
await nextTick(() => tableRef.value?.setCheckboxRow(rows, true));
};
/** 搜索按钮 */
const handleQuery = () => {
queryParams.unitPriceMin = sliderRange.value[0];
queryParams.unitPriceMax = sliderRange.value[1];
queryParams.pageNum = 1;
getList();
};
/** 重置按钮 */
const resetQuery = (refresh = true) => {
queryFormRef.value?.resetFields();
sliderRange.value = [priceMin, priceMax];
queryParams.unitPriceMin = undefined;
queryParams.unitPriceMax = undefined;
queryParams.pageNum = 1;
refresh && handleQuery();
};
const handleCheckboxChange = (checked: any) => {
const row = checked.row as CrmGiftInfoVO;
if (checked.checked) {
// 库存校验
if (prop.maxQuantity === 'stock' && (!row.inventoryQuantity || row.inventoryQuantity <= 0)) {
proxy?.$modal.msgWarning('库存为0的礼品不可选择');
tableRef.value?.setCheckboxRow(row, false);
return;
}
if (!prop.multiple) {
tableRef.value?.setCheckboxRow(selectGiftList.value, false);
selectGiftList.value = [];
}
selectGiftList.value.push(row);
} else {
selectGiftList.value = selectGiftList.value.filter((item) => String(item.giftId) !== String(row.giftId));
}
};
const handleCheckboxAll = (checked: any) => {
const rows = giftList.value;
if (checked.checked) {
rows.forEach((row) => {
if (prop.maxQuantity === 'stock' && (!row.inventoryQuantity || row.inventoryQuantity <= 0)) {
return;
}
if (!selectGiftList.value.some((item) => String(item.giftId) === String(row.giftId))) {
selectGiftList.value.push(row);
}
});
} else {
selectGiftList.value = selectGiftList.value.filter((item) => !rows.some((row) => String(row.giftId) === String(item.giftId)));
}
};
const handleCloseTag = (gift: CrmGiftInfoVO) => {
const rowKey = String(gift.giftId);
const rows = selectGiftList.value.filter((item) => String(item.giftId) === rowKey);
tableRef.value?.setCheckboxRow(rows, false);
selectGiftList.value = selectGiftList.value.filter((item) => String(item.giftId) !== rowKey);
};
const initSelectGift = async () => {
if (defaultIds.value.length > 0) {
const rows = giftList.value.filter((item) => defaultIds.value.includes(String(item.giftId)));
selectGiftList.value = rows;
await nextTick(() => tableRef.value?.setCheckboxRow(rows, true));
} else if (prop.modelValue) {
const mv = prop.modelValue instanceof Array ? prop.modelValue : [prop.modelValue];
selectGiftList.value = mv;
await nextTick(() => tableRef.value?.setCheckboxRow(mv, true));
}
};
const inventoryTagType = (qty?: number) => {
if (qty === undefined || qty === null) return 'info';
if (qty <= 0) return 'danger';
if (qty <= 10) return 'warning';
return 'success';
};
const confirm = () => {
// 确认前再次校验库存
if (prop.maxQuantity === 'stock') {
const invalid = selectGiftList.value.find((item) => !item.inventoryQuantity || item.inventoryQuantity <= 0);
if (invalid) {
proxy?.$modal.msgWarning('存在库存为0的礼品无法确认');
return;
}
}
emit('update:modelValue', selectGiftList.value);
emit('confirmCallBack', selectGiftList.value);
dialog.visible = false;
};
const open = async () => {
dialog.visible = true;
};
const close = () => {
dialog.visible = false;
};
watch(
() => dialog.visible,
async (val) => {
if (val) {
await getList();
await initSelectGift();
} else {
tableRef.value?.clearCheckboxReserve();
tableRef.value?.clearCheckboxRow();
resetQuery(false);
selectGiftList.value = [];
}
}
);
const expose = {
open,
close
};
onMounted(async () => {
// 预加载列表,避免首次打开白屏
await getList();
});
defineExpose(expose);
</script>
<style scoped></style>