Merge branch 'prod' of https://newgitea.sxczgkj.cn/czg_team/cashier-web into prod
This commit is contained in:
@@ -9,7 +9,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
// test
|
// test 11
|
||||||
import { useAppStore, useSettingsStore } from "@/store";
|
import { useAppStore, useSettingsStore } from "@/store";
|
||||||
import defaultSettings from "@/settings";
|
import defaultSettings from "@/settings";
|
||||||
import { ThemeEnum } from "@/enums/ThemeEnum";
|
import { ThemeEnum } from "@/enums/ThemeEnum";
|
||||||
|
|||||||
@@ -1,12 +1,18 @@
|
|||||||
import request from "@/utils/request";
|
import request from "@/utils/request";
|
||||||
import { Account_BaseUrl } from "@/api/config";
|
import { Account_BaseUrl, Market_BaseUrl } from "@/api/config";
|
||||||
const baseURL = Account_BaseUrl + "/admin/points";
|
const baseURL = Account_BaseUrl + "/admin/points";
|
||||||
const Api = {
|
const Api = {
|
||||||
// 002-获取订单可用积分及抵扣金额(支付页面使用)
|
// 002-获取订单可用积分及抵扣金额(支付页面使用)
|
||||||
|
// calcOrderUsablePoints(params: any) {
|
||||||
|
// return request<any>({
|
||||||
|
// url: `${baseURL}/memberPoints/calcUsablePoints`,
|
||||||
|
// method: "get",
|
||||||
|
// params
|
||||||
|
// });
|
||||||
|
// },
|
||||||
calcOrderUsablePoints(params: any) {
|
calcOrderUsablePoints(params: any) {
|
||||||
return request<any>({
|
return request<any>({
|
||||||
url: `${baseURL}/memberPoints/calcUsablePoints`,
|
url: `${Market_BaseUrl}/admin/points/userPoints`,
|
||||||
method: "get",
|
method: "get",
|
||||||
params
|
params
|
||||||
});
|
});
|
||||||
|
|||||||
68
src/api/account/quick.ts
Normal file
68
src/api/account/quick.ts
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import request from "@/utils/request";
|
||||||
|
import { Account_BaseUrl } from "@/api/config";
|
||||||
|
const baseURL = Account_BaseUrl + "/admin/quick";
|
||||||
|
const API = {
|
||||||
|
getList(data: any) {
|
||||||
|
return request<any, QuickMenu[]>({
|
||||||
|
url: `${baseURL}`,
|
||||||
|
method: "get",
|
||||||
|
params: data
|
||||||
|
});
|
||||||
|
},
|
||||||
|
add(data: any) {
|
||||||
|
return request({
|
||||||
|
url: `${baseURL}`,
|
||||||
|
method: "post",
|
||||||
|
data: data,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
delete(ids: [string | number]) {
|
||||||
|
return request({
|
||||||
|
url: `${baseURL}`,
|
||||||
|
method: "delete",
|
||||||
|
data: ids,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
edit(data: any) {
|
||||||
|
return request({
|
||||||
|
url: `${baseURL}`,
|
||||||
|
method: "put",
|
||||||
|
data: data,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}
|
||||||
|
export default API;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 悬浮窗配置 实体类。
|
||||||
|
*
|
||||||
|
* QuickMenu
|
||||||
|
*/
|
||||||
|
export interface QuickMenu {
|
||||||
|
createTime?: string;
|
||||||
|
id?: number;
|
||||||
|
/**
|
||||||
|
* 菜单Id
|
||||||
|
*/
|
||||||
|
menuId: number;
|
||||||
|
/**
|
||||||
|
* 店铺Id
|
||||||
|
*/
|
||||||
|
shopId?: number;
|
||||||
|
/**
|
||||||
|
* 排序
|
||||||
|
*/
|
||||||
|
sort?: number;
|
||||||
|
/**
|
||||||
|
* 状态 1-启用 0-禁用
|
||||||
|
*/
|
||||||
|
status?: number;
|
||||||
|
updateTime?: string;
|
||||||
|
/**
|
||||||
|
* 菜单图标
|
||||||
|
*/
|
||||||
|
url?: string;
|
||||||
|
[property: string]: any;
|
||||||
|
}
|
||||||
@@ -64,6 +64,14 @@ const OrderApi = {
|
|||||||
data
|
data
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
// 订单打印
|
||||||
|
printOrder(data: any) {
|
||||||
|
return request<any>({
|
||||||
|
url: `${baseURL}/print`,
|
||||||
|
method: "post",
|
||||||
|
data
|
||||||
|
});
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default OrderApi;
|
export default OrderApi;
|
||||||
|
|||||||
BIN
src/assets/images/close.png
Normal file
BIN
src/assets/images/close.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.2 KiB |
BIN
src/assets/images/plus.png
Normal file
BIN
src/assets/images/plus.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.0 KiB |
266
src/components/FastMenuConfig/dialog-add.vue
Normal file
266
src/components/FastMenuConfig/dialog-add.vue
Normal file
@@ -0,0 +1,266 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog :title="form.id ? '编辑' : '添加'" width="400px" v-model="visible" @closed="resetForm">
|
||||||
|
<el-form ref="formRef" :model="form" :rules="rules" label-width="100px" label-position="left">
|
||||||
|
<el-form-item label="菜单" prop="menuId">
|
||||||
|
<el-tree-select
|
||||||
|
:check-strictly="true"
|
||||||
|
v-model="form.menuId"
|
||||||
|
:data="menus"
|
||||||
|
:render-after-expand="false"
|
||||||
|
style="width: 240px"
|
||||||
|
node-key="menuId"
|
||||||
|
:disabled-key="disabled"
|
||||||
|
></el-tree-select>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="排序值">
|
||||||
|
<el-input-number v-model="form.sort" :step="1" step-strictly />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="启用状态">
|
||||||
|
<el-switch v-model="form.status" :active-value="1" :inactive-value="0"></el-switch>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog_footer">
|
||||||
|
<div class="btn">
|
||||||
|
<el-button size="large" style="width: 100%" @click="visible = false">取 消</el-button>
|
||||||
|
</div>
|
||||||
|
<div class="btn">
|
||||||
|
<el-button
|
||||||
|
size="large"
|
||||||
|
type="primary"
|
||||||
|
:loading="loading"
|
||||||
|
style="width: 100%"
|
||||||
|
@click="submitHandle"
|
||||||
|
>
|
||||||
|
确 定
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
// 补充缺失的导入
|
||||||
|
import { ref, onMounted, watch } from "vue";
|
||||||
|
import { ElNotification, ElMessage } from "element-plus"; // 导入提示组件
|
||||||
|
import { filterNumberInput } from "@/utils";
|
||||||
|
import { roleTemplateAdd } from "@/api/account/roleTemplate";
|
||||||
|
import MenuAPI from "@/api/account/menu";
|
||||||
|
import quickApi from "@/api/account/quick";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
isGetMenu: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false, // 补充默认值
|
||||||
|
},
|
||||||
|
parMenus: {
|
||||||
|
type: Array,
|
||||||
|
required: false,
|
||||||
|
default: () => [], // 补充默认值
|
||||||
|
},
|
||||||
|
item: {
|
||||||
|
type: Object,
|
||||||
|
required: false,
|
||||||
|
default: () => null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const menus = ref([]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化菜单数据:核心逻辑 - 禁用有子菜单的父节点,仅允许选叶子节点
|
||||||
|
* @param arr 原始菜单数组
|
||||||
|
* @returns 格式化后带禁用标记的菜单数组
|
||||||
|
*/
|
||||||
|
function returnMenu(arr) {
|
||||||
|
let result = [];
|
||||||
|
for (let menu of arr) {
|
||||||
|
// 过滤掉"默认接口目录"
|
||||||
|
if (menu.title === "默认接口目录") continue;
|
||||||
|
|
||||||
|
// 筛选有效子菜单:非隐藏 + 类型为0
|
||||||
|
const children = menu.children ? menu.children.filter((v) => !v.hidden && v.type === 0) : [];
|
||||||
|
// 递归处理子菜单
|
||||||
|
const formattedChildren = returnMenu(children);
|
||||||
|
|
||||||
|
// 核心:判断是否为叶子节点(无有效子菜单)
|
||||||
|
const isLeaf = formattedChildren.length === 0;
|
||||||
|
|
||||||
|
// 组装节点:有子菜单则禁用(disabled: true),无则允许选择
|
||||||
|
const menuNode = {
|
||||||
|
...menu,
|
||||||
|
label: menu.title,
|
||||||
|
value: menu.menuId,
|
||||||
|
menuId: menu.menuId, // 保持node-key一致
|
||||||
|
disabled: !isLeaf, // 有子菜单 → 禁用,无 → 启用
|
||||||
|
children: formattedChildren,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 特殊处理:如果子菜单只有1个,直接扁平化(保持原有逻辑)
|
||||||
|
if (formattedChildren.length === 1) {
|
||||||
|
result.push({
|
||||||
|
...formattedChildren[0],
|
||||||
|
disabled: formattedChildren[0].children.length > 0, // 子节点仍判断是否有后代
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
result.push(menuNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
if (!props.isGetMenu) return;
|
||||||
|
try {
|
||||||
|
const res = await MenuAPI.getRoutes();
|
||||||
|
menus.value = returnMenu(res);
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error("菜单数据加载失败");
|
||||||
|
console.error("getRoutes error:", error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 监听父组件传入的菜单变化
|
||||||
|
watch(
|
||||||
|
() => props.parMenus,
|
||||||
|
(newVal) => {
|
||||||
|
if (newVal.length) {
|
||||||
|
menus.value = returnMenu(newVal);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ deep: true } // 监听数组内部变化
|
||||||
|
);
|
||||||
|
|
||||||
|
// 对话框显隐绑定
|
||||||
|
const visible = defineModel({
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const formRef = ref(null);
|
||||||
|
const loading = ref(false);
|
||||||
|
// 表单初始化
|
||||||
|
const form = ref({
|
||||||
|
menuId: "",
|
||||||
|
sort: 0,
|
||||||
|
url: "",
|
||||||
|
status: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义校验规则:确保选中的是叶子节点(兜底验证)
|
||||||
|
* @param rule 校验规则
|
||||||
|
* @param value 选中的menuId
|
||||||
|
* @param callback 回调函数
|
||||||
|
*/
|
||||||
|
function validateLeafMenu(rule, value, callback) {
|
||||||
|
if (!value) {
|
||||||
|
return callback(new Error("请选择菜单"));
|
||||||
|
}
|
||||||
|
// 递归查找节点,判断是否为叶子节点
|
||||||
|
const findNode = (nodes, menuId) => {
|
||||||
|
for (let node of nodes) {
|
||||||
|
if (node.menuId === menuId) return node;
|
||||||
|
if (node.children) {
|
||||||
|
const res = findNode(node.children, menuId);
|
||||||
|
if (res) return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
const selectedNode = findNode(menus.value, value);
|
||||||
|
|
||||||
|
if (!selectedNode) {
|
||||||
|
callback(new Error("选择的菜单不存在"));
|
||||||
|
} else if (selectedNode.disabled) {
|
||||||
|
callback(new Error("有子菜单的父节点不可选,请选择子菜单"));
|
||||||
|
} else {
|
||||||
|
callback(); // 校验通过
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 表单校验规则:修复拼写错误 + 新增自定义叶子节点校验
|
||||||
|
const rules = ref({
|
||||||
|
menuId: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
validator: validateLeafMenu, // 替换原有规则为自定义校验
|
||||||
|
trigger: "change", // 选择变化时校验(原triiger拼写错误)
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
// 重置表单
|
||||||
|
function resetForm() {
|
||||||
|
form.value = {
|
||||||
|
menuId: "",
|
||||||
|
sort: 0,
|
||||||
|
url: "",
|
||||||
|
status: 1,
|
||||||
|
};
|
||||||
|
if (formRef.value) {
|
||||||
|
formRef.value.resetFields(); // 避免null调用
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交事件
|
||||||
|
const emits = defineEmits(["success"]);
|
||||||
|
async function submitHandle() {
|
||||||
|
try {
|
||||||
|
// 修复vaild拼写错误
|
||||||
|
const valid = await formRef.value.validate();
|
||||||
|
if (!valid) return;
|
||||||
|
|
||||||
|
loading.value = true;
|
||||||
|
// 排序值兜底
|
||||||
|
form.value.sort = form.value.sort === "" ? 0 : form.value.sort;
|
||||||
|
|
||||||
|
// 区分新增/编辑
|
||||||
|
if (form.value.id) {
|
||||||
|
await quickApi.edit(form.value);
|
||||||
|
ElNotification({ title: "成功", message: "编辑成功", type: "success" });
|
||||||
|
} else {
|
||||||
|
await quickApi.add(form.value);
|
||||||
|
ElNotification({ title: "成功", message: "添加成功", type: "success" });
|
||||||
|
}
|
||||||
|
|
||||||
|
emits("success");
|
||||||
|
visible.value = false;
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error(form.value.id ? "编辑失败" : "添加失败");
|
||||||
|
console.error("submit error:", error);
|
||||||
|
} finally {
|
||||||
|
loading.value = false; // 无论成败都关闭loading
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听props.item初始化表单
|
||||||
|
watch(
|
||||||
|
() => props.item,
|
||||||
|
(newval) => {
|
||||||
|
if (newval) {
|
||||||
|
form.value = { ...newval }; // 深拷贝,避免修改原对象
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: true } // 初始化触发
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.dialog_footer {
|
||||||
|
display: flex;
|
||||||
|
gap: 14px;
|
||||||
|
.btn {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 可选:美化禁用节点的样式
|
||||||
|
.el-select-dropdown__item.is-disabled {
|
||||||
|
color: var(--el-text-color-regular);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
183
src/components/FastMenuConfig/index.vue
Normal file
183
src/components/FastMenuConfig/index.vue
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
<template>
|
||||||
|
<div class="container">
|
||||||
|
<div class="table-box">
|
||||||
|
<div class="row mb-sm">
|
||||||
|
<el-button type="primary" @click="showAdd = true">添加</el-button>
|
||||||
|
</div>
|
||||||
|
<el-table
|
||||||
|
:data="tableData.list"
|
||||||
|
v-loading="tableData.loading"
|
||||||
|
stripe
|
||||||
|
border
|
||||||
|
row-key="id"
|
||||||
|
:tree-props="{ children: 'children' }"
|
||||||
|
default-expand-all
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
|
<el-table-column label="ID" prop="id"></el-table-column>
|
||||||
|
<el-table-column label="菜单名称" prop="isEnable">
|
||||||
|
<template #default="scope">
|
||||||
|
{{ returnMenuName(scope.row) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="启用状态" prop="isEnable">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-switch
|
||||||
|
v-model="scope.row.status"
|
||||||
|
:active-value="1"
|
||||||
|
:inactive-value="0"
|
||||||
|
@change="isEnableChange($event, scope.row)"
|
||||||
|
></el-switch>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="排序值" prop="sort"></el-table-column>
|
||||||
|
<el-table-column label="创建时间" prop="createTime"></el-table-column>
|
||||||
|
<el-table-column label="操作" width="250">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button link type="primary" @click="editMenu(scope.row)">编辑</el-button>
|
||||||
|
<el-popconfirm title="确认要删除吗?" @confirm="deleteHandle(scope.row)">
|
||||||
|
<template #reference>
|
||||||
|
<el-button type="danger" link>删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-popconfirm>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<dialogAdd
|
||||||
|
v-model="showAdd"
|
||||||
|
:parMenus="menus"
|
||||||
|
@success="Refresh()"
|
||||||
|
:item="nowEditMenu"
|
||||||
|
></dialogAdd>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { useQuickStore, usePermissionStore } from "@/store";
|
||||||
|
const quickStore = useQuickStore();
|
||||||
|
|
||||||
|
import { ref, reactive, onMounted, toRaw } from "vue";
|
||||||
|
import MenuAPI from "@/api/account/menu";
|
||||||
|
|
||||||
|
import quickApi from "@/api/account/quick";
|
||||||
|
|
||||||
|
import dialogAdd from "./dialog-add.vue";
|
||||||
|
|
||||||
|
const showAdd = ref(false);
|
||||||
|
|
||||||
|
const nowEditMenu = ref(null);
|
||||||
|
function editMenu(item) {
|
||||||
|
nowEditMenu.value = { ...item };
|
||||||
|
showAdd.value = true;
|
||||||
|
}
|
||||||
|
const tableData = reactive({
|
||||||
|
loading: false,
|
||||||
|
page: 1,
|
||||||
|
size: 10,
|
||||||
|
total: 0,
|
||||||
|
list: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
function Refresh() {
|
||||||
|
quickStore.getQuickMenus();
|
||||||
|
getList();
|
||||||
|
}
|
||||||
|
function getList() {
|
||||||
|
tableData.loading = true;
|
||||||
|
quickApi
|
||||||
|
.getList({
|
||||||
|
page: tableData.page,
|
||||||
|
size: tableData.size,
|
||||||
|
isEdit: 1,
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
tableData.list = res;
|
||||||
|
tableData.total = res.length;
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
tableData.loading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const menus = ref([]);
|
||||||
|
async function getMenus() {
|
||||||
|
const res = await MenuAPI.getRoutes();
|
||||||
|
menus.value = res;
|
||||||
|
console.log("menus", res);
|
||||||
|
}
|
||||||
|
const menusIdMap = computed(() => {
|
||||||
|
const map = getMenuMap();
|
||||||
|
console.log("map", map);
|
||||||
|
|
||||||
|
return map;
|
||||||
|
});
|
||||||
|
function returnMenuName(item) {
|
||||||
|
const menu = menusIdMap.value.get(`${item.menuId}`);
|
||||||
|
console.log("menuId", item.menuId);
|
||||||
|
console.log("menu", menu);
|
||||||
|
|
||||||
|
return menu ? menu.title : "";
|
||||||
|
}
|
||||||
|
function getMenuMap() {
|
||||||
|
// 初始化Map
|
||||||
|
const map = new Map();
|
||||||
|
|
||||||
|
// 定义递归函数处理菜单节点
|
||||||
|
function processMenuNode(menuNode) {
|
||||||
|
// 将当前节点存入Map
|
||||||
|
map.set(menuNode.menuId, menuNode);
|
||||||
|
|
||||||
|
// 如果有子节点且子节点数组不为空,则递归处理每个子节点
|
||||||
|
if (menuNode.children && Array.isArray(menuNode.children) && menuNode.children.length > 0) {
|
||||||
|
menuNode.children.forEach((childNode) => {
|
||||||
|
processMenuNode(childNode);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 遍历根级菜单,逐个处理
|
||||||
|
if (menus.value && Array.isArray(menus.value)) {
|
||||||
|
menus.value.forEach((rootMenu) => {
|
||||||
|
processMenuNode(rootMenu);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
function isEnableChange(value, item) {
|
||||||
|
quickApi
|
||||||
|
.edit({
|
||||||
|
...item,
|
||||||
|
status: value,
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
ElMessage({
|
||||||
|
message: "修改成功",
|
||||||
|
type: "success",
|
||||||
|
});
|
||||||
|
Refresh();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function deleteHandle(item) {
|
||||||
|
quickApi.delete([item.id]).then(() => {
|
||||||
|
Refresh();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
onMounted(async () => {
|
||||||
|
await getMenus();
|
||||||
|
getList();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.container {
|
||||||
|
padding: 14px;
|
||||||
|
width: 100%;
|
||||||
|
.table-box {
|
||||||
|
border-radius: 8px;
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
95
src/layout/components/FastMenu/index.vue
Normal file
95
src/layout/components/FastMenu/index.vue
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
<template>
|
||||||
|
<div class="fast-menu">
|
||||||
|
<el-dropdown
|
||||||
|
trigger="click"
|
||||||
|
@command="menuClick"
|
||||||
|
placement="top-end"
|
||||||
|
@visible-change="handleVisibleChange"
|
||||||
|
append-to-body="true"
|
||||||
|
>
|
||||||
|
<img class="img" :src="imgsrc" alt="" />
|
||||||
|
<!-- <el-icon color="#fff" size="24px">
|
||||||
|
<Plus v-if="!showMenu" />
|
||||||
|
<Close v-else />
|
||||||
|
</el-icon> -->
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item
|
||||||
|
v-for="(item, index) in quickStore.quickMenus"
|
||||||
|
:key="index"
|
||||||
|
:command="item.menuId"
|
||||||
|
>
|
||||||
|
{{ returnMenuName(item.menuId) }}
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item :command="-1">
|
||||||
|
<el-icon>
|
||||||
|
<Edit color="#4080FF" />
|
||||||
|
</el-icon>
|
||||||
|
<span style="color: #4080ff">编辑</span>
|
||||||
|
</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, computed } from "vue";
|
||||||
|
import { useQuickStore, usePermissionStore, useUserStore } from "@/store";
|
||||||
|
import router from "@/router";
|
||||||
|
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const quickStore = useQuickStore();
|
||||||
|
const permissionStore = usePermissionStore();
|
||||||
|
console.log("permissionStore", permissionStore.menus);
|
||||||
|
|
||||||
|
function menuClick(menuId: number) {
|
||||||
|
console.log("menuId", menuId);
|
||||||
|
if (menuId == -1) {
|
||||||
|
if (userStore.userInfo.id == 1) {
|
||||||
|
return router.push("/system/commonlyUsedMenu");
|
||||||
|
} else {
|
||||||
|
return router.push("/commonlyUsedMenu");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
permissionStore.menuJump(menuId);
|
||||||
|
}
|
||||||
|
|
||||||
|
function returnMenuName(menuId: number) {
|
||||||
|
return permissionStore.returnMenuName(menuId);
|
||||||
|
}
|
||||||
|
|
||||||
|
const imgsrc = computed(() => {
|
||||||
|
return showMenu.value
|
||||||
|
? new URL("/src/assets/images/close.png", import.meta.url).href
|
||||||
|
: new URL("/src/assets/images/plus.png", import.meta.url).href;
|
||||||
|
});
|
||||||
|
const showMenu = ref(false);
|
||||||
|
function handleVisibleChange(visible: boolean) {
|
||||||
|
showMenu.value = visible;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.fast-menu {
|
||||||
|
position: fixed;
|
||||||
|
$size: 40px;
|
||||||
|
right: 40px;
|
||||||
|
bottom: 40px;
|
||||||
|
width: $size;
|
||||||
|
height: $size;
|
||||||
|
.img {
|
||||||
|
width: $size;
|
||||||
|
height: $size;
|
||||||
|
}
|
||||||
|
// box-shadow: 0 0 10px rgba(0, 0, 0, 0.4);
|
||||||
|
// background: linear-gradient(135deg, #409eff, #3a84ff);
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 10;
|
||||||
|
.svg-icon {
|
||||||
|
margin-right: 0;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -27,10 +27,11 @@
|
|||||||
<TagsView v-if="showTagsView" />
|
<TagsView v-if="showTagsView" />
|
||||||
<AppMain />
|
<AppMain />
|
||||||
<Settings v-if="defaultSettings.showSettings" />
|
<Settings v-if="defaultSettings.showSettings" />
|
||||||
|
|
||||||
<!-- 返回顶部 -->
|
<!-- 返回顶部 -->
|
||||||
<el-backtop target=".app-main">
|
<!-- <el-backtop target=".app-main">
|
||||||
<svg-icon icon-class="backtop" size="24px" />
|
<svg-icon icon-class="backtop" size="24px" />
|
||||||
</el-backtop>
|
</el-backtop> -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -42,11 +43,15 @@
|
|||||||
</div>
|
</div>
|
||||||
<AppMain />
|
<AppMain />
|
||||||
<Settings v-if="defaultSettings.showSettings" />
|
<Settings v-if="defaultSettings.showSettings" />
|
||||||
|
|
||||||
<!-- 返回顶部 -->
|
<!-- 返回顶部 -->
|
||||||
<el-backtop target=".app-main">
|
<!-- <el-backtop target=".app-main">
|
||||||
<svg-icon icon-class="backtop" size="24px" />
|
<svg-icon icon-class="backtop" size="24px" />
|
||||||
</el-backtop>
|
</el-backtop> -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 悬浮球 -->
|
||||||
|
<FastMenu></FastMenu>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -105,6 +110,7 @@ function handleOutsideClick() {
|
|||||||
function toggleSidebar() {
|
function toggleSidebar() {
|
||||||
appStore.toggleSidebar();
|
appStore.toggleSidebar();
|
||||||
}
|
}
|
||||||
|
function showFastMenu() {}
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
watch(route, () => {
|
watch(route, () => {
|
||||||
|
|||||||
@@ -14,5 +14,6 @@ export * from "./modules/settings";
|
|||||||
export * from "./modules/tags-view";
|
export * from "./modules/tags-view";
|
||||||
export * from "./modules/user";
|
export * from "./modules/user";
|
||||||
export * from "./modules/dict";
|
export * from "./modules/dict";
|
||||||
|
export * from "./modules/quick";
|
||||||
|
|
||||||
export { store };
|
export { store };
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export const usePermissionStore = defineStore("permission", () => {
|
|||||||
// 路由是否加载完成
|
// 路由是否加载完成
|
||||||
const isRoutesLoaded = ref(false);
|
const isRoutesLoaded = ref(false);
|
||||||
|
|
||||||
|
const menus = ref<RouteVO[]>([])
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -31,10 +32,9 @@ export const usePermissionStore = defineStore("permission", () => {
|
|||||||
}
|
}
|
||||||
MenuAPI.getRoutes()
|
MenuAPI.getRoutes()
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
|
menus.value = data
|
||||||
if (!isTest) {
|
if (!isTest) {
|
||||||
const dynamicRoutes = parseDynamicRoutes(data.filter(v => v.type == 0));
|
const dynamicRoutes = parseDynamicRoutes(data.filter(v => v.type == 0));
|
||||||
console.log('dynamicRoutes')
|
|
||||||
console.log(dynamicRoutes)
|
|
||||||
dynamicRoutes.forEach((route) => {
|
dynamicRoutes.forEach((route) => {
|
||||||
//过滤出可见子节点
|
//过滤出可见子节点
|
||||||
let onlyOneChild = null
|
let onlyOneChild = null
|
||||||
@@ -95,8 +95,60 @@ export const usePermissionStore = defineStore("permission", () => {
|
|||||||
});
|
});
|
||||||
isRoutesLoaded.value = false;
|
isRoutesLoaded.value = false;
|
||||||
};
|
};
|
||||||
|
function getMenuMap() {
|
||||||
|
// 初始化Map
|
||||||
|
const map = new Map();
|
||||||
|
|
||||||
|
// 定义递归函数处理菜单节点
|
||||||
|
function processMenuNode(menuNode) {
|
||||||
|
// 将当前节点存入Map
|
||||||
|
map.set(menuNode.menuId, menuNode);
|
||||||
|
|
||||||
|
// 如果有子节点且子节点数组不为空,则递归处理每个子节点
|
||||||
|
if (menuNode.children && Array.isArray(menuNode.children) && menuNode.children.length > 0) {
|
||||||
|
menuNode.children.forEach((childNode) => {
|
||||||
|
processMenuNode(childNode);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 遍历根级菜单,逐个处理
|
||||||
|
if (menus.value && Array.isArray(menus.value)) {
|
||||||
|
menus.value.forEach((rootMenu) => {
|
||||||
|
processMenuNode(rootMenu);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
function returnMenuName(menuId: number | string) {
|
||||||
|
const menu = menusIdMap.value.get(`${menuId}`);
|
||||||
|
return menu ? menu.title : '';
|
||||||
|
}
|
||||||
|
const menusIdMap = computed(() => {
|
||||||
|
const map = getMenuMap();
|
||||||
|
return map;
|
||||||
|
});
|
||||||
|
|
||||||
|
function menuJump(menuId: number | string) {
|
||||||
|
const menu = menusIdMap.value.get(`${menuId}`);
|
||||||
|
console.log('menu', menu);
|
||||||
|
if (menu) {
|
||||||
|
if (menu.name) {
|
||||||
|
router.push({ name: menu.name as string })
|
||||||
|
|
||||||
|
} else {
|
||||||
|
router.push({ path: menu.path as string })
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
menuJump,
|
||||||
|
returnMenuName,
|
||||||
|
menus,
|
||||||
routes,
|
routes,
|
||||||
mixedLayoutLeftRoutes,
|
mixedLayoutLeftRoutes,
|
||||||
isRoutesLoaded,
|
isRoutesLoaded,
|
||||||
|
|||||||
37
src/store/modules/quick.ts
Normal file
37
src/store/modules/quick.ts
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
|
||||||
|
import { store } from "@/store";
|
||||||
|
import quickApi, { type QuickMenu } from "@/api/account/quick";
|
||||||
|
import { usePermissionStoreHook } from "@/store/modules/permission";
|
||||||
|
|
||||||
|
|
||||||
|
export const useQuickStore = defineStore("quick", () => {
|
||||||
|
const quickMenus = ref<QuickMenu[]>([]);
|
||||||
|
|
||||||
|
async function getQuickMenus() {
|
||||||
|
const res = await quickApi.getList({ isEdit: 0 });
|
||||||
|
const obj: any = {}
|
||||||
|
let arr = []
|
||||||
|
for (let menu of res) {
|
||||||
|
if (obj.hasOwnProperty(menu.menuId) || menu.status != 1 || !usePermissionStoreHook().returnMenuName(menu.menuId)) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
obj[menu.menuId] = true;
|
||||||
|
arr.push(menu)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
quickMenus.value = arr;
|
||||||
|
}
|
||||||
|
getQuickMenus()
|
||||||
|
return {
|
||||||
|
quickMenus, getQuickMenus
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用于在组件外部(如在Pinia Store 中)使用 Pinia 提供的 store 实例。
|
||||||
|
* 官方文档解释了如何在组件外部使用 Pinia Store:
|
||||||
|
* https://pinia.vuejs.org/core-concepts/outside-component-usage.html#using-a-store-outside-of-a-component
|
||||||
|
*/
|
||||||
|
export function useQuickStoreHook() {
|
||||||
|
return useQuickStore(store);
|
||||||
|
}
|
||||||
5
src/views/admin/system/commonlyUsedMenu/index.vue
Normal file
5
src/views/admin/system/commonlyUsedMenu/index.vue
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<FastMenuConfig></FastMenuConfig>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -56,7 +56,7 @@
|
|||||||
</el-icon>
|
</el-icon>
|
||||||
</div>
|
</div>
|
||||||
<div class="info">
|
<div class="info">
|
||||||
<div class="m">¥{{ payCount.totalAmount || 0 }}</div>
|
<div class="m">¥{{ multiplyAndFormat(payCount.totalAmount || 0) }}</div>
|
||||||
<div class="t">总金额</div>
|
<div class="t">总金额</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -67,7 +67,7 @@
|
|||||||
</el-icon>
|
</el-icon>
|
||||||
</div>
|
</div>
|
||||||
<div class="info">
|
<div class="info">
|
||||||
<div class="m">¥{{ payCount.refundAmount || 0 }}</div>
|
<div class="m">¥{{ multiplyAndFormat(payCount.refundAmount || 0) }}</div>
|
||||||
<div class="t">退款金额</div>
|
<div class="t">退款金额</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -100,7 +100,7 @@
|
|||||||
</el-icon>
|
</el-icon>
|
||||||
</div>
|
</div>
|
||||||
<div class="info">
|
<div class="info">
|
||||||
<div class="m">{{ payCount.totalAmount - payCount.refundAmount || 0 }}</div>
|
<div class="m">{{ multiplyAndFormat((payCount.totalAmount - payCount.refundAmount) || 0) }}</div>
|
||||||
<div class="t">实际销售额总和</div>
|
<div class="t">实际销售额总和</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -111,7 +111,7 @@
|
|||||||
</el-icon>
|
</el-icon>
|
||||||
</div>
|
</div>
|
||||||
<div class="info">
|
<div class="info">
|
||||||
<div class="m">{{ payCount.saleCount - payCount.refundCount || 0 }}</div>
|
<div class="m">{{ multiplyAndFormat((payCount.saleCount - payCount.refundCount) || 0) }}</div>
|
||||||
<div class="t">实际销量总和</div>
|
<div class="t">实际销量总和</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -152,10 +152,10 @@
|
|||||||
<template v-slot="scope">¥{{ scope.row.refund }}</template>
|
<template v-slot="scope">¥{{ scope.row.refund }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="实际销量" prop="refundCount">
|
<el-table-column label="实际销量" prop="refundCount">
|
||||||
<template v-slot="scope"> {{ scope.row.saleCount - scope.row.refundCount }} </template>
|
<template v-slot="scope"> {{ multiplyAndFormat(scope.row.saleCount - scope.row.refundCount) }} </template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="实际销售额" prop="refundCount">
|
<el-table-column label="实际销售额" prop="refundCount">
|
||||||
<template v-slot="scope"> ¥{{ scope.row.saleAmount - scope.row.refundAmount }} </template>
|
<template v-slot="scope"> ¥{{ multiplyAndFormat(scope.row.saleAmount - scope.row.refundAmount) }} </template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
<el-table :data="tableData.data" v-loading="tableData.loading" v-if="orderType == 2">
|
<el-table :data="tableData.data" v-loading="tableData.loading" v-if="orderType == 2">
|
||||||
@@ -165,17 +165,17 @@
|
|||||||
<el-table-column label="商品名称" prop="productName"></el-table-column>
|
<el-table-column label="商品名称" prop="productName"></el-table-column>
|
||||||
<el-table-column label="销量" prop="saleCount" sortable></el-table-column>
|
<el-table-column label="销量" prop="saleCount" sortable></el-table-column>
|
||||||
<el-table-column label="销售金额" prop="saleAmount" sortable>
|
<el-table-column label="销售金额" prop="saleAmount" sortable>
|
||||||
<template v-slot="scope">¥{{ scope.row.saleAmount }}</template>
|
<template v-slot="scope">¥{{ multiplyAndFormat(scope.row.saleAmount) }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="退单量" prop="refundCount" sortable></el-table-column>
|
<el-table-column label="退单量" prop="refundCount" sortable></el-table-column>
|
||||||
<el-table-column label="退款金额" prop="refundAmount" sortable>
|
<el-table-column label="退款金额" prop="refundAmount" sortable>
|
||||||
<template v-slot="scope">¥{{ scope.row.refundAmount }}</template>
|
<template v-slot="scope">¥{{ multiplyAndFormat(scope.row.refundAmount) }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="实际销量" prop="refundCount" sortable>
|
<el-table-column label="实际销量" prop="refundCount" sortable>
|
||||||
<template v-slot="scope"> {{ scope.row.saleCount - scope.row.refundCount }} </template>
|
<template v-slot="scope"> {{ multiplyAndFormat(scope.row.saleCount - scope.row.refundCount) }} </template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="实际销售额" prop="refundCount" sortable>
|
<el-table-column label="实际销售额" prop="refundCount" sortable>
|
||||||
<template v-slot="scope"> ¥{{ scope.row.saleAmount - scope.row.refundAmount }} </template>
|
<template v-slot="scope"> ¥{{ multiplyAndFormat(scope.row.saleAmount - scope.row.refundAmount) }} </template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
</div>
|
</div>
|
||||||
@@ -193,12 +193,13 @@ import saleSummaryApi from "@/api/order/sale-summary";
|
|||||||
import categoryApi from "@/api/product/productclassification";
|
import categoryApi from "@/api/product/productclassification";
|
||||||
import ShopApi from "@/api/account/shop";
|
import ShopApi from "@/api/account/shop";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { downloadFile } from "@/utils/index";
|
import { downloadFile, multiplyAndFormat } from "@/utils/index";
|
||||||
import { formatDateRange } from './utils/index.js'
|
import { formatDateRange } from './utils/index.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
multiplyAndFormat,
|
||||||
timeValue: "today",
|
timeValue: "today",
|
||||||
resetQuery: null,
|
resetQuery: null,
|
||||||
orderType: "2",
|
orderType: "2",
|
||||||
@@ -314,7 +315,8 @@ export default {
|
|||||||
endDate: this.query.createdAt[1],
|
endDate: this.query.createdAt[1],
|
||||||
categoryId: this.query.prodCategoryId,
|
categoryId: this.query.prodCategoryId,
|
||||||
productName: this.query.productName,
|
productName: this.query.productName,
|
||||||
shopId: this.shopId
|
shopId: this.shopId,
|
||||||
|
rangeType: this.timeValue,
|
||||||
});
|
});
|
||||||
downloadFile(file, "数据", "xlsx");
|
downloadFile(file, "数据", "xlsx");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -187,7 +187,8 @@ export default {
|
|||||||
const file = await tableSummaryApi.export({
|
const file = await tableSummaryApi.export({
|
||||||
beginDate: this.query.createdAt[0],
|
beginDate: this.query.createdAt[0],
|
||||||
endDate: this.query.createdAt[1],
|
endDate: this.query.createdAt[1],
|
||||||
shopId: this.shopId
|
shopId: this.shopId,
|
||||||
|
rangeType: this.timeValue,
|
||||||
});
|
});
|
||||||
downloadFile(file, "数据", "xlsx");
|
downloadFile(file, "数据", "xlsx");
|
||||||
this.downloadLoading = false;
|
this.downloadLoading = false;
|
||||||
|
|||||||
@@ -12,7 +12,15 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="耗材价格" prop="price">
|
<el-form-item label="耗材价格" prop="price">
|
||||||
<el-input-number v-model="form.price" placeholder="请输入耗材价格"></el-input-number>
|
<div class="center">
|
||||||
|
<el-input-number v-model="form.price" placeholder="请输入" @change="onPriceChange"></el-input-number>
|
||||||
|
<span>{{ form.conUnit }}</span>
|
||||||
|
<div class="ipt">
|
||||||
|
第二单位价格:
|
||||||
|
<el-input-number v-model="twoPrice" placeholder="请输入" @change="onTwoPriceChange"></el-input-number>
|
||||||
|
<span>{{ form.conUnitTwo }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<!-- <el-form-item label="库存">
|
<!-- <el-form-item label="库存">
|
||||||
<el-input-number v-model="form.stockNumber" placeholder="请输入库存值"></el-input-number>
|
<el-input-number v-model="form.stockNumber" placeholder="请输入库存值"></el-input-number>
|
||||||
@@ -23,7 +31,7 @@
|
|||||||
<el-form-item label="单位" prop="conUnit">
|
<el-form-item label="单位" prop="conUnit">
|
||||||
<el-input v-model="form.conUnit" placeholder="请输入单位"></el-input>
|
<el-input v-model="form.conUnit" placeholder="请输入单位"></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-alert class="u-m-t-10 u-m-b-10" title="提示" description="换算值为第二单位*第二单位转换数量=第一单位" type="warning" show-icon
|
<el-alert class="u-m-t-10 u-m-b-10" title="提示" description="换算值为第一单位*第二单位转换数量=第二单位" type="warning" show-icon
|
||||||
:closable="false"></el-alert>
|
:closable="false"></el-alert>
|
||||||
<el-form-item label="预警值">
|
<el-form-item label="预警值">
|
||||||
<el-input-number v-model="form.conWarning" placeholder="请输入耗材预警值"></el-input-number>
|
<el-input-number v-model="form.conWarning" placeholder="请输入耗材预警值"></el-input-number>
|
||||||
@@ -50,9 +58,11 @@
|
|||||||
|
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { ref, reactive, computed } from "vue";
|
||||||
import consApi from "@/api/product/cons";
|
import consApi from "@/api/product/cons";
|
||||||
import consGroupApi from "@/api/product/cons-group";
|
import consGroupApi from "@/api/product/cons-group";
|
||||||
import { ElMessage } from "element-plus";
|
import { ElMessage } from "element-plus";
|
||||||
|
|
||||||
const consGroups = ref([]);
|
const consGroups = ref([]);
|
||||||
const rules = {
|
const rules = {
|
||||||
conName: [{ required: true, message: "请输入耗材名称", trigger: "blur" }],
|
conName: [{ required: true, message: "请输入耗材名称", trigger: "blur" }],
|
||||||
@@ -94,6 +104,34 @@ const form = reactive({
|
|||||||
...basicForm,
|
...basicForm,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 第二单位价格为计算字段:第一单位价格 * 第二单位转换数量
|
||||||
|
const twoPrice = computed({
|
||||||
|
get() {
|
||||||
|
const p = Number(form.price || 0);
|
||||||
|
const c = Number(form.conUnitTwoConvert || 0);
|
||||||
|
const val = c ? p * c : 0;
|
||||||
|
return parseFloat(val.toFixed(4));
|
||||||
|
},
|
||||||
|
set(val) {
|
||||||
|
const c = Number(form.conUnitTwoConvert || 0);
|
||||||
|
if (!c || !isFinite(Number(val))) return;
|
||||||
|
// 修改第二单位价格时,反算第一单位价格并保留四位小数
|
||||||
|
form.price = parseFloat((Number(val) / c).toFixed(4));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
function onPriceChange(val) {
|
||||||
|
if (!isFinite(Number(val))) return;
|
||||||
|
form.price = parseFloat(Number(val).toFixed(4));
|
||||||
|
}
|
||||||
|
|
||||||
|
function onTwoPriceChange(val) {
|
||||||
|
if (!isFinite(Number(val))) return;
|
||||||
|
const c = Number(form.conUnitTwoConvert || 0);
|
||||||
|
if (!c) return;
|
||||||
|
form.price = parseFloat((Number(val) / c).toFixed(4));
|
||||||
|
}
|
||||||
|
|
||||||
const show = ref(false);
|
const show = ref(false);
|
||||||
let dialogtitle = ref("");
|
let dialogtitle = ref("");
|
||||||
function open(item) {
|
function open(item) {
|
||||||
@@ -171,4 +209,18 @@ defineExpose({
|
|||||||
open,
|
open,
|
||||||
close,
|
close,
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.center {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.ipt {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
margin-left: 28px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -280,9 +280,9 @@ export default {
|
|||||||
return price;
|
return price;
|
||||||
}
|
}
|
||||||
if (row.conUnitTwo && row.unit == row.conUnitTwo) {
|
if (row.conUnitTwo && row.unit == row.conUnitTwo) {
|
||||||
console.log(price / row.conUnitTwoConvert);
|
// console.log(price / row.conUnitTwoConvert);
|
||||||
console.log(new Decimal(price).div(new Decimal(row.conUnitTwoConvert)));
|
// console.log(new Decimal(price).div(new Decimal(row.conUnitTwoConvert)));
|
||||||
return new Decimal(price).div(new Decimal(row.conUnitTwoConvert));
|
return new Decimal(price).mul(new Decimal(row.conUnitTwoConvert));
|
||||||
}
|
}
|
||||||
return price;
|
return price;
|
||||||
},
|
},
|
||||||
@@ -294,7 +294,7 @@ export default {
|
|||||||
return number;
|
return number;
|
||||||
}
|
}
|
||||||
if (row.conUnitTwo && row.unit == row.conUnitTwo) {
|
if (row.conUnitTwo && row.unit == row.conUnitTwo) {
|
||||||
return number * row.conUnitTwoConvert;
|
return number / row.conUnitTwoConvert;
|
||||||
}
|
}
|
||||||
return number;
|
return number;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -14,7 +14,9 @@ const contentConfig: IContentConfig = {
|
|||||||
pageSizes: [10, 20, 30, 50],
|
pageSizes: [10, 20, 30, 50],
|
||||||
},
|
},
|
||||||
indexAction: function (params) {
|
indexAction: function (params) {
|
||||||
return Api.getList({});
|
return Api.getList({
|
||||||
|
...params,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
deleteAction: function (id) {
|
deleteAction: function (id) {
|
||||||
return Api.delete(id);
|
return Api.delete(id);
|
||||||
@@ -79,7 +81,7 @@ const contentConfig: IContentConfig = {
|
|||||||
fixed: "right",
|
fixed: "right",
|
||||||
width: 280,
|
width: 280,
|
||||||
templet: "tool",
|
templet: "tool",
|
||||||
operat: [{ text: "编辑", icon: 'edit', name: "edit"}, { text: "删除", icon: 'delete', type: 'danger', name: "delete"}],
|
operat: [{ text: "编辑", icon: 'edit', name: "edit" }, { text: "删除", icon: 'delete', type: 'danger', name: "delete" }],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -336,7 +336,8 @@ async function confirmRefundHandle(form) {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
}
|
}
|
||||||
tableData.loading = false
|
getTableData()
|
||||||
|
wareCountAjax()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 审核退款操作
|
// 审核退款操作
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ function returnPrice(skuList) {
|
|||||||
const emits = defineEmits(['success'])
|
const emits = defineEmits(['success'])
|
||||||
function selectItem(item) {
|
function selectItem(item) {
|
||||||
emits('success', {
|
emits('success', {
|
||||||
|
id: item.id,
|
||||||
coverImg: item.coverImg,
|
coverImg: item.coverImg,
|
||||||
name: item.name,
|
name: item.name,
|
||||||
price: returnPrice(item.skuList)
|
price: returnPrice(item.skuList)
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ const form = ref({
|
|||||||
watch(() => form.value.isEnable, (newValue, OldValue) => {
|
watch(() => form.value.isEnable, (newValue, OldValue) => {
|
||||||
if (loading.value == false) {
|
if (loading.value == false) {
|
||||||
if (newValue == 0) {
|
if (newValue == 0) {
|
||||||
ElMessageBox.confirm('关闭商品拼团所有未支付的订单都将自动取消,是否确定关闭?', '注意', {
|
ElMessageBox.confirm('关闭商品拼团所有未成团的订单都将自动取消,是否确定关闭?', '注意', {
|
||||||
confirmButtonText: '确认关闭',
|
confirmButtonText: '确认关闭',
|
||||||
type: 'danger'
|
type: 'danger'
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
|
|||||||
@@ -56,8 +56,13 @@
|
|||||||
<el-table-column label="操作" width="150">
|
<el-table-column label="操作" width="150">
|
||||||
<template v-slot="scope">
|
<template v-slot="scope">
|
||||||
<template v-if="scope.row.shopId === shopInfo.id">
|
<template v-if="scope.row.shopId === shopInfo.id">
|
||||||
<el-button link type="primary" @click="addGroupGoodsRef.show(scope.row)">编辑</el-button>
|
<template v-if="scope.row.onlineStatus">
|
||||||
<el-button type="danger" link @click="delHandle(scope.row)">删除</el-button>
|
<el-button link type="primary" disabled>(下架后编辑/删除)</el-button>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<el-button link type="primary" @click="addGroupGoodsRef.show(scope.row)">编辑</el-button>
|
||||||
|
<el-button link type="danger" @click="delHandle(scope.row)">删除</el-button>
|
||||||
|
</template>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<el-button link type="primary" disabled>无权操作</el-button>
|
<el-button link type="primary" disabled>无权操作</el-button>
|
||||||
|
|||||||
@@ -14,14 +14,22 @@ const contentConfig: IContentConfig = {
|
|||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
pageSizes: [10, 20, 30, 50],
|
pageSizes: [10, 20, 30, 50],
|
||||||
},
|
},
|
||||||
indexAction: function (params) {
|
async indexAction(params) {
|
||||||
console.log(params);
|
console.log(params);
|
||||||
if (params.createAt) {
|
if (params.createAt) {
|
||||||
params.startTime = params.createAt[0];
|
params.startTime = params.createAt[0];
|
||||||
params.endTime = params.createAt[1];
|
params.endTime = params.createAt[1];
|
||||||
delete params.createAt;
|
delete params.createAt;
|
||||||
}
|
}
|
||||||
return OrderApi.getList(params);
|
|
||||||
|
const res = await OrderApi.getList(params)
|
||||||
|
console.log('OrderApi.getList', res);
|
||||||
|
|
||||||
|
res.records.map(item => {
|
||||||
|
item.payLoading = false
|
||||||
|
item.printStatus = JSON.parse(item.printStatus || '[]')
|
||||||
|
})
|
||||||
|
return res;
|
||||||
},
|
},
|
||||||
indexActionData: {},
|
indexActionData: {},
|
||||||
// deleteAction: OrderApi.delete,
|
// deleteAction: OrderApi.delete,
|
||||||
@@ -93,7 +101,14 @@ const contentConfig: IContentConfig = {
|
|||||||
templet: "custom",
|
templet: "custom",
|
||||||
slotName: "options",
|
slotName: "options",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: "打印状态",
|
||||||
|
align: "center",
|
||||||
|
prop: "printStatus",
|
||||||
|
width: 120,
|
||||||
|
templet: "custom",
|
||||||
|
slotName: "printStatus",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: "状态",
|
label: "状态",
|
||||||
align: "center",
|
align: "center",
|
||||||
@@ -116,7 +131,7 @@ const contentConfig: IContentConfig = {
|
|||||||
label: "操作",
|
label: "操作",
|
||||||
align: "center",
|
align: "center",
|
||||||
fixed: "right",
|
fixed: "right",
|
||||||
width: 150,
|
width: 180,
|
||||||
templet: "custom",
|
templet: "custom",
|
||||||
slotName: "operate",
|
slotName: "operate",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -51,6 +51,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<!-- 打印状态 -->
|
||||||
|
<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('、')}})
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
<template #options="scope">
|
<template #options="scope">
|
||||||
{{ returnOptionsLabel(scope.prop, scope.row[scope.prop]) }}
|
{{ returnOptionsLabel(scope.prop, scope.row[scope.prop]) }}
|
||||||
</template>
|
</template>
|
||||||
@@ -65,6 +72,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<template #operate="scope">
|
<template #operate="scope">
|
||||||
<div>
|
<div>
|
||||||
|
<el-button link @click="printOrderHandle(scope.row)">打印</el-button>
|
||||||
<el-button link @click="showdetail(scope.row)">详情</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 == '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)">
|
||||||
@@ -148,6 +156,19 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 打印订单
|
||||||
|
async function printOrderHandle(order: getListResponse) {
|
||||||
|
try {
|
||||||
|
await orderApi.printOrder({
|
||||||
|
id: order.id,
|
||||||
|
type: 0
|
||||||
|
});
|
||||||
|
ElMessage.success("打印成功");
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error("打印失败,请稍后重试");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 新增
|
// 新增
|
||||||
async function handleAddClick() {
|
async function handleAddClick() {
|
||||||
addModalRef.value?.setModalVisible();
|
addModalRef.value?.setModalVisible();
|
||||||
|
|||||||
@@ -2,26 +2,14 @@
|
|||||||
<div class="app-container">
|
<div class="app-container">
|
||||||
<!-- 列表 -->
|
<!-- 列表 -->
|
||||||
<!-- 搜索 -->
|
<!-- 搜索 -->
|
||||||
<page-search
|
<page-search ref="searchRef" :search-config="searchConfig" @query-click="newHandleQueryClick"
|
||||||
ref="searchRef"
|
@reset-click="handleResetClick2" />
|
||||||
:search-config="searchConfig"
|
|
||||||
@query-click="newHandleQueryClick"
|
|
||||||
@reset-click="handleResetClick2"
|
|
||||||
/>
|
|
||||||
<!-- 顶部数据 -->
|
<!-- 顶部数据 -->
|
||||||
<Statistics :data="gongjiData"></Statistics>
|
<Statistics :data="gongjiData"></Statistics>
|
||||||
<!-- 列表 -->
|
<!-- 列表 -->
|
||||||
<page-content
|
<page-content ref="contentRef" :content-config="contentConfig" @add-click="handleAddClick"
|
||||||
ref="contentRef"
|
@edit-click="handleEditClick" @export-click="handleExportClick" @search-click="handleSearchClick"
|
||||||
:content-config="contentConfig"
|
@toolbar-click="handleToolbarClick" @operat-click="handleOperatClick" @filter-change="handleFilterChange">
|
||||||
@add-click="handleAddClick"
|
|
||||||
@edit-click="handleEditClick"
|
|
||||||
@export-click="handleExportClick"
|
|
||||||
@search-click="handleSearchClick"
|
|
||||||
@toolbar-click="handleToolbarClick"
|
|
||||||
@operat-click="handleOperatClick"
|
|
||||||
@filter-change="handleFilterChange"
|
|
||||||
>
|
|
||||||
<!-- <template #status="scope">
|
<!-- <template #status="scope">
|
||||||
<el-tag :type="scope.row[scope.prop] == 1 ? 'success' : 'info'">
|
<el-tag :type="scope.row[scope.prop] == 1 ? 'success' : 'info'">
|
||||||
{{ scope.row[scope.prop] == 1 ? "启用" : "禁用" }}
|
{{ scope.row[scope.prop] == 1 ? "启用" : "禁用" }}
|
||||||
@@ -34,20 +22,11 @@
|
|||||||
<DictLabel v-model="scope.row[scope.prop]" code="gender" />
|
<DictLabel v-model="scope.row[scope.prop]" code="gender" />
|
||||||
</template>
|
</template>
|
||||||
<template #shangjia="scope">
|
<template #shangjia="scope">
|
||||||
<el-switch
|
<el-switch v-model="scope.row[scope.prop]" :active-value="1" :inactive-value="0"
|
||||||
v-model="scope.row[scope.prop]"
|
@click="handleSwitchChange(scope.row)"></el-switch>
|
||||||
:active-value="1"
|
|
||||||
:inactive-value="0"
|
|
||||||
@click="handleSwitchChange(scope.row)"
|
|
||||||
></el-switch>
|
|
||||||
</template>
|
</template>
|
||||||
<template #isStock="scope">
|
<template #isStock="scope">
|
||||||
<el-switch
|
<el-switch disabled v-model="scope.row[scope.prop]" :active-value="1" :inactive-value="0"></el-switch>
|
||||||
disabled
|
|
||||||
v-model="scope.row[scope.prop]"
|
|
||||||
:active-value="1"
|
|
||||||
:inactive-value="0"
|
|
||||||
></el-switch>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #kucunedit="scope">
|
<template #kucunedit="scope">
|
||||||
@@ -60,29 +39,16 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #tuikuantuihui="scope">
|
<template #tuikuantuihui="scope">
|
||||||
<el-switch
|
<el-switch v-if="!scope.row.productId" v-model="scope.row[scope.prop]" :active-value="1" :inactive-value="0"
|
||||||
v-if="!scope.row.productId"
|
@click="handleSwitchhaocai(scope.row)"></el-switch>
|
||||||
v-model="scope.row[scope.prop]"
|
|
||||||
:active-value="1"
|
|
||||||
:inactive-value="0"
|
|
||||||
@click="handleSwitchhaocai(scope.row)"
|
|
||||||
></el-switch>
|
|
||||||
</template>
|
</template>
|
||||||
<template #sellOut="scope">
|
<template #sellOut="scope">
|
||||||
<el-switch
|
<el-switch v-model="scope.row[scope.prop]" :active-value="1" :inactive-value="0"
|
||||||
v-model="scope.row[scope.prop]"
|
@click="handleSwitchChangeTwo(scope.row)"></el-switch>
|
||||||
:active-value="1"
|
|
||||||
:inactive-value="0"
|
|
||||||
@click="handleSwitchChangeTwo(scope.row)"
|
|
||||||
></el-switch>
|
|
||||||
</template>
|
</template>
|
||||||
<template #mobile="scope">
|
<template #mobile="scope">
|
||||||
<el-text>{{ scope.row[scope.prop] }}</el-text>
|
<el-text>{{ scope.row[scope.prop] }}</el-text>
|
||||||
<copy-button
|
<copy-button v-if="scope.row[scope.prop]" :text="scope.row[scope.prop]" style="margin-left: 2px" />
|
||||||
v-if="scope.row[scope.prop]"
|
|
||||||
:text="scope.row[scope.prop]"
|
|
||||||
style="margin-left: 2px"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
<template #consumables="scope">
|
<template #consumables="scope">
|
||||||
<template v-if="scope.row.type != null">
|
<template v-if="scope.row.type != null">
|
||||||
@@ -105,11 +71,7 @@
|
|||||||
</page-modal>
|
</page-modal>
|
||||||
|
|
||||||
<!-- 编辑 -->
|
<!-- 编辑 -->
|
||||||
<page-modal
|
<page-modal ref="editModalRef" :modal-config="editModalConfig" @submit-click="handleSubmitClick">
|
||||||
ref="editModalRef"
|
|
||||||
:modal-config="editModalConfig"
|
|
||||||
@submit-click="handleSubmitClick"
|
|
||||||
>
|
|
||||||
<template #gender="scope">
|
<template #gender="scope">
|
||||||
<Dict v-model="scope.formData[scope.prop]" code="gender" v-bind="scope.attrs" />
|
<Dict v-model="scope.formData[scope.prop]" code="gender" v-bind="scope.attrs" />
|
||||||
</template>
|
</template>
|
||||||
@@ -156,32 +118,24 @@
|
|||||||
<el-table-column label="序号" type="index" width="60"></el-table-column>
|
<el-table-column label="序号" type="index" width="60"></el-table-column>
|
||||||
<el-table-column label="耗材名称" prop="consInfoId">
|
<el-table-column label="耗材名称" prop="consInfoId">
|
||||||
<template v-slot="scope">
|
<template v-slot="scope">
|
||||||
<el-select
|
<el-select v-model="scope.row.consInfoId" reserve-keyword placeholder="请输入关键词"
|
||||||
v-model="scope.row.consInfoId"
|
@change="selectionChange($event, scope.row)">
|
||||||
reserve-keyword
|
<el-option v-for="item in options" :key="item.id" :label="item.conName" :value="item.id"></el-option>
|
||||||
placeholder="请输入关键词"
|
|
||||||
@change="selectionChange($event, scope.row)"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="item in options"
|
|
||||||
:key="item.id"
|
|
||||||
:label="item.conName"
|
|
||||||
:value="item.id"
|
|
||||||
></el-option>
|
|
||||||
</el-select>
|
</el-select>
|
||||||
<!-- <div class="tips" v-if="scope.row.stockNumber">库存:{{ scope.row.stockNumber }}</div> -->
|
<!-- <div class="tips" v-if="scope.row.stockNumber">库存:{{ scope.row.stockNumber }}</div> -->
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="单位" prop="conUnit">
|
<el-table-column label="单位" prop="conUnit">
|
||||||
<template v-slot="scope">
|
<template v-slot="scope">
|
||||||
<el-select v-model="scope.row.conUnit" reserve-keyword placeholder="请输入关键词">
|
<el-input v-model="scope.row.conUnit" readonly disabled placeholder="请选择耗材"></el-input>
|
||||||
|
<!-- <el-select v-model="scope.row.conUnit" reserve-keyword placeholder="请输入关键词">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in returnConUnits(scope.row.consInfoId)"
|
v-for="item in returnConUnits(scope.row.consInfoId)"
|
||||||
:key="item"
|
:key="item"
|
||||||
:label="item"
|
:label="item"
|
||||||
:value="item"
|
:value="item"
|
||||||
></el-option>
|
></el-option>
|
||||||
</el-select>
|
</el-select> -->
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="使用数量" prop="surplusStock" min-width="150px">
|
<el-table-column label="使用数量" prop="surplusStock" min-width="150px">
|
||||||
@@ -192,20 +146,13 @@
|
|||||||
<el-table-column label="操作" width="100">
|
<el-table-column label="操作" width="100">
|
||||||
<template v-slot="scope">
|
<template v-slot="scope">
|
||||||
<div class="table_btn_wrap">
|
<div class="table_btn_wrap">
|
||||||
<div
|
<div class="btn sub" v-if="haocaiData.consList.length > 1"
|
||||||
class="btn sub"
|
@click="haocaiData.consList.splice(scope.$index, 1)">
|
||||||
v-if="haocaiData.consList.length > 1"
|
|
||||||
@click="haocaiData.consList.splice(scope.$index, 1)"
|
|
||||||
>
|
|
||||||
<el-icon>
|
<el-icon>
|
||||||
<RemoveFilled />
|
<RemoveFilled />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div class="btn add" v-if="scope.$index == haocaiData.consList.length - 1" @click="createItem(scope.row)">
|
||||||
class="btn add"
|
|
||||||
v-if="scope.$index == haocaiData.consList.length - 1"
|
|
||||||
@click="createItem(scope.row)"
|
|
||||||
>
|
|
||||||
<el-icon>
|
<el-icon>
|
||||||
<CirclePlusFilled />
|
<CirclePlusFilled />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
|
|||||||
@@ -222,7 +222,6 @@
|
|||||||
<el-form-item label="库存数量">
|
<el-form-item label="库存数量">
|
||||||
<el-input-number v-model="ruleForm.stockNumber" :min="0" />
|
<el-input-number v-model="ruleForm.stockNumber" :min="0" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item label="打包费" prop="delivery">
|
<el-form-item label="打包费" prop="delivery">
|
||||||
<div style="display: block;">
|
<div style="display: block;">
|
||||||
<el-input-number v-model="ruleForm.packFee" controls-position="right"
|
<el-input-number v-model="ruleForm.packFee" controls-position="right"
|
||||||
@@ -230,6 +229,42 @@
|
|||||||
<div style="color: #999;">单份商品打包费。注:店铺开启外卖模式下该数据才生效</div>
|
<div style="color: #999;">单份商品打包费。注:店铺开启外卖模式下该数据才生效</div>
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="关联推荐商品">
|
||||||
|
<div class="column">
|
||||||
|
<div class="row">
|
||||||
|
<div class="center">
|
||||||
|
<el-button type="primary" @click="selecProductDialogRef?.show()"
|
||||||
|
:disabled="ruleForm.relatedRecommendJson.length >= goodsListMax">添加商品</el-button>
|
||||||
|
<div class="tips">设置商品后,用户可以在商品详情页中看到推荐商品可拖动调整顺序,最多设置{{ goodsListMax }}个商品</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div id="goods_table_drag">
|
||||||
|
<el-table :data="ruleForm.relatedRecommendJson" border stripe style="width: 500px;" row-key="id">
|
||||||
|
<!-- 拖拽列 -->
|
||||||
|
<el-table-column label="排序" width="60">
|
||||||
|
<template v-slot="scope">
|
||||||
|
<div class="drag-handle">☰</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="商品图片" prop="coverImg" width="90">
|
||||||
|
<template v-slot="scope">
|
||||||
|
<el-image :src="scope.row.coverImg" style="width: 50px;height: 50px;border-radius: 4px;"
|
||||||
|
fit="cover"></el-image>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="商品名称" prop="name"></el-table-column>
|
||||||
|
<el-table-column label="操作" width="100">
|
||||||
|
<template v-slot="scope">
|
||||||
|
<el-button link type="danger"
|
||||||
|
@click="ruleForm.relatedRecommendJson.splice(scope.$index, 1)">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" @click="submitForm(ruleFormRef)">确定</el-button>
|
<el-button type="primary" @click="submitForm(ruleFormRef)">确定</el-button>
|
||||||
<el-button @click="resetForm(ruleFormRef)">取消</el-button>
|
<el-button @click="resetForm(ruleFormRef)">取消</el-button>
|
||||||
@@ -256,11 +291,13 @@
|
|||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
<!-- 选择商品弹窗 -->
|
||||||
|
<selecProductDialog ref="selecProductDialogRef" @success="selecProductSuccess" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { reactive, ref } from "vue";
|
import { reactive, ref, onMounted, nextTick } from "vue";
|
||||||
import type { FormInstance, FormRules } from "element-plus";
|
import type { FormInstance, FormRules } from "element-plus";
|
||||||
// 规格属性
|
// 规格属性
|
||||||
import SpecificationAttribute from "./SpecificationAttribute.vue";
|
import SpecificationAttribute from "./SpecificationAttribute.vue";
|
||||||
@@ -272,7 +309,69 @@ import UserAPI4 from "@/api/product/specificationsconfig";
|
|||||||
import shopList from "@/components/mycomponents/shopList.vue";
|
import shopList from "@/components/mycomponents/shopList.vue";
|
||||||
import AddImg from "@/components/mycomponents/addImg.vue";
|
import AddImg from "@/components/mycomponents/addImg.vue";
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
|
import Sortable from "sortablejs";
|
||||||
import { useTagsViewStore } from "@/store";
|
import { useTagsViewStore } from "@/store";
|
||||||
|
import selecProductDialog from "@/views/marketing_center/group_booking/components/selecProductDialog.vue";
|
||||||
|
|
||||||
|
const selecProductDialogRef = ref(null)
|
||||||
|
const goodsListMax = ref(9)
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// Sortable 需要在 el-table 渲染 tbody 后初始化,尝试多次以确保 DOM 可用
|
||||||
|
const initSortable = async () => {
|
||||||
|
await nextTick();
|
||||||
|
let attempts = 0;
|
||||||
|
const maxAttempts = 8;
|
||||||
|
const tryInit = () => {
|
||||||
|
const el = document.querySelector("#goods_table_drag .el-table__body-wrapper tbody");
|
||||||
|
if (!el) {
|
||||||
|
attempts++;
|
||||||
|
if (attempts < maxAttempts) {
|
||||||
|
setTimeout(tryInit, 80);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 已找到表体,初始化 Sortable
|
||||||
|
new Sortable(el as HTMLElement, {
|
||||||
|
animation: 150,
|
||||||
|
handle: ".drag-handle",
|
||||||
|
ghostClass: "sortable-ghost",
|
||||||
|
chosenClass: "sortable-chosen",
|
||||||
|
onEnd: (e) => {
|
||||||
|
if (e.oldIndex == null || e.newIndex == null) return;
|
||||||
|
if (e.oldIndex === e.newIndex) return;
|
||||||
|
const from = e.oldIndex;
|
||||||
|
const to = e.newIndex;
|
||||||
|
const item = ruleForm.relatedRecommendJson.splice(from, 1)[0];
|
||||||
|
ruleForm.relatedRecommendJson.splice(to, 0, item);
|
||||||
|
// 触发响应式更新
|
||||||
|
ruleForm.relatedRecommendJson = ruleForm.relatedRecommendJson.slice();
|
||||||
|
console.log("排序后的数据", ruleForm.relatedRecommendJson);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
tryInit();
|
||||||
|
};
|
||||||
|
|
||||||
|
initSortable();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 已选择的商品
|
||||||
|
function selecProductSuccess(res: any) {
|
||||||
|
if (res.id == ruleForm.id) {
|
||||||
|
ElMessage.error("不能选择当前商品");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let obj = ruleForm.relatedRecommendJson.find((item) => item.id == res.id);
|
||||||
|
if (obj && obj.id) {
|
||||||
|
ElMessage.error("该商品已选择");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ruleForm.relatedRecommendJson.push({ ...res });
|
||||||
|
}
|
||||||
|
|
||||||
const tagsViewStore = useTagsViewStore();
|
const tagsViewStore = useTagsViewStore();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
let list = ref<any[]>([
|
let list = ref<any[]>([
|
||||||
@@ -338,6 +437,7 @@ interface datasForm {
|
|||||||
addGroupIndex: any;
|
addGroupIndex: any;
|
||||||
}
|
}
|
||||||
interface RuleForm {
|
interface RuleForm {
|
||||||
|
id: number | string;
|
||||||
name: string;
|
name: string;
|
||||||
shortTitle: string;
|
shortTitle: string;
|
||||||
unitId: string;
|
unitId: string;
|
||||||
@@ -362,9 +462,11 @@ interface RuleForm {
|
|||||||
sort: Number;
|
sort: Number;
|
||||||
proGroupVo: any[];
|
proGroupVo: any[];
|
||||||
selectSpecInfo: any;
|
selectSpecInfo: any;
|
||||||
|
relatedRecommendJson: any[];
|
||||||
}
|
}
|
||||||
const ruleFormRef = ref<FormInstance>();
|
const ruleFormRef = ref<FormInstance>();
|
||||||
const ruleForm = reactive<RuleForm>({
|
const ruleForm = reactive<RuleForm>({
|
||||||
|
id: '',
|
||||||
//商品名称
|
//商品名称
|
||||||
name: "",
|
name: "",
|
||||||
//商品介绍
|
//商品介绍
|
||||||
@@ -418,6 +520,7 @@ const ruleForm = reactive<RuleForm>({
|
|||||||
// 排序值
|
// 排序值
|
||||||
sort: 1,
|
sort: 1,
|
||||||
selectSpecInfo: {},
|
selectSpecInfo: {},
|
||||||
|
relatedRecommendJson: [] // 关联推荐商品
|
||||||
});
|
});
|
||||||
const rules = reactive<FormRules<RuleForm>>({
|
const rules = reactive<FormRules<RuleForm>>({
|
||||||
name: [{ required: true, message: "请输入商品名称", trigger: "blur" }],
|
name: [{ required: true, message: "请输入商品名称", trigger: "blur" }],
|
||||||
@@ -915,6 +1018,27 @@ const resetForm = (formEl: FormInstance | undefined) => {
|
|||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.drag-handle {
|
||||||
|
cursor: move;
|
||||||
|
display: inline-block;
|
||||||
|
padding: 4px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SortableJS classes */
|
||||||
|
.sortable-ghost {
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sortable-chosen {
|
||||||
|
background: #f5f7ff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-table__body-wrapper tbody tr {
|
||||||
|
transition: transform 150ms ease, background-color 150ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
.showStyle:hover>.buttonstyle {
|
.showStyle:hover>.buttonstyle {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
@@ -942,4 +1066,26 @@ const resetForm = (formEl: FormInstance | undefined) => {
|
|||||||
top: -10px;
|
top: -10px;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
}
|
}
|
||||||
</style>
|
|
||||||
|
.column {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.row {
|
||||||
|
flex: 1;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.center {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
.tips {
|
||||||
|
color: #999;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
5
src/views/shop/commonlyUsedMenu/index.vue
Normal file
5
src/views/shop/commonlyUsedMenu/index.vue
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<FastMenuConfig></FastMenuConfig>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
113
src/views/shop/config/components/contactSetting.vue
Normal file
113
src/views/shop/config/components/contactSetting.vue
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
<!-- 客服设置 -->
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-form ref="formRef" :model="form" :rules="rules" label-width="100" label-position="right">
|
||||||
|
<el-form-item label="企业ID" prop="weworkId">
|
||||||
|
<el-input v-model="form.weworkId" placeholder="请输入企业ID" style="width: 400px;"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="接入链接" prop="weworkUrl">
|
||||||
|
<el-input v-model="form.weworkUrl" placeholder="请输入接入链接" style="width: 400px;"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<div class="detail">
|
||||||
|
<h3>如何获取企业</h3>
|
||||||
|
<p>1.登录【企业微信管理后台】(<el-link type="primary" target="_blank"
|
||||||
|
href="https://work.weixin.qq.com">work.weixin.qq.com</el-link>)</p>
|
||||||
|
<p>2.获取企业ID:登录后,进入【我的企业>>企业信息>>企业ID】</p>
|
||||||
|
<el-image :src="imgs[0]" style="width: 600px;height: auto;" :preview-src-list="imgs"
|
||||||
|
:initial-index="0"></el-image>
|
||||||
|
<h3>如何获取接入链接</h3>
|
||||||
|
<p>1. 登录【企业微信管理后台】(<el-link type="primary" target="_blank"
|
||||||
|
href="https://work.weixin.qq.com">work.weixin.qq.com</el-link>)。
|
||||||
|
</p>
|
||||||
|
<p>2.找到《微信客服》应用,路径:【应用管理>> 应用管理>>应用>>微信客服】</p>
|
||||||
|
<el-image :src="imgs[1]" style="width: 600px;height: auto;" :preview-src-list="imgs"
|
||||||
|
:initial-index="1"></el-image>
|
||||||
|
<p>3.进入《微信客服》,找到《创建账号》按钮,点击后进入页面完成创建。(具体的接待时间、接待人员等商家可自行配置)</p>
|
||||||
|
<el-image :src="imgs[2]" style="width: 600px;height: auto;" :preview-src-list="imgs"
|
||||||
|
:initial-index="2"></el-image>
|
||||||
|
<p>4.创建成功后,再次点击刚刚创建的账号,进入后复制《接入链接》粘贴过来即可;特别说明,账号可选用已创建的账号;</p>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" :loading="loading" @click="submitHandle">保存</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, onMounted } from "vue";
|
||||||
|
import ShopApi from "@/api/account/shop";
|
||||||
|
|
||||||
|
const imgs = [
|
||||||
|
"https://cashier-oss.oss-cn-beijing.aliyuncs.com/upload/1/06f236d656be479284833a166a5b98c1.png",
|
||||||
|
"https://cashier-oss.oss-cn-beijing.aliyuncs.com/upload/1/96f3c5c4c1f940c390eef901bcd11a14.png",
|
||||||
|
"https://cashier-oss.oss-cn-beijing.aliyuncs.com/upload/1/5dbbd3a7d31c4bef8af57c6679421dd5.png",
|
||||||
|
];
|
||||||
|
|
||||||
|
const formRef = ref();
|
||||||
|
const loading = ref(false);
|
||||||
|
const form = ref({
|
||||||
|
id: '',
|
||||||
|
weworkId: "",
|
||||||
|
weworkUrl: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
const rules = {
|
||||||
|
weworkId: [{ required: true, message: "请输入企业ID", trigger: "blur" }],
|
||||||
|
weworkUrl: [{ required: true, message: "请输入接入链接", trigger: "blur" }],
|
||||||
|
};
|
||||||
|
|
||||||
|
// 提交表单
|
||||||
|
function submitHandle() {
|
||||||
|
formRef.value.validate(async (valid) => {
|
||||||
|
if (valid) {
|
||||||
|
try {
|
||||||
|
loading.value = true;
|
||||||
|
await ShopApi.edit(form.value);
|
||||||
|
ElNotification({
|
||||||
|
title: "成功",
|
||||||
|
message: "保存成功",
|
||||||
|
type: "success",
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
loading.value = false;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 获取店铺信息
|
||||||
|
async function getShopInfo() {
|
||||||
|
try {
|
||||||
|
const res = await ShopApi.get()
|
||||||
|
form.value.id = res.id;
|
||||||
|
form.value.weworkId = res.weworkId;
|
||||||
|
form.value.weworkUrl = res.weworkUrl;
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getShopInfo();
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.detail {
|
||||||
|
p {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.img {
|
||||||
|
width: 600px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -3,25 +3,14 @@
|
|||||||
<div>
|
<div>
|
||||||
<el-form ref="form" :model="form" :rules="rules" label-width="160px" label-position="left">
|
<el-form ref="form" :model="form" :rules="rules" label-width="160px" label-position="left">
|
||||||
<el-form-item label="门店名称" prop="shopName">
|
<el-form-item label="门店名称" prop="shopName">
|
||||||
<el-input
|
<el-input v-model.trim="form.shopName" placeholder="请输入门店名称" style="width: 500px"></el-input>
|
||||||
v-model.trim="form.shopName"
|
|
||||||
placeholder="请输入门店名称"
|
|
||||||
style="width: 500px"
|
|
||||||
></el-input>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="连锁店扩展店名">
|
<el-form-item label="连锁店扩展店名">
|
||||||
<el-input
|
<el-input v-model.trim="form.chainName" placeholder="请输入连锁店扩展店名" style="width: 500px"></el-input>
|
||||||
v-model.trim="form.chainName"
|
|
||||||
placeholder="请输入连锁店扩展店名"
|
|
||||||
style="width: 500px"
|
|
||||||
></el-input>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="门店logo">
|
<el-form-item label="门店logo">
|
||||||
<div class="img_box">
|
<div class="img_box">
|
||||||
<single-image-upload
|
<single-image-upload style="width: 80px; height: 80px" v-model="form.logo"></single-image-upload>
|
||||||
style="width: 80px; height: 80px"
|
|
||||||
v-model="form.logo"
|
|
||||||
></single-image-upload>
|
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<!-- <el-form-item label="门店照片">
|
<!-- <el-form-item label="门店照片">
|
||||||
@@ -38,22 +27,14 @@
|
|||||||
<el-form-item label="门店收款码">
|
<el-form-item label="门店收款码">
|
||||||
<div class="img_box">
|
<div class="img_box">
|
||||||
<canvas ref="canvas" id="QRCode_header" style="width: 80px; height: 80px"></canvas>
|
<canvas ref="canvas" id="QRCode_header" style="width: 80px; height: 80px"></canvas>
|
||||||
<el-button
|
<el-button size="small" plain v-if="form.paymentQrcode" @click="downloadCanvas(form.paymentQrcode)">
|
||||||
size="small"
|
|
||||||
plain
|
|
||||||
v-if="form.paymentQrcode"
|
|
||||||
@click="downloadCanvas(form.paymentQrcode)"
|
|
||||||
>
|
|
||||||
下载
|
下载
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="微信二维码">
|
<el-form-item label="微信二维码">
|
||||||
<div class="img_box">
|
<div class="img_box">
|
||||||
<single-image-upload
|
<single-image-upload style="width: 80px; height: 80px" v-model="form.shopQrcode"></single-image-upload>
|
||||||
style="width: 80px; height: 80px"
|
|
||||||
v-model="form.shopQrcode"
|
|
||||||
></single-image-upload>
|
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<!-- <el-form-item label="店铺小程序码">
|
<!-- <el-form-item label="店铺小程序码">
|
||||||
@@ -84,11 +65,7 @@
|
|||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item> -->
|
</el-form-item> -->
|
||||||
<el-form-item label="联系电话" prop="phone">
|
<el-form-item label="联系电话" prop="phone">
|
||||||
<el-input
|
<el-input v-model.trim="form.phone" placeholder="请输入联系电话" style="width: 500px"></el-input>
|
||||||
v-model.trim="form.phone"
|
|
||||||
placeholder="请输入联系电话"
|
|
||||||
style="width: 500px"
|
|
||||||
></el-input>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<!-- <el-form-item label="外卖起送金额">
|
<!-- <el-form-item label="外卖起送金额">
|
||||||
<el-input-number v-model="form.takeaway_money" placeholder="0.00" controls-position="right"
|
<el-input-number v-model="form.takeaway_money" placeholder="0.00" controls-position="right"
|
||||||
@@ -114,62 +91,30 @@
|
|||||||
<div style="color: #999">注:准确的定位便于用户导航到店铺</div>
|
<div style="color: #999">注:准确的定位便于用户导航到店铺</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="门店详细地址">
|
<el-form-item label="门店详细地址">
|
||||||
<el-input
|
<el-input type="textarea" v-model.trim="form.address" placeholder="请输入门店详细地址" style="width: 500px"></el-input>
|
||||||
type="textarea"
|
|
||||||
v-model.trim="form.address"
|
|
||||||
placeholder="请输入门店详细地址"
|
|
||||||
style="width: 500px"
|
|
||||||
></el-input>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="营业时间">
|
<el-form-item label="营业时间">
|
||||||
<div class="u-flex gap-2" style="width: 50%">
|
<div class="u-flex gap-2" style="width: 50%">
|
||||||
<el-select v-model="form.businessStartDay" placeholder="周几开始">
|
<el-select v-model="form.businessStartDay" placeholder="周几开始">
|
||||||
<el-option
|
<el-option :value="item.label" :label="item.label" v-for="item in weeks" :key="item.value"></el-option>
|
||||||
:value="item.label"
|
|
||||||
:label="item.label"
|
|
||||||
v-for="item in weeks"
|
|
||||||
:key="item.value"
|
|
||||||
></el-option>
|
|
||||||
</el-select>
|
</el-select>
|
||||||
<el-select v-model="form.businessEndDay" placeholder="周几结束">
|
<el-select v-model="form.businessEndDay" placeholder="周几结束">
|
||||||
<el-option
|
<el-option :value="item.label" :label="item.label" v-for="item in weeks" :key="item.value"></el-option>
|
||||||
:value="item.label"
|
|
||||||
:label="item.label"
|
|
||||||
v-for="item in weeks"
|
|
||||||
:key="item.value"
|
|
||||||
></el-option>
|
|
||||||
</el-select>
|
</el-select>
|
||||||
<el-time-picker
|
<el-time-picker placeholder="起始时间" v-model="startTime" :picker-options="{
|
||||||
placeholder="起始时间"
|
selectableRange: '00:00:00 - 23:59:59',
|
||||||
v-model="startTime"
|
format: 'HH:mm',
|
||||||
:picker-options="{
|
}" format="HH:mm" value-format="HH:mm"></el-time-picker>
|
||||||
selectableRange: '00:00:00 - 23:59:59',
|
<el-time-picker placeholder="结束时间" v-model="endTime" :picker-options="{
|
||||||
format: 'HH:mm',
|
selectableRange: '00:00:00 - 23:59:59',
|
||||||
}"
|
}" format="HH:mm" value-format="HH:mm"></el-time-picker>
|
||||||
format="HH:mm"
|
|
||||||
value-format="HH:mm"
|
|
||||||
></el-time-picker>
|
|
||||||
<el-time-picker
|
|
||||||
placeholder="结束时间"
|
|
||||||
v-model="endTime"
|
|
||||||
:picker-options="{
|
|
||||||
selectableRange: '00:00:00 - 23:59:59',
|
|
||||||
}"
|
|
||||||
format="HH:mm"
|
|
||||||
value-format="HH:mm"
|
|
||||||
></el-time-picker>
|
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="桌位费/位/元">
|
<el-form-item label="桌位费/位/元">
|
||||||
<el-input-number :disabled="!!form.isTableFee" v-model="form.tableFee" :min="0" />
|
<el-input-number :disabled="!!form.isTableFee" v-model="form.tableFee" :min="0" />
|
||||||
<!-- <el-checkbox v-model="form.isTableFee" :label="1">免餐位费</el-checkbox> -->
|
<!-- <el-checkbox v-model="form.isTableFee" :label="1">免餐位费</el-checkbox> -->
|
||||||
<el-switch
|
<el-switch class="u-m-l-10" v-model.trim="form.isTableFee" :active-value="1" :inactive-value="0"
|
||||||
class="u-m-l-10"
|
active-text="免餐位费"></el-switch>
|
||||||
v-model.trim="form.isTableFee"
|
|
||||||
:active-value="1"
|
|
||||||
:inactive-value="0"
|
|
||||||
active-text="免餐位费"
|
|
||||||
></el-switch>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<!-- <el-form-item label="是否开启8折活动">
|
<!-- <el-form-item label="是否开启8折活动">
|
||||||
<el-switch v-model.trim="form.isOpenYhq" active-value="true" inactive-value="false"></el-switch>
|
<el-switch v-model.trim="form.isOpenYhq" active-value="true" inactive-value="false"></el-switch>
|
||||||
@@ -203,41 +148,24 @@
|
|||||||
</el-time-picker>
|
</el-time-picker>
|
||||||
</el-form-item> -->
|
</el-form-item> -->
|
||||||
<el-form-item label="店铺简介">
|
<el-form-item label="店铺简介">
|
||||||
<el-input
|
<el-input type="textarea" v-model.trim="form.detail" placeholder="请输入店铺简介" style="width: 500px"></el-input>
|
||||||
type="textarea"
|
|
||||||
v-model.trim="form.detail"
|
|
||||||
placeholder="请输入店铺简介"
|
|
||||||
style="width: 500px"
|
|
||||||
></el-input>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="台桌预订短信">
|
<el-form-item label="台桌预订短信">
|
||||||
<el-input
|
<el-input type="textarea" v-model.trim="form.bookingSms" placeholder="请输入台桌预订短信"
|
||||||
type="textarea"
|
style="width: 500px"></el-input>
|
||||||
v-model.trim="form.bookingSms"
|
|
||||||
placeholder="请输入台桌预订短信"
|
|
||||||
style="width: 500px"
|
|
||||||
></el-input>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="电子围栏" prop="isOrderFence">
|
<el-form-item label="电子围栏" prop="isOrderFence">
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<div class="center" style="display: flex; align-items: center; gap: 14px">
|
<div class="center" style="display: flex; align-items: center; gap: 14px">
|
||||||
<el-switch
|
<el-switch v-model="form.isOrderFence" :active-value="1" :inactive-value="0"></el-switch>
|
||||||
v-model="form.isOrderFence"
|
|
||||||
:active-value="1"
|
|
||||||
:inactive-value="0"
|
|
||||||
></el-switch>
|
|
||||||
<div class="tips" style="font-size: 14px; color: #999">
|
<div class="tips" style="font-size: 14px; color: #999">
|
||||||
开启后,用户只能在店铺附近xx公里内点餐
|
开启后,用户只能在店铺附近xx公里内点餐
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="center" v-if="form.isOrderFence == 1">
|
<div class="center" v-if="form.isOrderFence == 1">
|
||||||
<el-input
|
<el-input v-model="form.orderFenceDistance" placeholder="请输入"
|
||||||
v-model="form.orderFenceDistance"
|
@input="(e) => (form.orderFenceDistance = filterNumberInput(e))" style="width: 250px"
|
||||||
placeholder="请输入"
|
input-style="text-align: center;">
|
||||||
@input="(e) => (form.orderFenceDistance = filterNumberInput(e))"
|
|
||||||
style="width: 250px"
|
|
||||||
input-style="text-align: center;"
|
|
||||||
>
|
|
||||||
<template #prepend>限制:</template>
|
<template #prepend>限制:</template>
|
||||||
<template #append>公里</template>
|
<template #append>公里</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
@@ -247,29 +175,24 @@
|
|||||||
<el-form-item label="上菜时间(分钟)" prop="isServeTimeControl">
|
<el-form-item label="上菜时间(分钟)" prop="isServeTimeControl">
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<div class="center" style="display: flex; align-items: center; gap: 14px">
|
<div class="center" style="display: flex; align-items: center; gap: 14px">
|
||||||
<el-switch
|
<el-switch v-model="form.isServeTimeControl" :active-value="1" :inactive-value="0"></el-switch>
|
||||||
v-model="form.isServeTimeControl"
|
|
||||||
:active-value="1"
|
|
||||||
:inactive-value="0"
|
|
||||||
></el-switch>
|
|
||||||
<div class="tips" style="font-size: 14px; color: #999">
|
<div class="tips" style="font-size: 14px; color: #999">
|
||||||
起菜到上菜的时间间隔,开启后会有超时提示
|
起菜到上菜的时间间隔,开启后会有超时提示
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="center" v-if="form.isServeTimeControl == 1">
|
<div class="center" v-if="form.isServeTimeControl == 1">
|
||||||
<el-input
|
<el-input v-model="form.serveTime" placeholder="请输入"
|
||||||
v-model="form.serveTime"
|
@input="(e) => (form.serveTime = filterNumberInput(e))" style="width: 250px"
|
||||||
placeholder="请输入"
|
input-style="text-align: center;">
|
||||||
@input="(e) => (form.serveTime = filterNumberInput(e))"
|
|
||||||
style="width: 250px"
|
|
||||||
input-style="text-align: center;"
|
|
||||||
>
|
|
||||||
<template #prepend>限制:</template>
|
<template #prepend>限制:</template>
|
||||||
<template #append>分钟</template>
|
<template #append>分钟</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="数签子功能">
|
||||||
|
<el-switch v-model="form.isCountStick" :active-value="1" :inactive-value="0"></el-switch>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item label="状态">
|
<el-form-item label="状态">
|
||||||
<el-radio-group v-model="form.status">
|
<el-radio-group v-model="form.status">
|
||||||
<el-radio :value="1">营业中</el-radio>
|
<el-radio :value="1">营业中</el-radio>
|
||||||
@@ -285,24 +208,11 @@
|
|||||||
</el-form>
|
</el-form>
|
||||||
</div>
|
</div>
|
||||||
<ChooseAddress ref="refChooseAddress" @choose="chooseAddressConfirm"></ChooseAddress>
|
<ChooseAddress ref="refChooseAddress" @choose="chooseAddressConfirm"></ChooseAddress>
|
||||||
<el-dialog
|
<el-dialog v-model="showUpload" :close-on-click-modal="false" append-to-body width="500px"
|
||||||
v-model="showUpload"
|
@close="showUpload = false">
|
||||||
:close-on-click-modal="false"
|
<el-upload :before-remove="handleBeforeRemove" :on-success="handleSuccess" :on-error="handleError"
|
||||||
append-to-body
|
:file-list="fileList" :headers="headers" :action="qiNiuUploadApi" :limit="1" list-type="picture"
|
||||||
width="500px"
|
class="upload-demo">
|
||||||
@close="showUpload = false"
|
|
||||||
>
|
|
||||||
<el-upload
|
|
||||||
:before-remove="handleBeforeRemove"
|
|
||||||
:on-success="handleSuccess"
|
|
||||||
:on-error="handleError"
|
|
||||||
:file-list="fileList"
|
|
||||||
:headers="headers"
|
|
||||||
:action="qiNiuUploadApi"
|
|
||||||
:limit="1"
|
|
||||||
list-type="picture"
|
|
||||||
class="upload-demo"
|
|
||||||
>
|
|
||||||
<el-button size="small" type="primary">点击上传</el-button>
|
<el-button size="small" type="primary">点击上传</el-button>
|
||||||
<template #tip>
|
<template #tip>
|
||||||
<div style="display: block" class="el-upload__tip">请勿上传违法文件,且文件不超过15M</div>
|
<div style="display: block" class="el-upload__tip">请勿上传违法文件,且文件不超过15M</div>
|
||||||
@@ -549,7 +459,7 @@ export default {
|
|||||||
handleBeforeRemove(file, fileList) {
|
handleBeforeRemove(file, fileList) {
|
||||||
for (let i = 0; i < this.files.length; i++) {
|
for (let i = 0; i < this.files.length; i++) {
|
||||||
if (this.files[i].uid === file.uid) {
|
if (this.files[i].uid === file.uid) {
|
||||||
crudQiNiu.del([this.files[i].id]).then((res) => {});
|
crudQiNiu.del([this.files[i].id]).then((res) => { });
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,11 +6,13 @@
|
|||||||
<el-tab-pane label="安全设置" name="4"></el-tab-pane>
|
<el-tab-pane label="安全设置" name="4"></el-tab-pane>
|
||||||
<el-tab-pane label="跳转小程序" name="6" v-if="shopId == 1"></el-tab-pane>
|
<el-tab-pane label="跳转小程序" name="6" v-if="shopId == 1"></el-tab-pane>
|
||||||
<el-tab-pane label="通知中心" name="7"></el-tab-pane>
|
<el-tab-pane label="通知中心" name="7"></el-tab-pane>
|
||||||
|
<el-tab-pane label="客服设置" name="8"></el-tab-pane>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
<shopInfo v-if="activeName == 1" />
|
<shopInfo v-if="activeName == 1" />
|
||||||
<safe v-if="activeName == 4" />
|
<safe v-if="activeName == 4" />
|
||||||
<notifications v-if="activeName == 7" />
|
<notifications v-if="activeName == 7" />
|
||||||
<!-- <goxcx v-if="activeName == 6" />-->
|
<!-- <goxcx v-if="activeName == 6" />-->
|
||||||
|
<contactSetting v-if="activeName == 8" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -19,6 +21,7 @@
|
|||||||
import shopInfo from "./components/shopInfo.vue";
|
import shopInfo from "./components/shopInfo.vue";
|
||||||
import safe from "./components/safe.vue";
|
import safe from "./components/safe.vue";
|
||||||
import notifications from "./components/notifications.vue";
|
import notifications from "./components/notifications.vue";
|
||||||
|
import contactSetting from "./components/contactSetting.vue";
|
||||||
|
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
activeName: "1",
|
activeName: "1",
|
||||||
|
|||||||
@@ -4,22 +4,38 @@
|
|||||||
<template v-if="carts.list && carts.list.length >= 1">
|
<template v-if="carts.list && carts.list.length >= 1">
|
||||||
<!-- 当前购物车 -->
|
<!-- 当前购物车 -->
|
||||||
<div v-for="(item, index) in carts.list" :key="index">
|
<div v-for="(item, index) in carts.list" :key="index">
|
||||||
<carts-item :item="item" :useVipPrice="carts.useVipPrice" @changeNumber="changeNumber"
|
<carts-item
|
||||||
:selCart="carts.selCart" @itemClick="itemClick(item)" @editNote="editNote"></carts-item>
|
:item="item"
|
||||||
|
:useVipPrice="carts.useVipPrice"
|
||||||
|
@changeNumber="changeNumber"
|
||||||
|
:selCart="carts.selCart"
|
||||||
|
@itemClick="itemClick(item)"
|
||||||
|
@editNote="editNote"
|
||||||
|
></carts-item>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- 赠菜 -->
|
<!-- 赠菜 -->
|
||||||
<div class="cart-title" v-if="carts.giftList.length > 0"><span>以下是优惠菜品</span></div>
|
<div class="cart-title" v-if="carts.giftList.length > 0"><span>以下是优惠菜品</span></div>
|
||||||
<div v-for="(item, index) in carts.giftList" :key="index">
|
<div v-for="(item, index) in carts.giftList" :key="index">
|
||||||
<carts-item :item="item" @changeNumber="changeNumber" :useVipPrice="carts.useVipPrice" :selCart="carts.selCart"
|
<carts-item
|
||||||
@itemClick="itemClick(item)" @editNote="editNote"></carts-item>
|
:item="item"
|
||||||
|
@changeNumber="changeNumber"
|
||||||
|
:useVipPrice="carts.useVipPrice"
|
||||||
|
:selCart="carts.selCart"
|
||||||
|
@itemClick="itemClick(item)"
|
||||||
|
@editNote="editNote"
|
||||||
|
></carts-item>
|
||||||
</div>
|
</div>
|
||||||
<el-empty :image-size="60" v-if="carts.isEmpty" description="点餐列表为空" />
|
<el-empty :image-size="60" v-if="carts.isEmpty" description="点餐列表为空" />
|
||||||
<!-- 打包费 -->
|
<!-- 打包费 -->
|
||||||
<template v-if="carts.packNum > 0">
|
<template v-if="carts.packNum > 0">
|
||||||
<div class="cart-title"><span>打包费</span></div>
|
<div class="cart-title"><span>打包费</span></div>
|
||||||
<extra-fee name="打包费" :number="carts.packNum" :price="carts.orderCostSummary.packFee"></extra-fee>
|
<extra-fee
|
||||||
|
name="打包费"
|
||||||
|
:number="carts.packNum"
|
||||||
|
:price="carts.orderCostSummary.packFee"
|
||||||
|
></extra-fee>
|
||||||
</template>
|
</template>
|
||||||
<!-- 餐位费 -->
|
<!-- 餐位费 -->
|
||||||
<template v-if="perpole >= 1 && carts.dinnerType == 'dine-in'">
|
<template v-if="perpole >= 1 && carts.dinnerType == 'dine-in'">
|
||||||
@@ -40,9 +56,17 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-for="(detaiItem, index) in item" :key="index">
|
<div v-for="(detaiItem, index) in item" :key="index">
|
||||||
<carts-item :useVipPrice="carts.useVipPrice" :canChangeNumber="false" isOld :dinerType="dinerType"
|
<carts-item
|
||||||
:item="detaiItem" @changeNumber="changeNumber" :selCart="carts.selCart" @itemClick="itemClick(detaiItem)"
|
:useVipPrice="carts.useVipPrice"
|
||||||
@editNote="editNote"></carts-item>
|
:canChangeNumber="false"
|
||||||
|
isOld
|
||||||
|
:dinerType="dinerType"
|
||||||
|
:item="detaiItem"
|
||||||
|
@changeNumber="changeNumber"
|
||||||
|
:selCart="carts.selCart"
|
||||||
|
@itemClick="itemClick(detaiItem)"
|
||||||
|
@editNote="editNote"
|
||||||
|
></carts-item>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -50,9 +74,14 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="bottom">
|
<div class="bottom">
|
||||||
<div class="u-flex u-row-right">
|
<div class="u-flex u-row-right">
|
||||||
<el-tooltip placement="top" effect="light" popper-class="youhui-tips" :popper-options="{
|
<el-tooltip
|
||||||
'background-color': '#fff',
|
placement="top"
|
||||||
}">
|
effect="light"
|
||||||
|
popper-class="youhui-tips"
|
||||||
|
:popper-options="{
|
||||||
|
'background-color': '#fff',
|
||||||
|
}"
|
||||||
|
>
|
||||||
<template #content>
|
<template #content>
|
||||||
<div class="u-flex color-000 u-font-14 u-row-between">
|
<div class="u-flex color-000 u-font-14 u-row-between">
|
||||||
<span class="font-bold">会员优惠</span>
|
<span class="font-bold">会员优惠</span>
|
||||||
@@ -74,20 +103,36 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="u-flex u-row-between">
|
<div class="u-flex u-row-between">
|
||||||
<el-link type="primary">打印制作单</el-link>
|
<!-- <el-link type="primary">打印制作单</el-link> -->
|
||||||
|
<div></div>
|
||||||
<div>
|
<div>
|
||||||
<span class="totalNumber">共{{ customTruncateToTwoDecimals(carts.totalNumber) }}件</span>
|
<span class="totalNumber">共{{ customTruncateToTwoDecimals(carts.totalNumber) }}件</span>
|
||||||
<span class="totalPrice">¥{{ customTruncateToTwoDecimals(carts.payMoney) }}</span>
|
<span class="totalPrice">¥{{ customTruncateToTwoDecimals(carts.payMoney) }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="btn-group" v-if="isXianFuKuan">
|
<div class="btn-group" v-if="isXianFuKuan">
|
||||||
<el-button type="primary" size="large" :disabled="!disabledMorePay" @click="createOrder('wx-aiplay')">
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
size="large"
|
||||||
|
:disabled="!disabledMorePay"
|
||||||
|
@click="createOrder('wx-aiplay')"
|
||||||
|
>
|
||||||
微信/支付宝
|
微信/支付宝
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button type="primary" size="large" :disabled="!disabledMorePay" @click="createOrder('cash')">
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
size="large"
|
||||||
|
:disabled="!disabledMorePay"
|
||||||
|
@click="createOrder('cash')"
|
||||||
|
>
|
||||||
现金
|
现金
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button type="primary" size="large" :disabled="!disabledMorePay" @click="createOrder('more-pay')">
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
size="large"
|
||||||
|
:disabled="!disabledMorePay"
|
||||||
|
@click="createOrder('more-pay')"
|
||||||
|
>
|
||||||
更多支付
|
更多支付
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
@@ -100,20 +145,31 @@
|
|||||||
<el-button type="primary" size="large" :disabled="disabledMorePay" @click="createOrder('to-pay')">
|
<el-button type="primary" size="large" :disabled="disabledMorePay" @click="createOrder('to-pay')">
|
||||||
去结账
|
去结账
|
||||||
</el-button> -->
|
</el-button> -->
|
||||||
<el-button type="primary" size="large"
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
size="large"
|
||||||
:disabled="carts.list.length == 0 || carts.oldOrder.detailMap.length == 0"
|
:disabled="carts.list.length == 0 || carts.oldOrder.detailMap.length == 0"
|
||||||
@click="createOrder('only-create')">
|
@click="createOrder('only-create')"
|
||||||
|
>
|
||||||
仅下单
|
仅下单
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button type="primary" size="large"
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
size="large"
|
||||||
:disabled="carts.list.length == 0 && isEmptyObject(carts.oldOrder.detailMap)"
|
:disabled="carts.list.length == 0 && isEmptyObject(carts.oldOrder.detailMap)"
|
||||||
@click="createOrder('to-pay')">
|
@click="createOrder('to-pay')"
|
||||||
|
>
|
||||||
去结账
|
去结账
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<el-button type="default" size="large" @click="hideOrder()">加菜/返回</el-button>
|
<el-button type="default" size="large" @click="hideOrder()">加菜/返回</el-button>
|
||||||
<el-button type="primary" size="large" :disabled="!carts.isLinkFinshed" @click="createOrder('to-pay')">
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
size="large"
|
||||||
|
:disabled="!carts.isLinkFinshed"
|
||||||
|
@click="createOrder('to-pay')"
|
||||||
|
>
|
||||||
立即支付
|
立即支付
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
@@ -131,7 +187,7 @@ import { useUserStore } from "@/store/modules/user";
|
|||||||
|
|
||||||
function isEmptyObject(obj) {
|
function isEmptyObject(obj) {
|
||||||
// 步骤1:排除null和非对象类型
|
// 步骤1:排除null和非对象类型
|
||||||
if (obj === null || typeof obj !== 'object') {
|
if (obj === null || typeof obj !== "object") {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// 步骤2:排除数组(数组也是对象,需单独判断)
|
// 步骤2:排除数组(数组也是对象,需单独判断)
|
||||||
@@ -313,4 +369,4 @@ defineExpose({
|
|||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
</p>
|
</p>
|
||||||
<p class="u-font-14 color-666 u-m-t-10">
|
<p class="u-font-14 color-666 u-m-t-10">
|
||||||
<span class="money">余额:{{ carts.vipUser.amount }}</span>
|
<span class="money">余额:{{ carts.vipUser.amount }}</span>
|
||||||
<span class="score u-m-l-10">积分:{{ carts.vipUser.accountPoints }}</span>
|
<span class="score u-m-l-10">积分:{{ carts.vipUser.pointBalance }}</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -46,7 +46,8 @@
|
|||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
<el-input-number class="u-m-l-10" v-if="score.sel != -1" v-model="usePointsNumber" step-strictly
|
<el-input-number class="u-m-l-10" v-if="score.sel != -1" v-model="usePointsNumber" step-strictly
|
||||||
:step="pointsRes.equivalentPoints" placeholder="请输入积分抵扣数量" :min="pointsRes.minDeductionPoints"
|
:step="pointsRes.equivalentPoints" placeholder="请输入积分抵扣数量" :min="pointsRes.minDeductionPoints"
|
||||||
:max="pointsRes.maxUsablePoints" @change="pointsToMoney"></el-input-number>
|
:max="pointsRes.maxUsablePoints" :disabled="!pointsRes.usable"
|
||||||
|
@change="pointsToMoney"></el-input-number>
|
||||||
</div>
|
</div>
|
||||||
<p class="u-font-14 color-666 u-m-t-10" v-if="pointsRes.unusableReason && !pointsRes.usable">
|
<p class="u-font-14 color-666 u-m-t-10" v-if="pointsRes.unusableReason && !pointsRes.usable">
|
||||||
<span class="color-red">*</span>
|
<span class="color-red">*</span>
|
||||||
@@ -398,8 +399,16 @@ watch(
|
|||||||
);
|
);
|
||||||
|
|
||||||
//002-获取订单可用积分及抵扣金额(支付页面使用)
|
//002-获取订单可用积分及抵扣金额(支付页面使用)
|
||||||
const pointsRes = ref({ usable: true, maxUsablePoints: 0, minDeductionPoints: 0 });
|
const pointsRes = ref({
|
||||||
const usePointsNumber = ref(0);
|
usable: false,
|
||||||
|
equivalentPoints: 0, // 积分换算比例 eg: 20 积分 = 1 元
|
||||||
|
maxDeductionRatio: 0, // 下单最高抵扣比例(小数)
|
||||||
|
minPaymentAmount: 0, // 下单实付抵扣门槛(元)
|
||||||
|
maxUsablePoints: 0,
|
||||||
|
minDeductionPoints: 0,
|
||||||
|
unusableReason: "",
|
||||||
|
});
|
||||||
|
const usePointsNumber = ref(0); // 输入的积分数量(用户填写)
|
||||||
//积分可抵扣最大金额
|
//积分可抵扣最大金额
|
||||||
const scoreMaxMoney = computed(() => {
|
const scoreMaxMoney = computed(() => {
|
||||||
return (
|
return (
|
||||||
@@ -421,22 +430,129 @@ async function pointsInit() {
|
|||||||
if (!carts.vipUser.id) {
|
if (!carts.vipUser.id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const res = await PointsApi.calcOrderUsablePoints({
|
const { pointsConfig, pointsUser } = await PointsApi.calcOrderUsablePoints({
|
||||||
shopUserId: carts.vipUser.id,
|
shopUserId: carts.vipUser.id,
|
||||||
orderAmount: scoreMaxMoney.value,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 不修改 accountPoints(为余额),将积分保存到 pointBalance 字段
|
||||||
|
carts.vipUser.pointBalance = pointsUser && pointsUser.id ? pointsUser.pointBalance : 0;
|
||||||
|
|
||||||
|
// 保险取值
|
||||||
|
const eq = pointsConfig?.equivalentPoints || 0;
|
||||||
|
const maxRatio = pointsConfig?.maxDeductionRatio || 0;
|
||||||
|
const minPay = pointsConfig?.minPaymentAmount || 0;
|
||||||
|
|
||||||
|
// 计算当前订单可抵扣金额上限(元)
|
||||||
|
let finalPay = Number(carts.orderCostSummary.finalPayAmount) || 0;
|
||||||
|
|
||||||
|
const res = {
|
||||||
|
usable: true,
|
||||||
|
equivalentPoints: eq,
|
||||||
|
maxDeductionRatio: maxRatio,
|
||||||
|
minPaymentAmount: minPay,
|
||||||
|
maxUsablePoints: 0,
|
||||||
|
minDeductionPoints: pointsConfig?.minDeductionPoints || eq,
|
||||||
|
unusableReason: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
// 如果订单实付低于最小使用门槛,则不可用
|
||||||
|
if (finalPay <= 0 || (minPay > 0 && finalPay < minPay)) {
|
||||||
|
res.usable = false;
|
||||||
|
res.unusableReason = `订单实付金额低于 ${minPay} 元,无法使用积分抵扣`;
|
||||||
|
} else if (eq <= 0) {
|
||||||
|
res.usable = false;
|
||||||
|
res.unusableReason = `积分换算比例配置错误,无法使用积分抵扣`;
|
||||||
|
} else {
|
||||||
|
// 计算基于比例限制的最大抵扣金额(元)
|
||||||
|
let maxByRatio = finalPay * maxRatio;
|
||||||
|
// 保证抵扣后剩余金额 >= minPaymentAmount
|
||||||
|
if (minPay > 0) {
|
||||||
|
const allowed = finalPay - minPay;
|
||||||
|
if (allowed <= 0) {
|
||||||
|
res.usable = false;
|
||||||
|
res.unusableReason = `抵扣后实付金额必须大于等于 ${minPay} 元,当前不可使用积分`;
|
||||||
|
} else {
|
||||||
|
maxByRatio = Math.min(maxByRatio, allowed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res.usable) {
|
||||||
|
// 可用积分上限(向下取整为 eq 的倍数)
|
||||||
|
const maxByMoney = Math.floor(maxByRatio * eq);
|
||||||
|
const userPoints = carts.vipUser.pointBalance || 0;
|
||||||
|
res.maxUsablePoints = Math.min(userPoints, maxByMoney);
|
||||||
|
// 最小抵扣积分为配置值或等于换算比
|
||||||
|
res.minDeductionPoints = pointsConfig?.minDeductionPoints || eq;
|
||||||
|
if (res.maxUsablePoints < res.minDeductionPoints) {
|
||||||
|
res.usable = false;
|
||||||
|
res.unusableReason = `可用积分不足,至少需要 ${res.minDeductionPoints} 积分才可抵扣`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pointsRes.value = res;
|
pointsRes.value = res;
|
||||||
carts.pointDeductionRule.pointsPerYuan = res.equivalentPoints;
|
carts.pointDeductionRule.pointsPerYuan = res.equivalentPoints;
|
||||||
|
|
||||||
if (score.sel == -1) {
|
if (score.sel == -1) {
|
||||||
|
// 未选择使用积分
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果可用则默认填充可用最大值,否则清零
|
||||||
|
usePointsNumber.value = res.usable ? res.maxUsablePoints : 0;
|
||||||
|
if (!res.usable) score.sel = -1;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将输入的积分数转换为抵扣金额并写回 carts.orderCostSummary
|
||||||
|
function pointsToMoney(val) {
|
||||||
|
const cfg = pointsRes.value;
|
||||||
|
if (!cfg.usable || cfg.equivalentPoints <= 0) {
|
||||||
|
usePointsNumber.value = 0;
|
||||||
|
carts.orderCostSummary.pointUsed = 0;
|
||||||
|
carts.orderCostSummary.pointDeductionAmount = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
usePointsNumber.value = res.usable ? res.maxUsablePoints : 0;
|
// 确保为整数积分
|
||||||
if (res.usable) {
|
let pts = parseInt(usePointsNumber.value || 0, 10);
|
||||||
} else {
|
if (isNaN(pts) || pts <= 0) {
|
||||||
score.sel = -1;
|
pts = 0;
|
||||||
}
|
}
|
||||||
return res;
|
|
||||||
|
// 限制最大值
|
||||||
|
if (cfg.maxUsablePoints && pts > cfg.maxUsablePoints) {
|
||||||
|
pts = cfg.maxUsablePoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算抵扣金额(元),向下保留两位
|
||||||
|
const money = new BigNumber(pts).div(cfg.equivalentPoints).decimalPlaces(2, BigNumber.ROUND_DOWN).toNumber();
|
||||||
|
|
||||||
|
// 再次校验不超过允许的最大抵扣金额(基于比例或门槛)
|
||||||
|
let finalPay = Number(carts.orderCostSummary.finalPayAmount) || 0;
|
||||||
|
let maxByRatio = finalPay * cfg.maxDeductionRatio;
|
||||||
|
if (cfg.minPaymentAmount > 0) {
|
||||||
|
const allowed = finalPay - cfg.minPaymentAmount;
|
||||||
|
if (allowed <= 0) {
|
||||||
|
usePointsNumber.value = 0;
|
||||||
|
carts.orderCostSummary.pointUsed = 0;
|
||||||
|
carts.orderCostSummary.pointDeductionAmount = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
maxByRatio = Math.min(maxByRatio, allowed);
|
||||||
|
}
|
||||||
|
const maxAllowedMoney = new BigNumber(maxByRatio).decimalPlaces(2, BigNumber.ROUND_DOWN).toNumber();
|
||||||
|
if (money > maxAllowedMoney) {
|
||||||
|
// 调整积分到允许的最大金额对应的积分
|
||||||
|
const allowedPts = Math.floor(maxAllowedMoney * cfg.equivalentPoints);
|
||||||
|
pts = Math.min(allowedPts, cfg.maxUsablePoints);
|
||||||
|
}
|
||||||
|
|
||||||
|
usePointsNumber.value = pts;
|
||||||
|
const finalMoney = new BigNumber(pts).div(cfg.equivalentPoints).decimalPlaces(2, BigNumber.ROUND_DOWN).toNumber();
|
||||||
|
carts.orderCostSummary.pointUsed = pts;
|
||||||
|
carts.orderCostSummary.pointDeductionAmount = finalMoney;
|
||||||
}
|
}
|
||||||
|
|
||||||
const emits = defineEmits(["chooseUser", "paysuccess"]);
|
const emits = defineEmits(["chooseUser", "paysuccess"]);
|
||||||
@@ -693,6 +809,10 @@ watch(
|
|||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
carts.payParamsInit();
|
carts.payParamsInit();
|
||||||
getPaytype();
|
getPaytype();
|
||||||
|
|
||||||
|
if (carts.vipUser.id) {
|
||||||
|
pointsInit();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
defineExpose({
|
defineExpose({
|
||||||
nowPayClick,
|
nowPayClick,
|
||||||
|
|||||||
Reference in New Issue
Block a user