5 Commits

Author SHA1 Message Date
gyq
80ecf0526a 退菜更改为多选 2026-06-05 09:43:57 +08:00
gyq
380a8c6572 新增商品按字母搜索 2026-05-15 13:40:42 +08:00
gyq
a719807b0e 优化订单无法再次打印的问题 2026-05-13 09:48:03 +08:00
gyq
32e24c6d52 问题修复 2026-05-08 17:12:37 +08:00
66e8da53de 增加web端订单列表退菜显示 2026-05-08 11:30:37 +08:00
10 changed files with 207 additions and 116 deletions

View File

@@ -59,6 +59,7 @@
"path-browserify": "^1.0.1",
"path-to-regexp": "^8.2.0",
"pinia": "^2.3.1",
"pinyin-match": "^1.2.10",
"qrcode": "^1.5.4",
"qs": "^6.14.0",
"sockjs-client": "^1.6.1",

View File

@@ -8,6 +8,7 @@ import shopUserApi from '@/api/account/shopUser'
import limitTimeDiscountApi from '@/api/market/limitTimeDiscount.js'
import { BigNumber } from "bignumber.js";
import _ from "lodash";
import PinyinMatch from 'pinyin-match'
// 导入工具库及相关类型
import {
@@ -308,11 +309,17 @@ export const useCartsStore = defineStore("carts", () => {
console.log('代客下单页面商品缓存.获取当前店铺可用的限时折扣', limitDiscountRes.value);
// 1. 先把 query.name 提取出来,接口调用时不传,避免后端限制
const searchName = query.name || '';
const queryParams = { ...query };
delete queryParams.name;
// 2. 调用接口(不带 name 参数,拿全量数据,前端自己过滤)
const res = await productApi.getPage({
page: 1,
size: 999,
status: "on_sale",
...query,
...queryParams,
});
interface ProductItem {
@@ -332,7 +339,21 @@ export const useCartsStore = defineStore("carts", () => {
limitDiscountPrice: number;
}
goods.value = (res.records as ProductItem[]).map((item: ProductItem): GoodsWithDiscount => {
// 3. 【核心:拼音 + 汉字 模糊过滤】
let filteredList = res.records as ProductItem[];
if (searchName) {
filteredList = filteredList.filter(item => {
if (!item.name) return false;
// 汉字模糊搜索
const hasName = item.name.includes(searchName);
// 拼音/首字母搜索zs / zhangsan / 张三 都支持)
const hasPinyin = PinyinMatch.match(item.name, searchName);
return hasName || hasPinyin;
});
}
// 4. 价格处理(完全不变)
goods.value = filteredList.map((item: ProductItem): GoodsWithDiscount => {
item.salePrice = item.lowPrice
item.memberPrice = item.lowMemberPrice

View File

@@ -158,7 +158,7 @@
<!-- 选择耗材 -->
<ConsumableList ref="ConsumableList" @success="selectConsumable" />
<el-dialog v-model="showResult" :show-close="false" :close-on-press-escape="false" :close-on-click-modal="false">
<el-result icon="success" title="入库提交成功" :subTitle="`共操作${tableData.list.length}件商品`">
<el-result icon="success" :title="`提交成功3秒后自动关闭`" :subTitle="`共操作${tableData.list.length}件商品`">
<template #extra>
<template>
<el-button type="primary" size="medium" @click="resetHandle">创建新的入库单</el-button>
@@ -173,7 +173,7 @@
</template>
<script>
import { ElMessage, ElMessageBox } from "element-plus";
import { ElMessage } from "element-plus";
import consApi from "@/api/product/cons";
import Decimal from "decimal.js";
import vendorApi from "@/api/product/vendor";
@@ -402,13 +402,17 @@ export default {
}
this.queryFormLoading = false;
// const title = this.type == "in" ? "入库" : "出库";
ElMessage({
// message: title + "提交成功",
message: "提交成功",
type: "success",
});
this.$router.push("/inventory/consumables");
// ElMessage({
// // message: title + "提交成功",
// message: "提交成功",
// type: "success",
// });
this.showResult = true;
setTimeout(() => {
this.$router.push("/inventory/consumables");
}, 3000);
// this.$refs.shopList.reset()//清除选项
// this.$refs.ConsumableList.reset()//清除选项
} catch (error) {

View File

@@ -8,6 +8,7 @@
{{ item.productName }}
</span>
<span v-if="item.refundNum" class="refund">(退 - {{ item.refundNum }})</span>
<span v-else-if="item.returnNum" class="refund">(退 - {{ item.returnNum }})</span>
</div>
<div class="sku">{{ item.skuName }}</div>
</div>

View File

@@ -139,7 +139,7 @@ const contentConfig: IContentConfig = {
label: "操作",
align: "center",
fixed: "right",
width: 180,
width: 240,
templet: "custom",
slotName: "operate",
},

View File

@@ -2,37 +2,19 @@
<div class="app-container">
<!-- 列表 -->
<!-- 搜索 -->
<page-search
ref="searchRef"
:search-config="searchConfig"
:isOpenAutoSearch="true"
@query-click="handleQueryClick"
@reset-click="handleResetClick"
/>
<page-search ref="searchRef" :search-config="searchConfig" :isOpenAutoSearch="true" @query-click="handleQueryClick"
@reset-click="handleResetClick" />
<!-- 列表 -->
<page-content
ref="contentRef"
:content-config="contentConfig"
@add-click="handleAddClick"
@edit-click="handleEditClick"
@export-click="handleExportClick"
@search-click="handleSearchClick"
@toolbar-click="handleToolbarClick"
@operat-click="handleOperatClick"
@filter-change="handleFilterChange"
>
<page-content ref="contentRef" :content-config="contentConfig" @add-click="handleAddClick"
@edit-click="handleEditClick" @export-click="handleExportClick" @search-click="handleSearchClick"
@toolbar-click="handleToolbarClick" @operat-click="handleOperatClick" @filter-change="handleFilterChange">
<template #originAmount="scope">
{{ returnOriginAmount(scope.row) }}
</template>
<template #orderNo="scope">
<div style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap">
<el-tooltip
class="box-item"
effect="dark"
:content="scope.row.orderNo"
placement="top-start"
>
<el-tooltip class="box-item" effect="dark" :content="scope.row.orderNo" placement="top-start">
{{ scope.row.orderNo }}
</el-tooltip>
</div>
@@ -73,7 +55,7 @@
<!-- 打印状态 -->
<template #printStatus="scope">
<span v-if="scope.row.printStatus.length > 0" style="color: var(--el-color-danger)">
打印失败{{ scope.row.printStatus.map((item) => item.name).join("、") }}
打印失败{{scope.row.printStatus.map((item) => item.name).join("、")}}
</span>
</template>
@@ -87,25 +69,18 @@
</template>
<template #mobile="scope">
<el-text>{{ scope.row[scope.prop] }}</el-text>
<copy-button
v-if="scope.row[scope.prop]"
:text="scope.row[scope.prop]"
style="margin-left: 2px"
/>
<copy-button v-if="scope.row[scope.prop]" :text="scope.row[scope.prop]" style="margin-left: 2px" />
</template>
<template #payType="scope">
{{ returnPayTypeOptionsLabel(scope.prop, scope.row[scope.prop]) }}
</template>
<template #operate="scope">
<div>
<el-button link @click="printOrderHandle(scope.row)">打印</el-button>
<el-button type="primary" link @click="printOrderHandle(scope.row, 2)">打印前台</el-button>
<el-button type="warning" link @click="printOrderHandle(scope.row, 3)">打印厨房</el-button>
<el-button link @click="showdetail(scope.row)">详情</el-button>
<!-- <el-button v-if="scope.row.status == 'done'" link>开票</el-button> -->
<el-button
v-if="scope.row.status == 'unpaid'"
type="primary"
@click="toPayOrder(scope.row)"
>
<el-button v-if="scope.row.status == 'unpaid'" type="primary" @click="toPayOrder(scope.row)">
结账
</el-button>
</div>
@@ -121,11 +96,7 @@
</page-modal>
<!-- 编辑 -->
<page-modal
ref="editModalRef"
:modal-config="editModalConfig"
@submit-click="handleSubmitClick"
>
<page-modal ref="editModalRef" :modal-config="editModalConfig" @submit-click="handleSubmitClick">
<template #url="scope">
<FileUpload v-model="scope.formData[scope.prop]" :limit="1" v-bind="scope.attrs" />
<!-- <Dict v-model="scope.formData[scope.prop]" code="gender" v-bind="scope.attrs" /> -->
@@ -191,11 +162,11 @@ onMounted(() => {
});
// 打印订单
async function printOrderHandle(order: getListResponse) {
async function printOrderHandle(order: getListResponse, type: number) {
try {
await orderApi.printOrder({
id: order.id,
type: 0,
type: type,
});
ElMessage.success("打印成功");
} catch (error) {
@@ -404,7 +375,7 @@ function showdetail(row: OrderInfoVo) {
left: 36%;
padding: 18px;
> div:first-child {
>div:first-child {
display: flex;
align-items: center;
justify-content: space-between;
@@ -415,7 +386,7 @@ function showdetail(row: OrderInfoVo) {
transform: translateX(-80px);
}
> div:last-child {
>div:last-child {
text-align: center;
}
}

View File

@@ -50,9 +50,9 @@
</template>
<template #tuikuantuihui="scope">
<el-switch v-if="!scope.row.productId" v-model="scope.row[scope.prop]" :active-value="1" :inactive-value="0"
@click="handleSwitchhaocai(scope.row)"></el-switch>
<!-- <el-text>{{ returnLabel(scope.row[scope.prop]) }}</el-text> -->
<!-- <el-switch v-if="!scope.row.productId" v-model="scope.row[scope.prop]" :active-value="1" :inactive-value="0"
@click="handleSwitchhaocai(scope.row)"></el-switch> -->
<el-text>{{ returnLabel(scope.row[scope.prop]) }}</el-text>
</template>
<template #sellOut="scope">
<el-switch v-model="scope.row[scope.prop]" :active-value="1" :inactive-value="0"

View File

@@ -184,7 +184,7 @@
<el-form-item label="重量" v-if="ruleForm.type == 'weight'">
<el-col :span="12">
<div style="display: block">
<el-input v-model="ruleForm.weight" placeholder="纯文本描述"></el-input>
<el-input v-model="ruleForm.weight" placeholder="可输入每份重量向顾客展示500克/份"></el-input>
<!-- <div style="color: #999;">用于快递或配送运费计重</div> -->
</div>
</el-col>

View File

@@ -20,9 +20,9 @@
</div>
<div class="gyq_content row mt14">
<tabHeader v-model="tabActiveIndex" :list="tabList" />
<payStatusCard name="payStatusCard" key="payStatusCard" ref="payStatusCardRef" v-if="tabActiveIndex == 0" />
<!-- <payStatusCard name="payStatusCard" key="payStatusCard" ref="payStatusCardRef" v-if="tabActiveIndex == 0" /> -->
<detailModal name="detailModal" key="detailModal" ref="detailModalRef" v-model="detail"
v-if="tabActiveIndex == 1" />
v-if="tabActiveIndex == 0" />
</div>
</div>
</template>

View File

@@ -1,6 +1,6 @@
<template>
<el-dialog title="退菜" width="410px" v-model="show" @close="reset" :modal="modal">
<div class="u-p-b-16">
<el-dialog title="退菜" width="550px" v-model="show" @close="reset" :modal="modal">
<!-- <div class="u-p-b-16">
<div class="flex u-row-between">
<span>菜品名称</span>
<div class="u-flex">
@@ -31,15 +31,13 @@
</div>
</div>
</div>
<div class="u-m-t-10 u-font-12 color-999">菜品已点数量 {{ max }} </div>
<div class="u-m-t-26">
<div class="u-m-t-10 u-font-12 color-999">菜品已点数量 {{ max }} </div> -->
<div class="u-m-t-0">
<div>
<span>退菜原因</span>
<span class="color-red">*</span>
</div>
</div>
<div class="u-flex u-flex-wrap tags">
<div class="tag" v-for="(tag, index) in tags" @click="changeSel(tag)" :key="index"
:class="{ active: tag.checked }">
@@ -47,15 +45,49 @@
</div>
</div>
<div class="u-m-t-20">
<el-input v-model="note" placeholder="请输入自定义备注"></el-input>
<el-input v-model="note" placeholder="请输入自定义备注" clearable></el-input>
</div>
<!-- <div class="u-m-t-20">
<el-checkbox v-model="isPrint">打印退菜单</el-checkbox>
</div> -->
<div class="u-m-t-20">
<span>选择要退的菜品</span>
</div>
<div class="u-m-t-20">
<el-checkbox v-model="isPrint">打印退菜单</el-checkbox>
<el-table :data="orderList" ref="tableRef" row-key="id" border stripe height="280"
@selection-change="handleSelectionChange">
<el-table-column type="selection"></el-table-column>
<el-table-column prop="name" label="菜品信息">
<template #default="{ row }">
<div>{{ row.productName }}</div>
<div class="tag_wrap" v-if="row.sku_name">
<el-text type="info" size="small">规格</el-text>
<el-text type="info" size="small" v-for="item in row.sku_name.split(',')">
{{ item }}
</el-text>
</div>
<div class="tag_wrap" v-if="row.remark">
<el-text type="info" size="small">备注</el-text>
<el-text type="info" size="small">{{ row.remark }}</el-text>
</div>
</template>
</el-table-column>
<el-table-column prop="quantity" label="退菜数量">
<template #default="{ row }">
<el-input-number v-model="row.customReturnNum" :min="1" :max="row.number - row.returnNum"
style="width: 180px" :step="1" step-strictly>
</el-input-number>
</template>
</el-table-column>
</el-table>
</div>
<template #footer>
<div>
<el-button @click="close">取消</el-button>
<el-button type="primary" @click="confirm">确定</el-button>
<div class="footer_wrap">
<div class="letf">退菜数量{{ _.sumBy(selectData, 'customReturnNum') || 0 }}</div>
<div class="flex">
<el-button @click="close">取消</el-button>
<el-button type="primary" :disabled="selectData.length == 0" @click="confirm">确定</el-button>
</div>
</div>
</template>
</el-dialog>
@@ -63,6 +95,8 @@
<refundConsModal ref="refundConsModalRef" :list="refundList" @success="refundConsModalSuccess" />
</template>
<script>
import _ from "lodash";
import { toRaw } from "vue";
import { ElMessage } from "element-plus";
import { useCartsStore } from '@/store/modules/carts'
import refundConsModal from "@/components/refundConsModal.vue";
@@ -76,6 +110,9 @@ export default {
},
data() {
return {
_,
selectData: [],
orderList: [],
max: 1,
number: 1,
isPrint: false,
@@ -85,6 +122,8 @@ export default {
{ label: "不想要了", checked: false },
{ label: "食材不足", checked: false },
{ label: "等待时间过长", checked: false },
{ label: "点重复了", checked: false },
{ label: "口味问题", checked: false },
],
note: "",
goods: {
@@ -102,7 +141,12 @@ export default {
},
methods: {
changeSel(item) {
item.checked = !item.checked;
// item.checked = !item.checked;
if (this.note == '') {
this.note = item.label
} else {
this.note += `${item.label}`
}
},
reset() {
this.note = "";
@@ -121,15 +165,54 @@ export default {
}
this.note = tag + "," + this.note;
},
open(item) {
this.goods = item ? item : this.goods;
this.max = item.num - item.returnNum;
// 选择数据变化
handleSelectionChange(val) {
this.selectData = val
},
open(goods) {
this.goods = goods
const carts = useCartsStore();
let categorys = JSON.parse(localStorage.getItem('categorys'))
let shopInfo = JSON.parse(localStorage.getItem('userInfo'))
this.orderList = _.flatten(_.values(carts.oldOrder.detailMap), 'goods').map(item => ({
...item,
customReturnNum: item.number - item.returnNum
})).filter(item => item.number - item.returnNum > 0)
this.orderList.forEach(item => {
carts.goods.forEach(val => {
if (item.productId == val.id) {
if (shopInfo.refundMode == 1) {
// 跟随分类退款模式
categorys.forEach(v => {
if (val.categoryId == v.id) {
item.refundMode = v.refundMode
}
})
} else {
// 跟随商品退款模式及
item.refundMode = val.refundMode
}
}
})
})
console.log('cartOperationReturn组件打开了', this.orderList)
setTimeout(() => {
this.$refs.tableRef.toggleRowSelection(this.orderList.find(item => item.id == this.goods.id), true)
}, 100);
this.show = true;
if (item.productId != "-999") {
this.number = 1;
} else {
this.number = this.max;
}
// this.goods = item ? item : this.goods;
// this.max = item.num - item.returnNum;
// if (item.productId != "-999") {
// this.number = 1;
// } else {
// this.number = this.max;
// }
},
close() {
this.show = false;
@@ -144,56 +227,60 @@ export default {
this.$emit("confirm", {
refundReason: this.note,
refundAmount: 0,
refundDetails: [{ id: this.goods.id, num: this.number, returnAmount: 0 }],
refundDetails: toRaw(this.selectData).map(item => ({
id: item.id,
returnAmount: item.lowPrice,
num: item.customReturnNum
})),
refundStock: this.refundStock
});
this.close();
},
// 确认退菜
confirm() {
const selTag = this.tags
.filter((item) => item.checked)
.map((item) => item.label)
.join(",");
this.note = selTag + (this.note.length > 0 ? "," + this.note : "");
// const selTag = this.tags
// .filter((item) => item.checked)
// .map((item) => item.label)
// .join(",");
// this.note = selTag + (this.note.length > 0 ? "," + this.note : "");
if (!this.note) {
return ElMessage.error("请输入退菜原因");
}
const carts = useCartsStore();
let categorys = JSON.parse(localStorage.getItem('categorys'))
let shopInfo = JSON.parse(localStorage.getItem('userInfo'))
// const carts = useCartsStore();
// let categorys = JSON.parse(localStorage.getItem('categorys'))
// let shopInfo = JSON.parse(localStorage.getItem('userInfo'))
// 在这里给订单的商品补全库存信息 start
carts.goods.forEach(val => {
if (this.goods.productId == val.id) {
if (shopInfo.refundMode == 1) {
// 跟随分类退款模式
categorys.forEach(v => {
if (val.categoryId == v.id) {
this.goods.refundMode = v.refundMode
}
})
} else {
// 跟随商品退款模式及
this.goods.refundMode = val.refundMode
}
}
})
// // 在这里给订单的商品补全库存信息 start
// carts.goods.forEach(val => {
// if (this.goods.productId == val.id) {
// if (shopInfo.refundMode == 1) {
// // 跟随分类退款模式
// categorys.forEach(v => {
// if (val.categoryId == v.id) {
// this.goods.refundMode = v.refundMode
// }
// })
// } else {
// // 跟随商品退款模式及
// this.goods.refundMode = val.refundMode
// }
// }
// })
console.log('this.goods===', this.goods);
if (this.goods.refundMode == 3) {
this.refundList = [
{
name: this.goods.product_name,
num: this.number
}
]
console.log('this.selectData===', this.selectData);
this.refundList = toRaw(this.selectData).filter(item => item.refundMode == 3).map(item => ({
name: item.product_name,
num: item.customReturnNum
}))
if (this.refundList.length > 0) {
this.$refs.refundConsModalRef.show()
return
}
// 在这里给订单的商品补全库存信息 end
this.refundNext()
},
},
@@ -202,6 +289,12 @@ export default {
</script>
<style lang="scss" scoped>
.footer_wrap {
display: flex;
justify-content: space-between;
align-items: center;
}
:deep(.el-dialog__body) {
margin-bottom: 14px;
margin-top: 14px;