!560 【新增功能】添加扫描枪、门店店员绑定功能

Merge pull request !560 from 痴货/jh-smq
pull/561/MERGE
芋道源码 2024-10-13 00:49:54 +00:00 committed by Gitee
commit ca9a389ae8
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
6 changed files with 573 additions and 12 deletions

View File

@ -44,3 +44,13 @@ export const updateDeliveryPickUpStore = async (data: DeliveryPickUpStoreVO) =>
export const deleteDeliveryPickUpStore = async (id: number) => { export const deleteDeliveryPickUpStore = async (id: number) => {
return await request.delete({ url: '/trade/delivery/pick-up-store/delete?id=' + id }) return await request.delete({ url: '/trade/delivery/pick-up-store/delete?id=' + id })
} }
//绑定自提店员
export const bindStoreStaffId = async (data: any) => {
return await request.post({ url: '/trade/delivery/pick-up-store/bind', data })
}
//查询门店绑定情况
export const getDeliveryPickUpStoreStaff = async (id: number) => {
return await request.get({ url: '/trade/delivery/pick-up-store/get-store-staff?id=' + id })
}

View File

@ -26,9 +26,8 @@
<el-select <el-select
v-model="queryParams.pickUpStoreId" v-model="queryParams.pickUpStoreId"
class="!w-280px" class="!w-280px"
clearable
multiple
placeholder="全部" placeholder="全部"
@change="handleQuery"
> >
<el-option <el-option
v-for="item in pickUpStoreList" v-for="item in pickUpStoreList"
@ -73,10 +72,16 @@
<Icon class="mr-5px" icon="ep:refresh" /> <Icon class="mr-5px" icon="ep:refresh" />
重置 重置
</el-button> </el-button>
<el-button @click="handlePickup" type="success" plain v-hasPermi="['trade:order:pick-up']"> <el-button @click="handlePickup" type="success" plain v-hasPermi="['trade:order:pick-up']" :disabled="isUse">
<Icon class="mr-5px" icon="ep:check" /> <Icon class="mr-5px" icon="ep:check" />
核销 核销
</el-button> </el-button>
<el-button type="primary" @click="connectToSerialPort" :disabled="serialPort || isUse">
连接扫描枪
</el-button>
<el-button type="danger" @click="cutPort" :disabled="!serialPort || isUse">
断开扫描枪
</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
</ContentWrap> </ContentWrap>
@ -216,6 +221,12 @@ import { DeliveryTypeEnum } from '@/utils/constants'
import { TradeOrderSummaryRespVO } from '@/api/mall/trade/order' import { TradeOrderSummaryRespVO } from '@/api/mall/trade/order'
import { DeliveryPickUpStoreVO } from '@/api/mall/trade/delivery/pickUpStore' import { DeliveryPickUpStoreVO } from '@/api/mall/trade/delivery/pickUpStore'
import OrderPickUpForm from '@/views/mall/trade/order/form/OrderPickUpForm.vue' import OrderPickUpForm from '@/views/mall/trade/order/form/OrderPickUpForm.vue'
import { ref, onMounted } from "vue";
const message = useMessage() //
const port = ref("");
const ports = ref([]);
const reader = ref("");
defineOptions({ name: 'PickUpOrder' }) defineOptions({ name: 'PickUpOrder' })
@ -227,6 +238,8 @@ const total = ref(2)
const list = ref<TradeOrderApi.OrderVO[]>([]) const list = ref<TradeOrderApi.OrderVO[]>([])
// //
const queryFormRef = ref<FormInstance>() const queryFormRef = ref<FormInstance>()
const serialPort = ref(false)
const isUse = ref(true)
// //
const INIT_QUERY_PARAMS = { const INIT_QUERY_PARAMS = {
// //
@ -240,6 +253,7 @@ const INIT_QUERY_PARAMS = {
// //
pickUpStoreId: undefined pickUpStoreId: undefined
} }
// //
const queryParams = ref({ ...INIT_QUERY_PARAMS }) const queryParams = ref({ ...INIT_QUERY_PARAMS })
// queryParam // queryParam
@ -294,6 +308,9 @@ const handleQuery = async () => {
const resetQuery = () => { const resetQuery = () => {
queryFormRef.value?.resetFields() queryFormRef.value?.resetFields()
queryParams.value = { ...INIT_QUERY_PARAMS } queryParams.value = { ...INIT_QUERY_PARAMS }
if(pickUpStoreList.value.length > 0) {
queryParams.value.pickUpStoreId = pickUpStoreList.value[0].id
}
handleQuery() handleQuery()
} }
@ -309,10 +326,89 @@ const handlePickup = () => {
pickUpForm.value.open() pickUpForm.value.open()
} }
/** 连接扫码枪 */
const connectToSerialPort = async () => {
try {
//
if ("serial" in navigator && navigator.serial != null && typeof navigator.serial === 'object' && "requestPort" in navigator.serial) {
//
port.value = await navigator.serial.requestPort();
} else {
message.error("浏览器不支持扫码枪连接,请更换浏览器重试")
return
}
// 访
ports.value = await navigator.serial.getPorts();
// console.log(port.value, ports.value);
console.log(port.value);
//
await port.value.open({ baudRate: 9600 , dataBits: 8 , stopBits: 2});
// console.log(typeof port.value);
message.success("成功连接扫码枪")
serialPort.value = true;
// readData(port.value);
readData();
} catch (error) {
//
console.log("Error connecting to serial port:", error);
}
};
/** 监听扫码枪输入 */
const readData = async () => {
reader.value = port.value.readable.getReader();
let data = ""; //
//
while (true) {
const { value, done } = await reader.value.read();
if (done) {
//
reader.value.releaseLock();
break;
}
//
const serialData = new TextDecoder().decode(value);
data = `${data}${serialData}`;
if (serialData.includes("\r")) {
//
let codeData = data.replace("\r","");
data = ""; //
console.log(`二维码数据:${codeData}`);
//
pickUpForm.value.open(codeData)
}
}
};
/** 断开扫码枪 */
const cutPort = async () => {
if (port.value !== "") {
await reader.value.cancel();
await port.value.close();
port.value = "";
console.log("断开扫码枪连接");
message.success("已成功断开扫码枪连接")
serialPort.value = false;
} else {
message.warning("请先连接或打开扫码枪")
}
};
/** 初始化 **/ /** 初始化 **/
onMounted(() => { onMounted(async () => {
getList() await getPickUpStoreList()
getPickUpStoreList() if(pickUpStoreList.value.length > 0){
queryParams.value.pickUpStoreId = pickUpStoreList.value[0].id
isUse.value = false
await getList()
}else{
message.error("当前登录人没绑定任何自提点")
loading.value = false
isUse.value = true
}
}) })
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -0,0 +1,161 @@
<template>
<Dialog :title="dialogTitle" v-model="dialogVisible" width="20%">
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="120px"
v-loading="formLoading"
>
<el-row>
<el-col :span="24">
<el-form-item label="门店名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入门店名称" :disabled="true"/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="门店店员" prop="storeStaffIds">
<el-button type="primary" @click="storeStaffTableSelect.open()"></el-button>
</el-form-item>
<!-- 店员列表 -->
<ContentWrap v-if="usersList.length > 0">
<el-table :data="usersList">
<el-table-column label="编号" align="center" prop="id" />
<el-table-column
label="用户昵称"
align="center"
prop="nickname"
:show-overflow-tooltip="true"
/>
<el-table-column label="状态" align="center" key="status">
<template #default="scope">
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column align="center" label="操作">
<template #default="scope">
<el-button
v-hasPermi="['trade:delivery:pick-up-store:delete']"
link
type="danger"
@click="handleDelete(scope.row.id)"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
</ContentWrap>
</el-col>
</el-row>
</el-form>
<template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
<StoreStaffTableSelect ref="storeStaffTableSelect" @change="changeStoreStaff"/>
</template>
<script setup lang="ts">
import * as DeliveryPickUpStoreApi from '@/api/mall/trade/delivery/pickUpStore'
import StoreStaffTableSelect from './components/StoreStaffTableSelect.vue'
import {DICT_TYPE} from "@/utils/dict";
const message = useMessage() //
const { t } = useI18n() //
const dialogVisible = ref(false) //
const dialogTitle = ref('') //
const formLoading = ref(false) // 12
const formData = ref({
id: undefined,
name: '',
storeStaffIds: [],
storeStaffs: [],
})
const formRules = reactive({
name: [{ required: true, message: '门店名称不能为空', trigger: 'blur' }],
})
const formRef = ref() // Ref
const storeStaffTableSelect = ref() // Ref
/** 打开弹窗 */
const open = async (id: number) => {
dialogVisible.value = true
dialogTitle.value = '绑定自提门店员工'
resetForm()
formLoading.value = true
try {
await getList(id)
} finally {
formLoading.value = false
}
}
defineExpose({ open }) // open
/** 提交表单 */
const submitForm = async () => {
formData.value.storeStaffIds = usersList.value.map(item => item.id) as [];
//
if (!formRef) return
const valid = await formRef.value.validate()
if (!valid) return
//
formLoading.value = true
try {
const data = formData.value
await DeliveryPickUpStoreApi.bindStoreStaffId(data)
message.success("绑定成功")
dialogVisible.value = false
} finally {
formLoading.value = false
}
}
/** 删除按钮操作 */
const handleDelete = async (id: number) => {
try {
//
await message.delConfirm()
//
const index = usersList.value.findIndex(item => {
if (item.id == id){
return true;
}
})
usersList.value.splice(index, 1);
//await DeliveryPickUpStoreStaffApi.deleteDeliveryPickUpStoreStaff(id,formData.value.id)
message.success(t('common.delSuccess'))
//
//await getList(formData.value.id)
} catch {}
}
/**
* 查询自提点员工绑定关系
*/
const getList = async (id: number) => {
formData.value = await DeliveryPickUpStoreApi.getDeliveryPickUpStoreStaff(id)
if(formData.value.storeStaffs){
usersList.value = formData.value.storeStaffs;
}
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: undefined,
name: '',
storeStaffIds: [],
storeStaffs: [],
}
formRef.value?.resetFields()
}
const usersList = ref([])
const changeStoreStaff = (checkedUsers : []) => {
usersList.value = checkedUsers
}
</script>

View File

@ -0,0 +1,270 @@
<template>
<Dialog :title="dialogTitle" v-model="dialogVisible" width="60%">
<el-row :gutter="20">
<!-- 左侧部门树 -->
<el-col :span="4" :xs="24">
<ContentWrap class="h-1/1">
<DeptTree @node-click="handleDeptNodeClick" />
</ContentWrap>
</el-col>
<el-col :span="20" :xs="24">
<!-- 搜索 -->
<ContentWrap>
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="68px"
>
<el-form-item label="用户名称" prop="username">
<el-input
v-model="queryParams.username"
placeholder="请输入用户名称"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="手机号码" prop="mobile">
<el-input
v-model="queryParams.mobile"
placeholder="请输入手机号码"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select
v-model="queryParams.status"
placeholder="用户状态"
clearable
class="!w-240px"
>
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker
v-model="queryParams.createTime"
value-format="YYYY-MM-DD HH:mm:ss"
type="datetimerange"
start-placeholder="开始日期"
end-placeholder="结束日期"
class="!w-240px"
/>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" />搜索</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" />重置</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<ContentWrap>
<el-table v-loading="loading" :data="list">
<el-table-column width="55">
<template #header>
<el-checkbox
v-model="isCheckAll"
:indeterminate="isIndeterminate"
@change="handleCheckAll"
/>
</template>
<template #default="{ row }">
<el-checkbox
v-model="checkedStatus[row.id]"
@change="(checked: boolean) => handleCheckOne(checked, row, true)"
/>
</template>
</el-table-column>
<el-table-column label="用户编号" align="center" key="id" prop="id" />
<el-table-column
label="用户名称"
align="center"
prop="username"
:show-overflow-tooltip="true"
/>
<el-table-column
label="用户昵称"
align="center"
prop="nickname"
:show-overflow-tooltip="true"
/>
<el-table-column
label="部门"
align="center"
key="deptName"
prop="deptName"
:show-overflow-tooltip="true"
/>
<el-table-column label="手机号码" align="center" prop="mobile" width="120" />
<el-table-column label="状态" key="status">
<template #default="scope">
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column
label="创建时间"
align="center"
prop="createTime"
:formatter="dateFormatter"
width="180"
/>
</el-table>
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</ContentWrap>
</el-col>
</el-row>
<template #footer>
<el-button type="primary" @click="handleEmitChange"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
</template>
<script lang="ts" setup>
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime'
import * as UserApi from '@/api/system/user'
import DeptTree from '@/views/system/user/DeptTree.vue'
//
const isCheckAll = ref(false)
// &&
const isIndeterminate = ref(false)
//
const checkedUsers = ref([])
// keyIDvalue
const checkedStatus = ref<Record<string, boolean>>({})
const dialogTitle = "选择店员"
const dialogVisible = ref(false)
const loading = ref(true) //
const total = ref(0) //
const list = ref([]) //
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
username: undefined,
mobile: undefined,
status: undefined,
deptId: undefined,
roleId: 5,
createTime: []
})
const queryFormRef = ref() //
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
const data = await UserApi.getUserPage(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields()
handleQuery()
}
/** 处理部门被点击 */
const handleDeptNodeClick = async (row) => {
queryParams.deptId = row.id
await getList()
}
/** 打开弹窗 */
const open = async () => {
dialogVisible.value = true
loading.value = true
try {
await getList()
} finally {
loading.value = false
}
}
defineExpose({ open }) // open
/** 全选/全不选 */
const handleCheckAll = (checked: boolean) => {
isCheckAll.value = checked
isIndeterminate.value = false
list.value.forEach((combinationActivity) => handleCheckOne(checked, combinationActivity, false))
}
/**
* 选中一行
* @param checked 是否选中
* @param combinationActivity 活动
* @param isCalcCheckAll 是否计算全选
*/
const handleCheckOne = (
checked: boolean,
combinationActivity,
isCalcCheckAll: boolean
) => {
if (checked) {
checkedUsers.value.push(combinationActivity as never)
checkedStatus.value[combinationActivity.id] = true
} else {
const index = findCheckedIndex(combinationActivity)
if (index > -1) {
checkedUsers.value.splice(index, 1)
checkedStatus.value[combinationActivity.id] = false
isCheckAll.value = false
}
}
//
if (isCalcCheckAll) {
calculateIsCheckAll()
}
}
//
const findCheckedIndex = (user) =>
checkedUsers.value.findIndex((item) => item.id === user.id)
//
const calculateIsCheckAll = () => {
isCheckAll.value = list.value.every((user) => checkedStatus.value[user.id])
// &&
isIndeterminate.value =
!isCheckAll.value && list.value.some((user) => checkedStatus.value[user.id])
}
/** 多选完成 */
const handleEmitChange = () => {
//
dialogVisible.value = false
emits("change", [...checkedUsers.value])
}
/** 确认选择时的触发事件 */
const emits = defineEmits<{
change: [CombinationActivityApi: any]
}>()
</script>

View File

@ -103,6 +103,14 @@
> >
编辑 编辑
</el-button> </el-button>
<el-button
v-hasPermi="['trade:delivery:pick-up-store:bind']"
link
type="primary"
@click="openFormBind(scope.row.id)"
>
绑定店员
</el-button>
<el-button <el-button
v-hasPermi="['trade:delivery:pick-up-store:delete']" v-hasPermi="['trade:delivery:pick-up-store:delete']"
link link
@ -117,10 +125,13 @@
</ContentWrap> </ContentWrap>
<!-- 表单弹窗添加/修改 --> <!-- 表单弹窗添加/修改 -->
<DeliveryPickUpStoreForm ref="formRef" @success="getList" /> <DeliveryPickUpStoreForm ref="formRef" @success="getList" />
<!-- 表单弹窗绑定店员 -->
<DeliveryPickUpStoreBindForm ref="formBindRef"/>
</template> </template>
<script lang="ts" name="DeliveryPickUpStore" setup> <script lang="ts" name="DeliveryPickUpStore" setup>
import * as DeliveryPickUpStoreApi from '@/api/mall/trade/delivery/pickUpStore' import * as DeliveryPickUpStoreApi from '@/api/mall/trade/delivery/pickUpStore'
import DeliveryPickUpStoreForm from './PickUpStoreForm.vue' import DeliveryPickUpStoreForm from './PickUpStoreForm.vue'
import DeliveryPickUpStoreBindForm from './DeliveryPickUpStoreBindForm.vue'
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime' import { dateFormatter } from '@/utils/formatTime'
@ -146,6 +157,11 @@ const openForm = (type: string, id?: number) => {
formRef.value.open(type, id) formRef.value.open(type, id)
} }
const formBindRef = ref()
const openFormBind = (id?: number) => {
formBindRef.value.open(id)
}
/** 删除按钮操作 */ /** 删除按钮操作 */
const handleDelete = async (id: number) => { const handleDelete = async (id: number) => {
try { try {

View File

@ -13,7 +13,7 @@
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
<el-button type="primary" :disabled="formLoading" @click="getOrderByPickUpVerifyCode"> <el-button type="primary" :disabled="formLoading" @click="getOrderByPickUpVerifyCodeClick">
查询 查询
</el-button> </el-button>
<el-button @click="dialogVisible = false"> </el-button> <el-button @click="dialogVisible = false"> </el-button>
@ -52,9 +52,14 @@ const formRef = ref() // 表单 Ref
const orderDetails = ref<OrderVO>({}) const orderDetails = ref<OrderVO>({})
/** 打开弹窗 */ /** 打开弹窗 */
const open = async () => { const open = async (pickUpVerifyCode: string) => {
resetForm() resetForm()
dialogVisible.value = true if(pickUpVerifyCode != null){
formData.value.pickUpVerifyCode = pickUpVerifyCode;
await getOrderByPickUpVerifyCode()
}else{
dialogVisible.value = true
}
} }
defineExpose({ open }) // open defineExpose({ open }) // open
@ -83,18 +88,21 @@ const resetForm = () => {
formRef.value?.resetFields() formRef.value?.resetFields()
} }
/** 查询核销码对应的订单 */ const getOrderByPickUpVerifyCodeClick = async () => {
const getOrderByPickUpVerifyCode = async () => {
// //
if (!formRef) return if (!formRef) return
const valid = await formRef.value.validate() const valid = await formRef.value.validate()
if (!valid) return if (!valid) return
await getOrderByPickUpVerifyCode()
}
/** 查询核销码对应的订单 */
const getOrderByPickUpVerifyCode = async () => {
formLoading.value = true formLoading.value = true
const data = await TradeOrderApi.getOrderByPickUpVerifyCode(formData.value.pickUpVerifyCode) const data = await TradeOrderApi.getOrderByPickUpVerifyCode(formData.value.pickUpVerifyCode)
formLoading.value = false formLoading.value = false
if (data?.deliveryType !== DeliveryTypeEnum.PICK_UP.type) { if (data?.deliveryType !== DeliveryTypeEnum.PICK_UP.type) {
message.error('请输入正确的核销码') message.error('未查询到订单')
return return
} }
if (data?.status !== TradeOrderStatusEnum.UNDELIVERED.status) { if (data?.status !== TradeOrderStatusEnum.UNDELIVERED.status) {