9 Commits

68 changed files with 4043 additions and 1534 deletions

View File

@@ -7,8 +7,8 @@ VITE_APP_BASE_API=/dev-api
# 接口地址
# VITE_APP_API_URL=https://tapi.cashier.sxczgkj.cn/ # 测试
VITE_APP_API_URL=https://cashier.sxczgkj.com/ # 正式
# VITE_APP_API_URL=http://192.168.1.42/ # 本地
# VITE_APP_API_URL=https://cashier.sxczgkj.com/ # 正式
VITE_APP_API_URL=http://192.168.1.42/ # 本地
VITE_APP_API_PHP_URL=http://192.168.1.42:8000 #php抖音美团测试环境
VITE_APP_API_PHP_IMPORT_URL=http://192.168.1.42:8789 #本地php批量导入
# VITE_APP_API_PHP_IMPORT_URL=https://diftcs.sxczgkj.com #本地线上php批量导入

View File

@@ -3,7 +3,17 @@
<!-- 开启水印 -->
<el-watermark :font="{ color: fontColor }" :content="watermarkEnabled ? defaultSettings.watermarkContent : ''"
:z-index="9999" class="wh-full">
<router-view />
<!-- 🔴 修改开始使用 v-slot 处理路由组件 -->
<router-view v-slot="{ Component, route }">
<!--
1. 使用 <transition> 包裹防止渲染冲突
2. 加上 :key="route.path" 强制 Vue 在路由变化时重新渲染避免复用导致的空白
-->
<transition name="fade" mode="out-in">
<component :is="Component" :key="route.path" />
</transition>
</router-view>
<!-- 🟢 修改结束 -->
</el-watermark>
</el-config-provider>
</template>

View File

@@ -59,6 +59,22 @@ const ShopApi = {
params,
});
},
// 重置密码
putPassword(data: any) {
return request({
url: `/account/admin/sysUser/pwd`,
method: "put",
data,
});
},
// 续期记录
registerRecord(params: any) {
return request({
url: `${baseURL}/registerRecord`,
method: "get",
params,
});
},
};
export default ShopApi;

View File

@@ -45,14 +45,13 @@ const API = {
responseType: "blob",
});
},
// 重置密码
pwd(data: any) {
return request({
url: `${baseURL}/pwd`,
method: "put",
data
});
}
,
},
}
export default API;

View File

@@ -12,6 +12,19 @@ export const getRegion = () => {
});
}
/**
* 获取所有短信签名
* @param params
* @returns
*/
export const getSms = ({ type }: { type: string }) => {
return request<any, any[]>({
url: `/account/admin/common/sms`,
method: "get",
params: { type }
});
}
/**
* 获取所有银行
* @param params

View File

@@ -89,6 +89,22 @@ const OrderApi = {
responseType: 'blob'
});
},
// 打印经营日报
printDayReport(params: any) {
return request<any>({
url: `${Order_BaseUrl}/admin/finance/printDayReport`,
method: "get",
params
});
},
// 打印日结单
printDaySettle(params: any) {
return request<any>({
url: `${Order_BaseUrl}/admin/finance/printDaySettle`,
method: "get",
params
});
},
};
export default OrderApi;

View File

@@ -28,6 +28,14 @@ const Api = {
responseType: 'blob'
});
},
// 商品报表打印
print(params: any) {
return request<any>({
url: `${baseURL}/print`,
method: "get",
params
});
},
};
export default Api;

View File

@@ -48,6 +48,14 @@ const AuthAPI = {
data,
});
},
// 商品-标记自动售罄
markIsAutoSoldOut(data: Object) {
return request<any, Responseres>({
url: `${baseURL}/markIsAutoSoldOut`,
method: "post",
data,
});
},
// 删除
deleteByIds(id: number | String) {
return request<any, Responseres>({
@@ -88,6 +96,14 @@ const AuthAPI = {
params
});
},
// 耗材列表
consStock(params: any) {
return request<any, Responseres>({
url: `/product/admin/product/cons/consStock`,
method: "get",
params
});
},
// 上下架
onOff(data: any) {
return request<any, Responseres>({
@@ -197,6 +213,14 @@ const AuthAPI = {
params,
responseType: 'blob'
});
},
// 商品-批量操作
batchOperate(params: any) {
return request<any, Responseres>({
url: `${baseURL}/batchOperate`,
method: "get",
params
});
}
};

View File

@@ -386,6 +386,7 @@ const emit = defineEmits<{
editClick: [row: IObject];
operatClick: [data: IOperatData];
filterChange: [data: IObject];
selectChange: []
}>();
// 主键
@@ -490,6 +491,7 @@ function handleSelectionChange(selection: any[]) {
console.log("defaultSelData.value", defaultSelData.value);
selectionData.value = selection;
removeIds.value = selection.map((item) => item[pk]);
emit('selectChange', getselectTable())
}
// 获取选中的表格数据
function getselectTable() {

View File

@@ -5,18 +5,8 @@
<el-input v-model="searhForm.name" placeholder="商品名称"></el-input>
</el-form-item>
<el-form-item>
<el-select
style="width: 200px"
v-model="searhForm.category"
placeholder="商品分类"
:disabled="disableCategory"
>
<el-option
:label="item.name"
:value="item.id"
v-for="item in categoryList"
:key="item.id"
></el-option>
<el-select style="width: 200px" v-model="searhForm.category" placeholder="商品分类" :disabled="disableCategory">
<el-option :label="item.name" :value="item.id" v-for="item in categoryList" :key="item.id"></el-option>
</el-select>
</el-form-item>
<el-form-item>
@@ -25,7 +15,7 @@
</el-form-item>
</el-form>
<div class="head-container">
<el-table ref="table" :data="tableData.list" height="500" v-loading="tableData.loading">
<el-table ref="table" :data="tableData.list" height="500" border stripe v-loading="tableData.loading">
<el-table-column type="selection" width="55" align="center" v-if="!radio"></el-table-column>
<el-table-column label="商品信息">
<template v-slot="scope">
@@ -72,14 +62,9 @@
</el-table-column>
</el-table>
</div>
<el-pagination
:total="tableData.total"
:current-page="tableData.page"
:page-size="tableData.size"
@current-change="paginationChange"
@size-change="sizeChange"
layout="total, sizes, prev, pager, next, jumper"
></el-pagination>
<el-pagination :total="tableData.total" :current-page="tableData.page" :page-size="tableData.size"
@current-change="paginationChange" @size-change="sizeChange"
layout="total, sizes, prev, pager, next, jumper"></el-pagination>
<template #footer>
<span class="dialog-footer" v-if="!radio">
<el-button @click="dialogVisible = false"> </el-button>
@@ -132,7 +117,7 @@ export default {
},
// 确定选商品
confirmHandle() {
let res = this.$refs.table.selection;
let res = this.$refs.table.getSelectionRows();
console.log(res);
if (!res) {
return ElMessage.error("请选择商品");
@@ -148,6 +133,7 @@ export default {
this.tableData.page = 1;
this.tableData.size = 10;
this.tableData.list = [];
this.getTableData();
},
// 分页大小改变
sizeChange(e) {

View File

@@ -0,0 +1,120 @@
<template>
<el-dialog title="打印确认" width="400px" v-model="showDayBusiness">
<div class="business_wrap">
<div class="title">
<el-text>请选择要打印的日期</el-text> <el-text type="danger">周期最长为7天</el-text>
</div>
<div class="row">
<el-date-picker v-model="printDayBusinessParams.date" type="daterange" start-placeholder="开始日期"
end-placeholder="结束日期" format="YYYY-MM-DD" value-format="YYYY-MM-DD" :disabled-date="disabledDate"
@change="handleDateChange" clearable />
</div>
<div class="business_tips">
<el-text type="info">若数据过多打印时间会比较长请耐心等待</el-text>
</div>
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="showDayBusiness = false"> </el-button>
<el-button type="primary" @click="confirmHandle" :loading="printDayBusinessLoading"> </el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import dayjs from "dayjs";
import { ref, onMounted } from "vue";
import { ElMessage } from 'element-plus'
const props = defineProps({
title: {
type: String,
default: '经营日报'
}
})
const showDayBusiness = ref(false)
const emits = defineEmits(['success'])
// 打印参数
const printDayBusinessParams = ref({
date: [],
})
const printDayBusinessLoading = ref(false)
// 取消
const confirmHandle = () => {
// 校验日期
if (!printDayBusinessParams.value.date || printDayBusinessParams.value.date.length !== 2) {
ElMessage.warning('请选择日期范围')
return
}
showDayBusiness.value = false
emits('success', { date: printDayBusinessParams.value.date })
}
// 日期禁用规则:只能选昨天及更早,不能选未来
const disabledDate = (time) => {
const yesterday = dayjs().subtract(1, 'day').format('YYYY-MM-DD')
return dayjs(time).isAfter(yesterday)
}
// 日期选择后校验区间长度不能超过7天
const handleDateChange = (val) => {
if (!val || val.length !== 2) return
const [start, end] = val
const days = dayjs(end).diff(start, 'day') + 1 // 包含起止日
if (days > 7) {
ElMessage.warning('日期范围最多只能选择7天')
printDayBusinessParams.value.date = [] // 清空选择
}
}
// 判断当前时间 是否在 00:00 ~ 05:20 之间
const time = ref(['00:00', '05:20'])
const isInTimeRange = () => {
const now = dayjs()
const startTime = dayjs(time.value[0], 'HH:mm')
const endTime = dayjs(time.value[1], 'HH:mm')
return now.isAfter(startTime) && now.isBefore(endTime)
}
// 打开弹窗
const show = () => {
if (isInTimeRange()) {
ElMessage.warning(`当前时间不能打印${props.title},打印时间:${time.value[0]}点至${time.value[1]}`)
return
}
showDayBusiness.value = true
}
// 暴露方法
defineExpose({ show })
// 初始化默认选中昨天
onMounted(() => {
const yesterday = dayjs().subtract(1, 'day').format('YYYY-MM-DD')
printDayBusinessParams.value.date = [yesterday, yesterday]
})
</script>
<style scoped lang="scss">
.business_wrap {
.title {
display: flex;
gap: 6px;
align-items: center;
}
.row {
padding-top: 14px;
}
.business_tips {
padding-top: 6px;
display: flex;
justify-content: center;
}
}
</style>

View File

@@ -0,0 +1,83 @@
<template>
<el-dialog title="提示" width="450px" v-model="visible">
<div class="refund_content">
<div class="title_wrap">请确认当前菜品是否已上菜</div>
<div class="list_wrap">
<div class="item" v-for="(item, index) in list" :key="index">
<span>{{ item.name }}</span>
<span>x{{ item.num }}</span>
</div>
</div>
</div>
<div class="dialog_footer">
<div class="btn">
<el-button @click="handleCancel" style="width: 100%;">未上菜退还库存</el-button>
</div>
<div class="btn">
<el-button type="primary" @click="handleOk" style="width: 100%;">已上菜不退库存</el-button>
</div>
</div>
</el-dialog>
</template>
<script setup>
import { ref } from 'vue'
const visible = ref(false)
const props = defineProps({
list: {
type: Array,
default: () => []
}
})
const emits = defineEmits(['success'])
// 未上菜 1退菜图库存
function handleCancel() {
visible.value = false
emits('success', 1)
}
// 已上菜 2仅退菜不退库存
function handleOk() {
visible.value = false
emits('success', 2)
}
function show() {
visible.value = true
}
defineExpose({
show
})
</script>
<style scoped lang="scss">
.refund_content {
.title_wrap {
font-size: 16px;
}
.list_wrap {
padding-top: 14px;
.item {
display: flex;
justify-content: space-between;
}
}
}
.dialog_footer {
display: flex;
justify-content: center;
gap: 20px;
padding-top: 20px;
.btn {
flex: 1;
}
}
</style>

View File

@@ -0,0 +1,123 @@
<template>
<!-- 验证码按钮 - 纯功能高度定制版 -->
<el-button :type="buttonType" :size="buttonSize" :disabled="disabled || isCountingDown" :loading="isLoading"
class="captcha-btn" @click="handleGetCaptcha">
<!-- 倒计时状态展示 -->
<span v-if="isCountingDown">{{ countDown }} 秒后重新获取</span>
<!-- 正常状态展示 -->
<span v-else>{{ buttonText }}</span>
</el-button>
</template>
<script setup>
import { ref } from 'vue'
import { getSms } from "@/api/common";
// 接收父组件传递的自定义参数
const props = defineProps({
// 验证码类型
// editShopInfoOpePwd 店铺操作密码
// wxMiniPwd 微信小程序用户登录密码
// shopPwd 店铺登录密码
type: {
type: String,
default: 'editShopInfoOpePwd'
},
// 按钮类型primary/success/warning/danger/info/default
buttonType: {
type: String,
default: 'primary'
},
// 按钮尺寸large/default/small
buttonSize: {
type: String,
default: 'default'
},
// 正常状态按钮文字
buttonText: {
type: String,
default: '获取验证码'
},
// 倒计时总秒数
countDownNum: {
type: Number,
default: 60
},
// 手动控制按钮禁用状态
disabled: {
type: Boolean,
default: false
}
})
// 发射事件给父组件
const emit = defineEmits(['get-captcha'])
// 加载状态(请求验证码时)
const isLoading = ref(false)
// 是否正在倒计时
const isCountingDown = ref(false)
// 倒计时秒数
const countDown = ref(0)
// 倒计时定时器
let timer = null
// 获取验证码点击事件
const handleGetCaptcha = async () => {
// 倒计时中 / 加载中 / 禁用状态 禁止点击
if (isCountingDown.value || isLoading.value || props.disabled) return
try {
// 开启加载状态
isLoading.value = true
// 触发父组件的请求方法(真正的接口请求写在父组件)
await getSms({ type: props.type });
ElNotification.success({
title: '成功',
message: '验证码已发送,请注意查收',
});
// ======================
// 请求成功 → 开始倒计时
// ======================
startCountDown()
} catch (error) {
// 请求失败 → 不触发倒计时,控制台提示
console.error('', error)
} finally {
// 关闭加载状态
setTimeout(() => {
isLoading.value = false
}, 500) // 适当延迟,避免闪烁
}
}
// 倒计时核心逻辑
const startCountDown = () => {
// 初始化倒计时时间
countDown.value = props.countDownNum
isCountingDown.value = true
// 开启定时器
timer = setInterval(() => {
countDown.value--
// 倒计时结束
if (countDown.value <= 0) {
clearInterval(timer)
timer = null
isCountingDown.value = false
}
}, 1000)
}
// 页面销毁时清除定时器(防止内存泄漏)
onUnmounted(() => {
if (timer) clearInterval(timer)
})
</script>
<style scoped>
/* 你可以在这里自定义按钮默认样式,也可以在父组件覆盖 */
.captcha-btn {
min-width: 120px;
}
</style>

View File

@@ -0,0 +1,180 @@
<template>
<el-dialog title="修改密码" modal-append-to-body append-to-body v-model="dialogVisible" @close="reset" width="400px">
<el-form ref="refForm" :model="form" :rules="rules" label-width="100px">
<el-form-item label="手机号">
<el-input :value="maskPhone(shopInfo.phone)" disabled></el-input>
</el-form-item>
<el-form-item label="旧密码" prop="originalPassword">
<el-input type="password" show-password v-model="form.originalPassword" placeholder="请输入旧密码"></el-input>
</el-form-item>
<el-form-item label="新密码" prop="password">
<el-input type="password" show-password v-model="form.password" placeholder="请输入新密码"></el-input>
</el-form-item>
<el-form-item style="margin-top: -10px;"><el-text :type="isPws ? 'danger' : 'info'" size="small"
style="line-height: 16px;">注意新密码必须至少包含字母数字特殊符号中的两种且长度6-18</el-text></el-form-item>
<el-form-item label="确认新密码" prop="checkPassword">
<el-input type="password" show-password v-model="form.checkPassword" placeholder="请再次输入新密码"></el-input>
</el-form-item>
<el-form-item label="验证码" prop="code">
<div class="center">
<el-input v-model="form.code" placeholder="请输入验证码"></el-input>
<captcha-btn type="shopPwd" />
</div>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false"> </el-button>
<el-button type="primary" :loading="formLoading" @click="submitHandle"> </el-button>
</span>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import sysUser from "@/api/account/sysUser";
import { ElNotification } from "element-plus";
import { maskPhone } from "@/utils";
import CaptchaBtn from "./CaptchaBtn.vue";
const props = defineProps({
// 修改成功后的跳转类型 1- 跳转到登录页 2- 留在当前页
type: {
type: [String, Number],
default: 1
}
})
const shopInfo = ref({
phone: ''
})
const router = useRouter();
const dialogVisible = ref(false);
const formLoading = ref(false);
const isPws = ref(false);
const form = reactive({
id: '',
originalPassword: "", // 原密码
code: '', // 验证码
checkPassword: "", // 确认新密码
password: "", // 新密码
});
function reset() {
form.originalPassword = "";
form.checkPassword = "";
form.password = "";
form.code = "";
refForm.value.resetFields();
}
const reg = /^(?![0-9]+$)(?![a-zA-Z]+$)(?![^\da-zA-Z]+$).{6,}$/;
const validateNewPass = (rule: any, value: string, callback: (error?: Error) => void) => {
if (!form.password) {
callback(new Error(" "));
} else if (form.password === form.originalPassword) {
callback(new Error("请输入与旧密码不同的新密码"));
} else if (form.password.length < 6 || form.password.length > 18) {
// 密码长度6 - 18位
callback(new Error("密码长度应为6-18位"));
} else if (!reg.test(form.password)) {
// 新密码长度不能小于6 需包含字母、数字、特殊符号中至少两种
isPws.value = true;
callback(new Error(""));
} else {
isPws.value = false;
callback();
}
};
const validateRnewPass = (rule: any, value: string, callback: (error?: Error) => void) => {
if (!form.checkPassword) {
callback(new Error(" "));
} else if (form.checkPassword !== form.password) {
callback(new Error("两次密码输入不一致"));
} else {
callback();
}
};
const rules = {
originalPassword: [
{
required: true,
message: " ",
trigger: "blur",
},
],
password: [
{
required: true,
validator: validateNewPass,
trigger: "blur",
},
],
checkPassword: [
{
required: true,
validator: validateRnewPass,
trigger: "blur",
},
],
code: [
{
required: true,
message: " ",
trigger: "blur",
},
],
};
const emit = defineEmits(['success'])
const refForm = ref();
// 提交修改密码
function submitHandle() {
refForm.value.validate(async (vaild: boolean) => {
if (vaild) {
try {
formLoading.value = true;
await sysUser.pwd(form);
if (props.type == 1) {
ElNotification.success("密码修改成功,请重新登录");
setTimeout(() => {
router.push("/login");
}, 1000);
} else {
ElNotification.success("密码修改成功");
dialogVisible.value = false;
}
emit('success')
} catch (error) {
formLoading.value = false;
console.log(error);
}
}
});
}
function show(e: object) {
console.log(e);
shopInfo.value = e
form.id = e.id
dialogVisible.value = true
}
defineExpose({
show,
});
</script>
<style lang="scss" scoped>
.center {
display: flex;
align-items: center;
gap: 10px;
}
</style>

View File

@@ -10,41 +10,25 @@
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="handleOpenUserProfile">店铺配置</el-dropdown-item>
<el-dropdown-item divided @click="dialogVisible = true">修改密码</el-dropdown-item>
<el-dropdown-item divided @click="refResetPasswordRef.show(shopInfo)">修改密码</el-dropdown-item>
<el-dropdown-item divided @click="logout">退出登录</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-dialog title="修改密码" modal-append-to-body append-to-body v-model="dialogVisible" @close="reset" width="400px">
<el-form ref="refForm" :model="form" :rules="rules">
<el-form-item label="旧密码" prop="oldPass">
<el-input type="password" show-password v-model="form.oldPass" placeholder="请输入旧密码"></el-input>
</el-form-item>
<el-form-item label="新密码" prop="newPass">
<el-input type="password" show-password v-model="form.newPass" placeholder="请输入新密码"></el-input>
</el-form-item>
<el-form-item label="确认新密码" prop="rnewPass">
<el-input type="password" show-password v-model="form.rnewPass" placeholder="请再次输入新密码"></el-input>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false"> </el-button>
<el-button type="primary" :loading="formLoading" @click="submitHandle"> </el-button>
</span>
</template>
</el-dialog>
<resetPassword ref="refResetPasswordRef" />
</template>
<script setup lang="ts">
import sysUser from "@/api/account/sysUser";
defineOptions({
name: "UserProfile",
});
import { ElNotification } from "element-plus";
import { useTagsViewStore, useUserStore } from "@/store";
import { useCartsStore } from '@/store/modules/carts'
import resetPassword from "@/components/resetPassword/index.vue";
const shopInfo = JSON.parse(localStorage.getItem("userInfo") || "{}");
const refResetPasswordRef = ref(null);
const tagsViewStore = useTagsViewStore();
const userStore = useUserStore();
@@ -53,91 +37,6 @@ const cartStore = useCartsStore()
const route = useRoute();
const router = useRouter();
const dialogVisible = ref(false);
const formLoading = ref(false);
const form = reactive({
oldPass: "",
newPass: "",
rnewPass: "",
});
function reset() {
form.oldPass = "";
form.newPass = "";
form.rnewPass = "";
refForm.value.resetFields();
}
const validateNewPass = (rule: any, value: string, callback: (error?: Error) => void) => {
if (!form.newPass) {
callback(new Error(" "));
} else if (form.newPass === form.oldPass) {
callback(new Error("请输入与旧密码不同的新密码"));
} else {
callback();
}
};
const validateRnewPass = (rule: any, value: string, callback: (error?: Error) => void) => {
if (!form.rnewPass) {
callback(new Error(" "));
} else if (form.rnewPass !== form.newPass) {
callback(new Error("两次密码输入不一致"));
} else {
callback();
}
};
const rules = {
oldPass: [
{
required: true,
message: " ",
trigger: "blur",
},
],
newPass: [
{
required: true,
validator: validateNewPass,
trigger: "blur",
},
],
rnewPass: [
{
required: true,
validator: validateRnewPass,
trigger: "blur",
},
],
};
const refForm = ref();
// 提交修改密码
function submitHandle() {
refForm.value.validate(async (vaild: boolean) => {
if (vaild) {
try {
formLoading.value = true;
const res = await sysUser.pwd({
originalPassword: form.oldPass,
checkPassword: form.newPass,
password: form.newPass,
});
ElNotification.success("修改成功,请重新登陆");
userStore
.logout()
.then(() => {
tagsViewStore.delAllViews();
})
.then(() => {
router.push(`/login?redirect=${route.fullPath}`);
});
} catch (error) {
formLoading.value = false;
console.log(error);
}
}
});
}
/**
* 打开个人中心页面
*/

View File

@@ -3,6 +3,7 @@ import WebSocketManager, { type ApifoxModel, msgType } from "@/utils/websocket";
import orderApi from "@/api/order/order";
import { useUserStoreHook } from "@/store/modules/user";
import productApi from "@/api/product/index";
import categoryApi from "@/api/product/productclassification";
import shopUserApi from '@/api/account/shopUser'
import limitTimeDiscountApi from '@/api/market/limitTimeDiscount.js'
import { BigNumber } from "bignumber.js";
@@ -82,8 +83,6 @@ export const useCartsStore = defineStore("carts", () => {
default: productType = GoodsType.NORMAL;
}
return {
...item,
id: item.id,
@@ -350,11 +349,61 @@ export const useCartsStore = defineStore("carts", () => {
}
});
const consList = await productApi.consStock({ shopId: localStorage.getItem("shopId") })
goods.value = addGoodsSoldOutStatus(goods.value, consList)
console.log('代客下单页面商品缓存.goods.value', goods.value);
setGoodsMap(goods.value);
}
/**
* 给商品列表批量添加 isSoldOut 售罄状态字段
* @param {Array} goodsList - 商品列表 [ { consList, isAutoSoldStock } ]
* @param {Array} consStockList - 真实耗材库存列表 [ { consId, stockNumber } ]
* @returns 带 isSoldOut 字段的新商品列表
*/
function addGoodsSoldOutStatus(goodsList, consStockList) {
// console.log('addGoodsSoldOutStatus.goodsList', goodsList);
// console.log('addGoodsSoldOutStatus.consStockList', consStockList);
// 耗材ID映射真实库存保留
const consMap = _.keyBy(consStockList, item => String(item.consId));
return _.map(goodsList, goods => {
let isSoldOut = false;
// 开启自动售罄才判断
if (goods.isAutoSoldStock === 1 || goods.isAutoSoldStock === true) {
const goodsConsList = goods.consList || [];
// 无耗材 → 不售罄
if (goodsConsList.length === 0) {
isSoldOut = false;
} else {
// 核心:只要有一个耗材 真实库存 < 商品需要量 → 售罄
isSoldOut = _.some(goodsConsList, consItem => {
// 商品绑定的耗材ID对应真实库存ID
const consId = String(consItem.consInfoId);
// 商品需要消耗的数量(你的需求量)
const needStock = consItem.surplusStock || 0;
// 起售数量
const suitNum = goods.type == 'single' ? goods.skuList[0].suitNum : 1;
// 真实库存
const realStock = _.get(consMap, [consId, 'stockNumber'], 0);
// 真实库存 < 需要量 * 起售数量 → 不足 → 售罄
return realStock < needStock * suitNum;
});
}
}
return { ...goods, isSoldOut };
});
}
function setGoodsMap(goods: any[]) {
for (let item of goods) {
goodsMap[item.id] = item;
@@ -660,7 +709,9 @@ export const useCartsStore = defineStore("carts", () => {
sendMessage('add', { ...basic_msg, ...data });
}
// 添加购物车/编辑购物车
function add(data: any) {
// console.log('添加购物车/编辑购物车===', data);
goods.value.map(item => {
if (item.id == data.product_id) {
data.is_time_discount = item.is_time_discount ? 1 : 0
@@ -675,8 +726,29 @@ export const useCartsStore = defineStore("carts", () => {
console.log('carts.add===', data);
if (hasCart) {
// console.log('编辑', msg);
if (hasCart.number * 1 + msg.number * 1 == 2) {
ElMessage({
type: 'warning',
message: '购物车已有该商品,请确认是否重复',
duration: 4000
})
}
update({ ...hasCart, ...msg, number: hasCart.number * 1 + msg.number * 1 });
} else {
// console.log('添加', msg);
let arr = _.flatten(_.values(oldOrder.value.detailMap))
// console.log('添加.arr===', arr);
const isExist = _.some(arr, item => Number(item.productId) === msg.product_id)
// console.log('添加.isExist===', isExist);
if (msg.number == 1 && isExist) {
ElMessage({
type: 'warning',
message: '该商品已下单过,请确认是否重复',
duration: 4000
})
}
sendMessage('add', msg);
}
}
@@ -906,7 +978,7 @@ export const useCartsStore = defineStore("carts", () => {
console.log("收到消息:", msg);
if (!msg.status) {
if (msg.hasOwnProperty('status') && msg.status !== 1 && msg.operate_type !== 'bulk_edit') {
return ElMessage.error(msg.message || '操作失败');
return ElMessage.error(msg.msg || '操作失败');
}
}
if (msg?.data) {
@@ -1068,8 +1140,6 @@ export const useCartsStore = defineStore("carts", () => {
newUserDiscount.value = {}
}
return {
disconnect,
dinnerType,

View File

@@ -39,7 +39,7 @@ export const useUserStore = defineStore("user", () => {
setRefreshToken(token);
localStorage.setItem("shopId", "" + data.shopInfo.id);
localStorage.setItem("branch_shopId", data.shopInfo.id)
resolve();
resolve(data);
})
.catch((error) => {
reject(error);

View File

@@ -322,3 +322,41 @@ export function isValidMobile(phone: string): boolean {
const mobileRegex = /^1[3-9]\d{9}$/;
return mobileRegex.test(s);
}
/**
* 手机号脱敏:隐藏中间四位数字
* @param {string | number} phone - 原始手机号(支持数字/字符串)
* @returns {string} 脱敏后的手机号,如 138****1234
*/
export function maskPhone(phone) {
// 先转成字符串,避免传入数字类型报错
const phoneStr = String(phone).trim();
// 简单校验必须是11位数字
if (!/^1\d{10}$/.test(phoneStr)) {
console.warn('手机号格式不正确');
return phoneStr; // 格式错误直接返回原值
}
// 核心前3位 + **** + 后4位
return phoneStr.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2');
}
/**
* 金额保留两位小数 不四舍五入
* @param {number} num 金额数字
* @param {boolean} hasSymbol 是否带¥符号默认true
* @returns {string}
*/
export function formatMoney(num, hasSymbol = true) {
// 防止非数字、空值报错
if (isNaN(Number(num))) num = 0;
// 不四舍五入截断两位小数
const value = Math.floor(Math.abs(num) * 100) / 100;
// 拼接两位小数
const fixedNum = value.toFixed(2);
// 负数处理
const result = num < 0 ? `-${fixedNum}` : fixedNum;
// 默认返回带¥
return hasSymbol ? `¥${result}` : result;
}

View File

@@ -10,6 +10,8 @@
<el-form-item>
<el-button type="primary" icon="Search" :loading="loading" @click="handleQuery">查询</el-button>
<el-button @click="handleReset" icon="Refresh" :loading="loading">重置</el-button>
<el-button icon="Printer" @click="printBusinessDialogRef.show()">经营日报</el-button>
<el-button icon="Printer" @click="printBusinessDialogRef2.show()">日结单</el-button>
</el-form-item>
</el-form>
<el-form inline>
@@ -359,6 +361,8 @@
</div>
</div>
</div>
<printBusinessDialog ref="printBusinessDialogRef" @success="printHandle" />
<printBusinessDialog ref="printBusinessDialogRef2" @success="printHandle2" />
</template>
<script setup>
@@ -366,6 +370,46 @@ import dayjs from "dayjs";
import { ref, onMounted } from "vue";
import OrderApi from "@/api/order/order";
import { downloadFile, multiplyAndFormat } from '@/utils'
import printBusinessDialog from "@/components/printBusinessDialog.vue";
const printBusinessDialogRef = ref(null)
// 打印经营日报回调
async function printHandle(res) {
try {
const shopStaff = JSON.parse(localStorage.getItem('shopStaff')) || { name: '' }
const shopName = localStorage.getItem('shopName')
await OrderApi.printDayReport({
beginDate: res.date[0],
endDate: res.date[1],
rangeType: 'CUSTOM',
shopId: localStorage.getItem('shopId') || '',
operator: shopStaff.name || shopName
})
ElMessage.success('打印成功')
} catch (error) {
console.log(error);
}
}
const printBusinessDialogRef2 = ref(null)
// 打印日结单回调
async function printHandle2(res) {
try {
const shopStaff = JSON.parse(localStorage.getItem('shopStaff')) || { name: '' }
const shopName = localStorage.getItem('shopName')
await OrderApi.printDaySettle({
beginDate: res.date[0],
endDate: res.date[1],
rangeType: 'CUSTOM',
shopId: localStorage.getItem('shopId') || '',
operator: shopStaff.name || shopName
})
ElMessage.success('打印成功')
} catch (error) {
console.log(error);
}
}
const queryForm = ref({
queryDate: dayjs().format('YYYY-MM-DD'), // 查询日期 yyyy-MM-dd

View File

@@ -44,6 +44,7 @@
<span v-if="!downloadLoading">导出Excel</span>
<span v-else>下载中...</span>
</el-button>
<el-button icon="Printer" :loading="printLoading" @click="$refs.printBusinessDialogRef.show()">打印</el-button>
<importData :type="7" @close="getTableData" />
</el-form-item>
</el-form>
@@ -185,6 +186,7 @@
@current-change="paginationChange" @size-change="sizeChange"
layout="total, sizes, prev, pager, next, jumper"></el-pagination>
</div>
<printBusinessDialog ref="printBusinessDialogRef" title="商品报表" @success="printBusinessConfirm" />
</div>
</template>
@@ -197,9 +199,10 @@ import ShopApi from "@/api/account/shop";
import dayjs from "dayjs";
import { downloadFile, multiplyAndFormat } from "@/utils/index";
import { formatDateRange } from './utils/index.js'
import printBusinessDialog from '@/components/printBusinessDialog.vue';
export default {
components: { importData },
components: { importData, printBusinessDialog },
data() {
return {
multiplyAndFormat,
@@ -233,7 +236,8 @@ export default {
// dayjs(time):将原生 Date 转为 dayjs 对象
// isAfter判断目标日期是否在今天之后
return dayjs(time).isAfter(dayjs().startOf('day'));
}
},
printLoading: false
};
},
filters: {
@@ -255,6 +259,34 @@ export default {
this.geiShopList();
},
methods: {
// 确认打印
printBusinessConfirm(res) {
this.printHandle(res.date)
},
// 打印
async printHandle(date) {
try {
const shopStaff = JSON.parse(localStorage.getItem('shopStaff')) || { name: '' }
const shopName = localStorage.getItem('shopName')
this.printLoading = true
await saleSummaryApi.print({
beginDate: date[0],
endDate: date[1],
categoryId: this.query.prodCategoryId,
productName: this.query.productName,
shopId: this.shopId,
rangeType: this.timeValue,
operator: shopStaff.name || shopName
})
ElMessage.success('操作成功')
} catch (error) {
console.log(error);
}
this.printLoading = false
},
/**
* 获取分店列表
*/

View File

@@ -4,10 +4,10 @@
<el-table-column prop="shopName" align="center" label="商户名称" />
<el-table-column prop="staffName" align="center" label="职员名称" />
<el-table-column prop="orderCount" align="center" label="订单数量" />
<el-table-column prop="handAmount" align="center" label="应交金额" />
<el-table-column prop="quickInAmount" align="center" label="快捷收款金额" />
<el-table-column prop="orderTurnover" align="center" label="应交金额" />
<el-table-column prop="balance" align="center" label="余额支付" />
<el-table-column prop="refundAmount" align="center" label="退款金额" />
<el-table-column prop="handAmount" align="center" label="总收入" />
<el-table-column prop="turnover" align="center" label="总收入" />
<el-table-column prop="loginTime" align="center" label="开始时间" />
<el-table-column prop="handoverTime" align="center" label="交班时间" />
<el-table-column label="操作" align="center">

View File

@@ -2,7 +2,6 @@ import printerApi, { type addRequest } from "@/api/account/printer";
import { options } from './config'
import type { IModalConfig } from "@/components/CURD/types";
import { c } from "vite/dist/node/moduleRunnerTransport.d-CXw_Ws6P";
const modalConfig: IModalConfig<addRequest> = {
pageName: "sys:user",
@@ -17,7 +16,7 @@ const modalConfig: IModalConfig<addRequest> = {
formAction: function (data) {
let obj = { ...data }
console.log("打印类型", data);
obj.printType = data.printType.join(',')
// obj.printType = data.printType.join(',')
obj.categoryIds = JSON.stringify(data.categoryIdsArr)
obj.categoryList = JSON.stringify(data.categoryIdsArr)
if (data.classifyPrint == 0) {
@@ -90,7 +89,7 @@ const modalConfig: IModalConfig<addRequest> = {
{
type: "select",
label: "打印类型",
prop: "subType",
prop: "printType",
rules: [{ required: false, message: "请选择打印类型", trigger: "blur" }],
attrs: {
placeholder: "请选择打印类型",
@@ -108,7 +107,7 @@ const modalConfig: IModalConfig<addRequest> = {
{
type: "select",
label: "打印机品牌",
prop: "contentType",
prop: "brand",
rules: [{ required: true, message: "请选择打印机品牌", trigger: "blur" }],
attrs: {
placeholder: "请选择打印机品牌",
@@ -144,27 +143,27 @@ const modalConfig: IModalConfig<addRequest> = {
label: "",
initialValue: []
},
{
type: "radio",
label: "打印数量",
prop: "printQty",
prop: "printNum",
options: options.printQty,
initialValue: options.printQty[0].value
},
{
type: "radio",
label: "打印方式",
prop: "printMethod",
prop: "kitchenPrintMode",
options: options.printMethod,
initialValue: options.printMethod[0].value
},
{
type: "checkbox",
type: "custom",
label: "打印类型",
prop: "printType",
prop: "printContentType",
options: options.printType,
initialValue: options.printType.map(v => v.value)
initialValue: options.printType.map(v => v.value),
slotName: 'printTypeSlot'
},
{
label: "打印机状态",

View File

@@ -59,8 +59,8 @@ const contentConfig: IContentConfig<getListRequest> = {
label: "状态",
align: "center",
prop: "status",
templet: "switch",
slotName: "status",
templet: "custom",
slotName: "status"
},
{ label: "创建时间", align: "center", prop: "createTime" },
// {

View File

@@ -160,11 +160,11 @@ const modalConfig: IModalConfig<editRequest> = {
initialValue: options.printMethod[0].value
},
{
type: "checkbox",
type: "custom",
label: "打印类型",
prop: "printType",
options: options.printType,
initialValue: options.printType.map(v => v.value)
prop: "printContentType",
options: '',
initialValue: ''
},
{
label: "打印机状态",

View File

@@ -2,30 +2,18 @@
<div class="app-container">
<!-- 列表 -->
<!-- 搜索 -->
<page-search
ref="searchRef"
:search-config="searchConfig"
@query-click="handleQueryClick"
@reset-click="handleResetClick"
/>
<page-search ref="searchRef" :search-config="searchConfig" @query-click="handleQueryClick"
@reset-click="handleResetClick" />
<!-- 列表 -->
<page-content
ref="contentRef"
:content-config="contentConfig"
@add-click="handleAddClick"
@edit-click="handleEditClick"
@export-click="handleExportClick"
@search-click="handleSearchClick"
@toolbar-click="handleToolbarClick"
@operat-click="handleOperatClick"
@filter-change="handleFilterChange"
>
<template #status="scope">
<page-content ref="contentRef" :content-config="contentConfig" @add-click="handleAddClick"
@edit-click="handleEditClick" @export-click="handleExportClick" @search-click="handleSearchClick"
@toolbar-click="handleToolbarClick" @operat-click="handleOperatClick" @filter-change="handleFilterChange">
<!-- <template #status="scope">
<el-tag :type="scope.row[scope.prop] == 1 ? 'success' : 'info'">
{{ scope.row[scope.prop] == 1 ? "启用" : "禁用" }}
</el-tag>
</template>
</template> -->
<template #contentType="scope">
{{ scope.row.contentType == "yxyPrinter" ? "云想印" : "飞鹅" }}
</template>
@@ -38,6 +26,10 @@
<template #gender="scope">
<DictLabel v-model="scope.row[scope.prop]" code="gender" />
</template>
<template #status="scope">
<el-switch v-model="scope.row.status" :active-value="1" :inactive-value="0"
@click="statusChange($event, scope.row)"></el-switch>
</template>
<template #operate="scope">
<div v-if="scope.row.connectionType != 'USB'">
<el-button @click="handleEditClick(scope.row)" icon="Edit" type="primary" link>
@@ -55,10 +47,21 @@
</page-content>
<!-- 新增 -->
<page-modal ref="addModalRef" :modal-config="addModalConfig" @submit-click="handleSubmitClick">
<!-- <page-modal ref="addModalRef" :modal-config="addModalConfig" @submit-click="handleSubmitClick">
<template #gender="scope">
<Dict v-model="scope.formData[scope.prop]" code="gender" />
</template>
<template #printTypeSlot="scope">
<div class="row" v-for="(item, index) in printTypeList" :key="index">
<div class="title">{{ item.label }}</div>
<div class="cont">
<el-checkbox-group v-model="item.values" @change="printTypeChange($event, index, scope)">
<el-checkbox :label="item.label" :value="item.value" v-for="item in item.list"
:key="item.value"></el-checkbox>
</el-checkbox-group>
</div>
</div>
</template>
<template #classifyPrintData="scope">
<template v-if="scope.formData.classifyPrint == 1">
<el-checkbox-group v-model="scope.formData.categoryIdsArr">
@@ -68,17 +71,24 @@
</el-checkbox-group>
</template>
</template>
</page-modal>
</page-modal> -->
<!-- 编辑 -->
<page-modal
ref="editModalRef"
:modal-config="editModalConfig"
@submit-click="handleSubmitClick"
>
<!-- <page-modal ref="editModalRef" :modal-config="editModalConfig" @submit-click="handleSubmitClick">
<template #gender="scope">
<Dict v-model="scope.formData[scope.prop]" code="gender" v-bind="scope.attrs" />
</template>
<template #printTypeSlot="scope">
<div class="row" v-for="(item, index) in printTypeList" :key="index">
<div class="title">{{ item.label }}</div>
<div class="cont">
<el-checkbox-group v-model="item.values" @change="printTypeChange($event, index, scope)">
<el-checkbox :label="item.label" :value="item.value" v-for="item in item.list"
:key="item.value"></el-checkbox>
</el-checkbox-group>
</div>
</div>
</template>
<template #classifyPrintData="scope">
<template v-if="scope.formData.classifyPrint == 1">
<el-checkbox-group v-model="scope.formData.categoryIdsArr">
@@ -88,11 +98,103 @@
</el-checkbox-group>
</template>
</template>
</page-modal>
</page-modal> -->
<el-dialog :title="form.id ? '编辑打印机' : '添加打印机'" width="800px" v-model="visible" @closed="dialogClosed"
@open="dialogOpen">
<div style="height: 60vh;overflow-y: auto;" ref="formDivRef">
<el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
<el-form-item label="设备名称" prop="name">
<el-input v-model="form.name" placeholder="请输入设备名称"></el-input>
</el-form-item>
<el-form-item label="设备类型">
<el-radio-group v-model="form.connectionType">
<el-radio-button label="USB" value="USB"></el-radio-button>
<el-radio-button label="云打印" value="云打印"></el-radio-button>
<el-radio-button label="局域网" value="局域网"></el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item label="小票类型">
<el-radio-group v-model="form.printType">
<el-radio-button label="标签" value="label"></el-radio-button>
<el-radio-button label="小票" value="cash"></el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item label="打印机品牌" prop="brand">
<el-radio-group v-model="form.brand">
<el-radio-button label="飞鹅"></el-radio-button>
<el-radio-button label="云想印"></el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item label="ip地址/MAC地址" prop="address">
<el-input v-model="form.address" placeholder="请输入ip地址/MAC地址"></el-input>
</el-form-item>
<el-form-item label="端口" prop="port">
<el-input v-model="form.port" placeholder="请输入端口"></el-input>
</el-form-item>
<el-form-item label="小票尺寸" prop="receiptSize">
<el-radio-group v-model="form.receiptSize">
<el-radio-button label="58mm"></el-radio-button>
<el-radio-button label="80mm"></el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item label="打印数量" prop="printNum">
<el-input-number v-model="form.printNum" :step="1" :min="1"></el-input-number>
</el-form-item>
<el-form-item label="打印内容" prop="printContentType">
<div class="row" v-for="(item, index) in printTypeList" :key="index">
<div class="title">{{ item.label }}</div>
<div class="cont">
<el-checkbox-group v-model="item.values" @change="printTypeChange($event, index)">
<el-checkbox :label="item.label" :value="item.value" v-for="item in item.list"
:key="item.value"></el-checkbox>
</el-checkbox-group>
</div>
</div>
</el-form-item>
<!-- <el-form-item label="打印模式" prop="kitchenPrintMode">
<el-radio-group v-model="form.kitchenPrintMode">
<el-radio-button label="整单" value="all"></el-radio-button>
<el-radio-button label="单个" value="only"></el-radio-button>
</el-radio-group>
<el-text type="info" style="margin-left: 14px;">仅针对厨房制作单的打印</el-text>
</el-form-item> -->
<el-form-item label="分类打印">
<div class="column">
<div style="display: flex;align-items: center;">
<el-radio-group v-model="form.classifyPrint">
<el-radio-button label="所有" value="0"></el-radio-button>
<el-radio-button label="部分分类" value="1" v-if="printTypeList[1].values.length > 0"></el-radio-button>
</el-radio-group>
<el-text type="info" style="margin-left: 14px;">仅针对厨房制作单的打印</el-text>
</div>
<template v-if="form.classifyPrint == 1">
<el-checkbox-group v-model="form.categoryIds">
<el-checkbox v-for="item in PrinterTypeList" :value="item.id" :label="item.name"></el-checkbox>
</el-checkbox-group>
</template>
</div>
</el-form-item>
<el-form-item label="媒体音量">
<el-switch v-model="form.volumeSwitch" :active-value="1" :inactive-value="0"></el-switch>
</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>
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="visible = false"> </el-button>
<el-button type="primary" :loading="loading" @click="submitHandle"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import _ from 'lodash'
import UserAPI from "@/api/account/printer";
import type { IObject, IOperatData } from "@/components/CURD/types";
import usePage from "@/components/CURD/usePage";
@@ -100,6 +202,166 @@ import addModalConfig from "./config/add";
import contentConfig from "./config/content";
import editModalConfig from "./config/edit";
import searchConfig from "./config/search";
import { options } from './config/config'
import printerApi, { type addRequest } from "@/api/account/printer";
const printTypeList = ref([
{
label: '前台',
values: [],
list: [
{ label: '客看单', value: 'GUEST_ORDER' },
{ label: '预结算单', value: 'PRE_ORDER' },
{ label: '结算单', value: 'ORDER' },
{ label: '退菜单', value: 'RETURN_ORDER' },
{ label: '退款单', value: 'REFUND_ORDER' },
]
},
{
label: '后厨',
values: [],
list: [
{ label: '后厨-整单', value: 'ALL_KITCHEN' },
{ label: '后厨-分单', value: 'ONLY_KITCHEN' },
{ label: '后厨-退菜单', value: 'REFUND_KITCHEN' },
]
},
{
label: '其它',
values: [],
list: [
{ label: '交班单', value: 'HANDOVER' },
{ label: '排队取号', value: 'CALL' },
{ label: '储值单', value: 'RECHARGE' },
{ label: '入库单', value: 'STOCK' },
{ label: '盘点单', value: 'STOCK_CHECK' },
{ label: '商品报表', value: 'PRODUCT_REPORT' },
{ label: '经营日报', value: 'DAY_REPORT' },
{ label: '日结单', value: 'DAY_ORDER' },
]
}
])
const loading = ref(false)
const visible = ref(false)
const formRef = ref(null)
const obj = {
id: '',
name: '', // 设备名称
connectionType: '云打印', // 连接方式 USB、云打印、局域网
printType: 'cash', // 打印类型 label标签 cash小票
brand: '', // 打印机品牌 飞鹅/云想印
address: '', // ip地址/MAC地址
port: '', // 端口
receiptSize: '58mm', // 小票尺寸 58mm 80mm
printNum: 1, // 打印数量
printContentType: '', // 打印内容
kitchenPrintMode: 'all', // 打印模式(厨房打印菜品) all整单 /only单个
classifyPrint: '0', // 分类打印 0-所有 1-部分分类
categoryIds: [], // 分类Id
status: 1, // 0 禁用 1启用
volumeSwitch: 1, // 媒体声音开关 0关1开
}
const form = ref({ ...obj })
const rules = {
name: [
{
required: true,
message: '请输入设备名称',
trigger: 'blur'
}
],
brand: [
{
required: true,
message: '请选择打印机品牌',
trigger: 'change'
}
],
printNum: [
{
required: true,
message: '请选择打印数量',
trigger: 'change'
}
],
// kitchenPrintMode: [
// {
// required: true,
// message: '请选择打印模式',
// trigger: 'change'
// }
// ]
}
function dialogClosed() {
formRef.value.resetFields()
form.value = { ...obj }
}
const formDivRef = ref(null)
async function dialogOpen() {
await nextTick()
if (formDivRef.value) {
// console.log('开始滚动到顶部')
formDivRef.value.scrollTop = 0
}
}
// 打印类型切换
function printTypeChange(e, index, scope) {
if (index == 1 && printTypeList.value[index].values.length == 0) {
form.value.categoryList = []
}
let arr = []
printTypeList.value.forEach(item => {
arr.push(...item.values)
})
form.value.printContentType = arr.join(',')
}
function submitHandle() {
console.log('submitHandle===', form.value);
formRef.value.validate(async valid => {
try {
if (valid) {
loading.value = true
const data = { ...form.value }
data.categoryIds = form.value.categoryIds.join(',')
if (form.value.id) {
await printerApi.edit(data)
} else {
await printerApi.add(data)
}
ElMessage.success(form.value.id ? '编辑成功' : '添加成功')
visible.value = false
handleQueryClick();
}
} catch (error) {
console.log(error);
}
setTimeout(() => {
loading.value = false
}, 300);
})
}
// 更改状态
async function statusChange(e, data) {
try {
await printerApi.edit({
id: data.id,
status: data.status
});
handleQueryClick();
} catch (error) {
console.log(error);
}
}
const {
searchRef,
@@ -138,27 +400,41 @@ function handdeleteevent(item) {
}
// 新增
async function handleAddClick() {
addModalRef.value?.setModalVisible();
// addModalRef.value?.setModalVisible();
visible.value = true
}
// 编辑
async function handleEditClick(row: IObject) {
editModalRef.value?.handleDisabled(false);
editModalRef.value?.setModalVisible();
// 根据id获取数据进行填充
let data = await UserAPI.get(row.id);
data.printType = data.printType.split(",");
form.value = { ...row }
visible.value = true
if (data.categoryIds) {
data.categoryIdsArr = JSON.parse(data.categoryIds);
} else {
data.categoryIdsArr = [];
}
console.log(data.categoryIdsArr);
console.log(data);
form.value.categoryIds = form.value.categoryIds.split(',')
data.classifyPrint = data.classifyPrint * 1;
const printContentTypes = row.printContentType.split(',')
printTypeList.value.forEach(val => {
val.values = _.map(
_.filter(val.list, item => printContentTypes.includes(item.value)),
'value'
);
})
editModalRef.value?.setFormData(data);
// editModalRef.value?.handleDisabled(false);
// editModalRef.value?.setModalVisible();
// // 根据id获取数据进行填充
// let data = await UserAPI.get(row.id);
// data.printType = data.printType.split(",");
// if (data.categoryIds) {
// data.categoryIdsArr = JSON.parse(data.categoryIds);
// } else {
// data.categoryIdsArr = [];
// }
// console.log(data.categoryIdsArr);
// console.log(data);
// data.classifyPrint = data.classifyPrint * 1;
// editModalRef.value?.setFormData(data);
}
// 其他工具栏
function handleToolbarClick(name: string) {

View File

@@ -3,7 +3,8 @@
<div class="item_wrap">
<div class="title">您好欢迎登录</div>
<div class="item_list">
<div class="item" v-for="(item, index) in quickStore.quickMenus" :key="item.id" @click="menuClick(item.menuId)">
<div class="item" v-for="(item, index) in quickStore.quickMenus.splice(0, 6)" :key="item.id"
@click="menuClick(item.menuId)">
<img class="icon" :src="icons[index + 1]" alt="">
{{ returnMenuName(item.menuId) }}
</div>

View File

@@ -0,0 +1,81 @@
<template>
<el-dialog :title="form.id ? '编辑耗材类型' : '添加耗材类型'" width="600px" v-model="visible" @close="onClose">
<el-form ref="formRef" :model="form" :rules="rules" label-width="120" label-position="right">
<el-form-item label="耗材类型名称" prop="name">
<el-input v-model="form.name" placeholder="请输入耗材类型名称" :maxlength="20"></el-input>
</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">
<el-button @click="visible = false"> </el-button>
<el-button type="primary" @click="handleOk" :loading="confirmLoading"> </el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import _ from 'lodash'
import { ref } from 'vue'
import Api from "@/api/product/cons-group";
const visible = ref(false)
const formRef = ref(null)
const form = ref({
name: '',
status: 1
})
const rules = ref({
name: [
{
required: true,
message: '请输入耗材类型名称',
trigger: 'blur'
}
]
})
const emit = defineEmits(['success'])
const confirmLoading = ref(false)
function handleOk() {
formRef.value.validate(async vaild => {
try {
if (vaild) {
confirmLoading.value = true
if (form.value.id) {
await Api.edit(form.value)
ElMessage.success('编辑成功')
} else {
await Api.add(form.value)
ElMessage.success('添加成功')
}
emit('success')
visible.value = false
}
} catch (error) {
console.log(error);
} finally {
confirmLoading.value = false
}
})
}
function onClose() {
formRef.value.resetFields()
form.value.name = ''
form.value.status = 1
}
function open(obj) {
visible.value = true
if (obj && obj.id) {
form.value = _.cloneDeep(obj)
}
}
defineExpose({ open })
</script>

View File

@@ -9,17 +9,9 @@
@reset-click="handleResetClick"
/> -->
<!-- 列表 -->
<page-content
ref="contentRef"
:content-config="contentConfig"
@add-click="handleAddClick"
@edit-click="handleEditClick"
@export-click="handleExportClick"
@search-click="handleSearchClick"
@toolbar-click="handleToolbarClick"
@operat-click="handleOperatClick"
@filter-change="handleFilterChange"
>
<page-content ref="contentRef" :content-config="contentConfig" @add-click="handleAddClick"
@edit-click="handleEditClick" @export-click="handleExportClick" @search-click="handleSearchClick"
@toolbar-click="handleToolbarClick" @operat-click="handleOperatClick" @filter-change="handleFilterChange">
<template #status="scope">
<el-tag :type="scope.row[scope.prop] == 1 ? 'success' : 'info'">
{{ scope.row[scope.prop] == 1 ? "启用" : "禁用" }}
@@ -29,36 +21,22 @@
{{ returnOptionsLabel(scope.prop, scope.row[scope.prop]) }}
</template>
<template #switch="scope">
<el-switch
v-model="scope.row[scope.prop]"
disabled
:active-value="1"
:inactive-value="0"
></el-switch>
<el-switch v-model="scope.row[scope.prop]" disabled :active-value="1" :inactive-value="0"></el-switch>
</template>
<template #mobile="scope">
<el-text>{{ scope.row[scope.prop] }}</el-text>
<copy-button
v-if="scope.row[scope.prop]"
:text="scope.row[scope.prop]"
style="margin-left: 2px"
/>
<copy-button v-if="scope.row[scope.prop]" :text="scope.row[scope.prop]" style="margin-left: 2px" />
</template>
</page-content>
<!-- 新增 -->
<page-modal
ref="addModalRef"
:modal-config="addModalConfig"
@submit-click="handleSubmitClick"
></page-modal>
<page-modal ref="addModalRef" :modal-config="addModalConfig" @submit-click="handleSubmitClick"></page-modal>
<!-- 编辑 -->
<page-modal
ref="editModalRef"
:modal-config="editModalConfig"
@submit-click="handleSubmitClick"
></page-modal>
<page-modal ref="editModalRef" :modal-config="editModalConfig" @submit-click="handleSubmitClick"></page-modal>
<!-- 添加分类 -->
<addClassificationModal ref="addClassificationModalRef" @success="handleResetClick" />
</div>
</template>
@@ -72,6 +50,9 @@ import editModalConfig from "./config/edit";
import searchConfig from "./config/search";
import { returnOptionsLabel } from "./config/config";
import { isSyncStatus } from "@/utils/index";
import addClassificationModal from "./components/addClassificationModal.vue";
const addClassificationModalRef = ref(null)
const {
searchRef,
@@ -98,16 +79,18 @@ if (isSyncStatus()) {
// 新增
async function handleAddClick() {
addModalRef.value?.setModalVisible();
addClassificationModalRef.value.open()
// addModalRef.value?.setModalVisible();
// addModalConfig.formItems[2]!.attrs!.data =
}
// 编辑
async function handleEditClick(row: IObject) {
editModalRef.value?.handleDisabled(false);
editModalRef.value?.setModalVisible();
// 根据id获取数据进行填充
console.log(row);
editModalRef.value?.setFormData({ ...row });
addClassificationModalRef.value.open(row)
// editModalRef.value?.handleDisabled(false);
// editModalRef.value?.setModalVisible();
// // 根据id获取数据进行填充
// console.log(row);
// editModalRef.value?.setFormData({ ...row });
}
1;
// 其他工具栏

View File

@@ -6,10 +6,13 @@
<el-input v-model="form.conName" placeholder="请输入耗材名称"></el-input>
</el-form-item>
<el-form-item label="耗材分类" prop="consGroupId">
<el-select v-model="form.consGroupId" placeholder="请选择耗材分类" style="width: 200px">
<el-option v-for="option in consGroups" :key="option.conTypeId" :label="option.label"
:value="option.id"></el-option>
</el-select>
<div class="center" style="gap: 14px;">
<el-select v-model="form.consGroupId" placeholder="请选择耗材分类" style="width: 200px">
<el-option v-for="option in consGroups" :key="option.conTypeId" :label="option.label"
:value="option.id"></el-option>
</el-select>
<el-button type="primary" icon="Plus" @click="addClassificationModalRef.open()">添加耗材类型</el-button>
</div>
</el-form-item>
<el-form-item label="耗材价格" prop="price">
<div class="center">
@@ -54,6 +57,7 @@
</el-form-item>
</el-form>
</el-dialog>
<addClassificationModal ref="addClassificationModalRef" @success="getConsGroups" />
</template>
@@ -62,6 +66,9 @@ import { ref, reactive, computed } from "vue";
import consApi from "@/api/product/cons";
import consGroupApi from "@/api/product/cons-group";
import { ElMessage } from "element-plus";
import addClassificationModal from "../../classification/components/addClassificationModal.vue";
const addClassificationModalRef = ref(null)
const consGroups = ref([]);
const rules = {

View File

@@ -97,13 +97,13 @@ const contentConfig: IContentConfig = {
align: "center",
prop: "conName",
},
{
label: "单位",
align: "center",
prop: "conUnit",
templet: "custom",
slotName: "conUnit",
},
// {
// label: "单位",
// align: "center",
// prop: "conUnit",
// templet: "custom",
// slotName: "conUnit",
// },
{
label: "所属商品",
align: "center",
@@ -112,14 +112,24 @@ const contentConfig: IContentConfig = {
},
{
label: "库存数量",
align: "center",
align: "left",
prop: "stockNumber",
templet: "custom",
slotName: "stockNumber",
},
{
label: "预警值",
align: "center",
prop: "conWarning",
},
{
width: '120',
label: "是否检测耗材",
align: "center",
prop: "isStock",
templet: "custom",
slotName: "isStock",
},
{
label: "是否启用",
align: "center",

View File

@@ -10,6 +10,25 @@
<page-content ref="contentRef" :content-config="contentConfig" @add-click="handleAddClick"
@edit-click="handleEditClick" @export-click="handleExportClick" @search-click="handleSearchClick"
@toolbar-click="handleToolbarClick" @operat-click="handleOperatClick" @filter-change="handleFilterChange">
<template #stockNumber="scope">
<div class="columne">
<div class="center">
<el-text>第一单位</el-text>
<el-text>{{ scope.row.stockNumber }}/{{ scope.row.conUnit }}</el-text>
</div>
<div class="center" v-if="scope.row.conUnitTwo">
<el-text>第二单位</el-text>
<el-text>{{ scope.row.stockNumber / scope.row.conUnitTwoConvert }}/{{ scope.row.conUnitTwo }}</el-text>
</div>
<div class="center" v-else>
<el-text type="info">未设置第二单位</el-text>
</div>
</div>
</template>
<template #isStock="scope">
<el-switch v-model="scope.row.isStock" :active-value="1" :inactive-value="0"
@click="isStockChange($event, scope.row)"></el-switch>
</template>
<template #status="scope">
<el-tag :type="scope.row[scope.prop] == 1 ? 'success' : 'info'">
{{ scope.row[scope.prop] == 1 ? "启用" : "禁用" }}
@@ -124,6 +143,12 @@ const {
handleFilterChange,
} = usePage();
async function isStockChange(e, row) {
// console.log('isStockChange.e', e);
// console.log('isStockChange.row', row);
await consApi.edit(row)
}
function toGoods(id: number | string) {
router.push({ path: "/product/index", query: { id: id } });
}
@@ -280,6 +305,11 @@ onMounted(() => {
</script>
<style scoped lang="scss">
.center {
display: flex;
gap: 10px;
}
.goodslang {
display: flex;
justify-content: flex-start;

View File

@@ -198,6 +198,12 @@ function handleLogin() {
userStore
.login(user)
.then(async (res) => {
console.log('login===', res);
localStorage.setItem('shopStaff', JSON.stringify(res.shopStaff))
const token = getToken();
console.log("token", token);
$douyin_checkIn({

View File

@@ -0,0 +1,163 @@
<template>
<el-dialog :title="forms.id ? '编辑分组' : '添加分组'" width="600px" v-model="visible" @close="onClose">
<el-form :model="forms" label-width="120px" ref="elFormref" :rules="rules">
<el-form-item label="分组名称" prop="name">
<el-input v-model="forms.name" />
</el-form-item>
<el-form-item label="选择商品">
<el-button type="primary" icon="Plus" @click="addgoods">添加商品</el-button>
</el-form-item>
<el-form-item label="" v-if="selectData.length">
<!-- 选责商品列表 -->
<selectGoodslist @deleteItememit="deleteItem($event)" :list="selectData"></selectGoodslist>
</el-form-item>
<el-form-item label="分组状态">
<el-radio-group v-model="forms.status">
<el-radio :value="1" label="启用" />
<el-radio :value="0" label="禁用" />
</el-radio-group>
</el-form-item>
<el-form-item label="售卖时间管控">
<el-radio-group v-model="forms.useTime">
<el-radio :value="1" label="启用" />
<el-radio :value="0" label="禁用" />
</el-radio-group>
</el-form-item>
<el-form-item label="时间选择" v-if="forms.useTime == 1">
<el-time-picker value-format="HH:mm:ss" v-model="forms.time" is-range range-separator="到"
start-placeholder="开始时间" end-placeholder="结束时间" />
</el-form-item>
<el-form-item label="排列方式">
<el-radio-group v-model="forms.sortMode">
<el-radio value="0" label="默认" />
<el-radio value="1" label="价格由高到低" />
<el-radio value="2" label="价格由低到高" />
<el-radio value="3" label="销量由高到低" />
<el-radio value="4" label="销量由低到高" />
</el-radio-group>
</el-form-item>
<el-form-item label="分组排序">
<el-input-number v-model="forms.sort" controls-position="right" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="visible = false"> </el-button>
<el-button type="primary" @click="addConfirm" :loading="confirmLoading"> </el-button>
</div>
</template>
</el-dialog>
<!-- 新增添加商品 -->
<GoodsSelect ref="GoodsSelectRef" @success="e => selectData = e" />
</template>
<script setup>
import _ from 'lodash'
import { ref } from 'vue'
import selectGoodslist from '../goodsGroupconfig/selectGoodslist.vue';
import UserAPI from "@/api/onlineShop/goodsGroupconfig";
import GoodsSelect from '@/components/GoodsSelect/index.vue'
const visible = ref(false)
const GoodsSelectRef = ref(null)
const elFormref = ref(null);
let selectData = ref([]);
const formsObj = {
name: "",
status: 1,
useTime: 0,
sortMode: "0",
sort: 1,
time: "",
saleEndTime: '',
saleStartTime: '',
productIds: '',
productList: ''
}
let forms = ref(_.cloneDeep(formsObj));
const rules = ref({
name: [{ required: true, message: "请输入分组名称", trigger: "blur" }],
});
// 重置表单
function onClose() {
forms.value = _.cloneDeep(formsObj)
elFormref.value.resetFields()
selectData.value = []
}
// 添加商品
async function addgoods() {
GoodsSelectRef.value.show(selectData.value);
}
// 删除商品
function deleteItem(data) {
selectData.value = data;
}
const emit = defineEmits(['success'])
const confirmLoading = ref(false)
// 添加编辑商品分组
function addConfirm() {
elFormref.value.validate(async (valid, fields) => {
try {
if (valid) {
let obj = {
...forms.value,
productList: selectData.value,
};
// 商品选择的合集
if (selectData.value.length) {
let arr = [];
selectData.value.forEach((item, index) => {
arr.push(item.id);
});
obj.productIds = arr;
}
console.log(obj, "商品分组的参数");
if (obj.time && obj.time.length) {
obj.saleStartTime = obj.time[0];
obj.saleEndTime = obj.time[1];
}
if (forms.value.id) {
if (obj.useTime == 0) {
obj.saleStartTime = null;
obj.saleEndTime = null;
}
let res = await UserAPI.update(obj);
if (res.code == 200) {
ElMessage.success("编辑成功");
}
} else {
let res = await UserAPI.addunit(obj);
if (res.code == 200) {
ElMessage.success("添加成功");
}
}
visible.value = false
emit('success')
} else {
console.log("error submit!", fields);
}
} catch (error) {
console.log(error);
}
});
}
function open(obj) {
visible.value = true
if (obj && obj.id) {
forms.value = _.cloneDeep(obj)
selectData.value = obj.productList || []
forms.value.time = [obj.saleStartTime, obj.saleEndTime]
}
}
defineExpose({
open
})
</script>

View File

@@ -5,17 +5,9 @@
<!-- 搜索 -->
<!-- 列表 -->
<page-content
ref="contentRef"
:content-config="contentConfig"
@add-click="handleAddClick"
@edit-click="handleEditClick"
@export-click="handleExportClick"
@search-click="handleSearchClick"
@toolbar-click="handleToolbarClick"
@operat-click="handleOperatClick"
@filter-change="handleFilterChange"
>
<page-content ref="contentRef" :content-config="contentConfig" @add-click="handleAddClick"
@edit-click="handleEditClick" @export-click="handleExportClick" @search-click="handleSearchClick"
@toolbar-click="handleToolbarClick" @operat-click="handleOperatClick" @filter-change="handleFilterChange">
<template #status="scope">
<el-tag :type="scope.row[scope.prop] == 1 ? 'success' : 'info'">
{{ scope.row[scope.prop] == 1 ? "启用" : "禁用" }}
@@ -29,11 +21,7 @@
</template>
<template #mobile="scope">
<el-text>{{ scope.row[scope.prop] }}</el-text>
<copy-button
v-if="scope.row[scope.prop]"
:text="scope.row[scope.prop]"
style="margin-left: 2px"
/>
<copy-button v-if="scope.row[scope.prop]" :text="scope.row[scope.prop]" style="margin-left: 2px" />
</template>
</page-content>
@@ -62,11 +50,7 @@
</page-modal> -->
<!-- 编辑 -->
<page-modal
ref="editModalRef"
:modal-config="editModalConfig"
@submit-click="handleSubmitClick"
>
<page-modal ref="editModalRef" :modal-config="editModalConfig" @submit-click="handleSubmitClick">
<template #gender="scope">
<Dict v-model="scope.formData[scope.prop]" code="gender" v-bind="scope.attrs" />
</template>
@@ -79,33 +63,20 @@
添加商品
</el-button>
<!-- 选责商品列表 -->
<selectGoodslist
@deleteItememit="deleteItem($event)"
:list="selectData"
></selectGoodslist>
<selectGoodslist @deleteItememit="deleteItem($event)" :list="selectData"></selectGoodslist>
</div>
</template>
<template #addmanagementtime="scope">
<template v-if="scope.formData.useTime == 1">
{{ scope.formData }}
<el-time-picker
value-format="HH:mm:ss"
v-model="scope.formData.saleTime"
is-range
range-separator=""
start-placeholder="开始时间"
end-placeholder="结束时间"
/>
<el-time-picker value-format="HH:mm:ss" v-model="scope.formData.saleTime" is-range range-separator="到"
start-placeholder="开始时间" end-placeholder="结束时间" />
</template>
</template>
</page-modal>
</template>
<template v-else>
<page-content
ref="contentRef"
:content-config="contentConfig2"
@operat-click="handleOperatClick"
>
<page-content ref="contentRef" :content-config="contentConfig2" @operat-click="handleOperatClick">
<template #status="scope">
<el-tag :type="scope.row[scope.prop] == 1 ? 'success' : 'info'">
{{ scope.row[scope.prop] == 1 ? "启用" : "禁用" }}
@@ -113,32 +84,17 @@
</template>
</page-content>
</template>
<!-- 添加/编辑分组 -->
<addGoodsGroup ref="addGoodsGroupRef" @success="handleQueryClick" />
<!-- 新增添加商品 -->
<myDialog
title="选择商品"
width="50%"
ref="myDialogRef"
@Confirm="subitgood"
@close="resetSelectData"
>
<page-search
ref="searchRefs"
:search-config="searchConfig2"
@query-click="searchs"
@reset-click="handleResetClick"
/>
<page-content
ref="contentRefs"
v-if="switchref"
:content-config="contentConfig2"
@add-click="handleAddClick"
@edit-click="handleEditClick"
@export-click="handleExportClick"
@search-click="handleSearchClick"
@toolbar-click="handleToolbarClick"
@operat-click="handleOperatClick"
@filter-change="handleFilterChange"
>
<myDialog title="选择商品" width="50%" ref="myDialogRef" @Confirm="subitgood" @close="resetSelectData">
<page-search ref="searchRefs" :search-config="searchConfig2" @query-click="searchs"
@reset-click="handleResetClick" />
<page-content ref="contentRefs" v-if="switchref" :content-config="contentConfig2" @add-click="handleAddClick"
@edit-click="handleEditClick" @export-click="handleExportClick" @search-click="handleSearchClick"
@toolbar-click="handleToolbarClick" @operat-click="handleOperatClick" @filter-change="handleFilterChange">
<template #status="scope">
<el-tag :type="scope.row[scope.prop] == 1 ? 'success' : 'info'">
{{ scope.row[scope.prop] == 1 ? "启用" : "禁用" }}
@@ -152,21 +108,17 @@
</template>
<template #mobile="scope">
<el-text>{{ scope.row[scope.prop] }}</el-text>
<copy-button
v-if="scope.row[scope.prop]"
:text="scope.row[scope.prop]"
style="margin-left: 2px"
/>
<copy-button v-if="scope.row[scope.prop]" :text="scope.row[scope.prop]" style="margin-left: 2px" />
</template>
</page-content>
<!-- <el-table :data="selectData" border style="width: 100%">
<el-table :data="selectData" border style="width: 100%">
<el-table-column prop="date" align="center" label="Date" />
<el-table-column prop="address" align="center" label="Address" />
</el-table> -->
</el-table>
</myDialog>
<!-- 新增 -->
<myDialog :title="title" width="30%" ref="myDialogRefAdd" @Confirm="addConfirm()">
<!-- <myDialog :title="title" width="50%" ref="myDialogRefAdd" @Confirm="addConfirm()">
<el-form :model="forms" label-width="120px" ref="elFormref" :rules="rules">
<el-form-item label="分组名称" prop="name">
<el-input v-model="forms.name" />
@@ -175,11 +127,7 @@
<el-button type="primary" icon="Plus" @click="addgoods">添加商品</el-button>
</el-form-item>
<el-form-item label="" v-if="selectData.length">
<!-- 选责商品列表 -->
<selectGoodslist
@deleteItememit="deleteItem($event)"
:list="selectData"
></selectGoodslist>
<selectGoodslist @deleteItememit="deleteItem($event)" :list="selectData"></selectGoodslist>
</el-form-item>
<el-form-item label="分组状态">
<el-radio-group v-model="forms.status">
@@ -194,14 +142,8 @@
</el-radio-group>
</el-form-item>
<el-form-item label="时间选择" v-if="forms.useTime == 1">
<el-time-picker
value-format="HH:mm:ss"
v-model="forms.time"
is-range
range-separator=""
start-placeholder="开始时间"
end-placeholder="结束时间"
/>
<el-time-picker value-format="HH:mm:ss" v-model="forms.time" is-range range-separator="到"
start-placeholder="开始时间" end-placeholder="结束时间" />
</el-form-item>
<el-form-item label="排列方式">
<el-radio-group v-model="forms.sortMode">
@@ -216,7 +158,7 @@
<el-input-number v-model="forms.sort" controls-position="right" />
</el-form-item>
</el-form>
</myDialog>
</myDialog> -->
</div>
</template>
@@ -235,6 +177,7 @@ import searchConfig2 from "./goodsGroupconfig/search2";
import myDialog from "@/components/mycomponents/myDialog.vue";
import selectGoodslist from "./goodsGroupconfig/selectGoodslist.vue";
import { isSyncStatus } from "@/utils/index";
import addGoodsGroup from "./components/addGoodsGroup.vue";
const {
searchRef,
@@ -268,10 +211,13 @@ if (isSyncStatus()) {
let switchref = ref(false);
const addGoodsGroupRef = ref(null)
// 新增
async function handleAddClick() {
addModalRef.value?.setModalVisible();
selectData.value = [];
addGoodsGroupRef.value.open()
// addModalRef.value?.setModalVisible();
// selectData.value = [];
// 加载上级规格下拉数据源
// addModalConfig.formItems[2]!.attrs!.data = await UserAPI.getPage({ name: "" });
// 加载角色下拉数据源
@@ -377,22 +323,22 @@ function deleteItem(data: any) {
// 编辑
async function handleEditClick(row: IObject) {
editModalRef.value?.handleDisabled(false);
title.value = "编辑分组";
// editModalRef.value?.handleDisabled(false);
// title.value = "编辑分组";
// 加载部门下拉数据源
// editModalConfig.formItems[2]!.attrs!.data = await UserAPI.getPage({ name: "" });
// editModalConfig.formItems[2]!.attrs!.data = await DeptAPI.getOptions();
// 加载角色下拉数据源
// editModalConfig.formItems[4]!.options = await RoleAPI.getOptions();
// 根据id获取数据进行填充
const data = await UserAPI.getunitinfo(row.id);
let obj = { ...data };
selectData.value = data.productList;
obj.time = [obj.saleStartTime, obj.saleEndTime];
for (let key in obj) {
forms[key] = obj[key];
}
myDialogRefAdd.value.open();
// const data = await UserAPI.getunitinfo(row.id);
// let obj = { ...data };
// selectData.value = data.productList;
// obj.time = [obj.saleStartTime, obj.saleEndTime];
// for (let key in obj) {
// forms[key] = obj[key];
// }
addGoodsGroupRef.value.open(row)
// editModalRef.value?.setFormData(obj);
// editModalRef.value?.setModalVisible();
}
@@ -400,19 +346,20 @@ async function handleEditClick(row: IObject) {
function handleToolbarClick(name: string) {
console.log(name);
if (name === "custom1") {
forms = reactive({
name: "",
status: 1,
useTime: 0,
sortMode: "0",
sort: 1,
time: "",
});
selectData.value = [];
title.value = "新增分组";
// forms = reactive({
// name: "",
// status: 1,
// useTime: 0,
// sortMode: "0",
// sort: 1,
// time: "",
// });
// selectData.value = [];
// title.value = "新增分组";
// 新增
myDialogRefAdd.value.open();
// // 新增
// myDialogRefAdd.value.open();
addGoodsGroupRef.value.open()
}
}
// 其他操作列

View File

@@ -9,13 +9,20 @@
<div class="form">
<div class="preview_wrap">
<div class="phone_wrap">
<!-- 首页背景 -->
<div class="index_bg" v-if="tableActive == 'index_bg'">
<img class="bg" v-if="!isJsonArrayString(selectItem.value)" :src="selectItem.value" alt="">
<el-carousel height="500px" v-else>
<el-carousel-item v-for="item in JSON.parse(selectItem.value)">
<!-- 多图轮播 -->
<el-carousel height="500px"
v-if="isJsonArrayString(selectItem.value) && JSON.parse(selectItem.value).length > 0">
<el-carousel-item v-for="(item, idx) in JSON.parse(selectItem.value)" :key="idx">
<img class="bg" :src="item" />
</el-carousel-item>
</el-carousel>
<!-- 单图渲染 -->
<img class="bg" v-else-if="selectItem.value" :src="selectItem.value" alt="" />
<!-- 空状态提示 -->
<div class="empty-tip" v-else>暂无图片请上传</div>
<div class="menu_wrap">
<div class="menu_wrap_div">
<div class="left">
@@ -46,8 +53,11 @@
</div>
</div>
</div>
<!-- 个人中心背景 -->
<div class="my_bg" v-if="tableActive == 'my_bg'">
<img class="bg" :src="selectItem.value" />
<img class="bg" :src="selectItem.value" v-if="selectItem.value" />
<div class="empty-tip my-empty-tip" v-else>暂无图片请上传</div>
<div class="content">
<div class="item" style="display: flex">
<div class="left">
@@ -108,10 +118,13 @@
</div>
</div>
</div>
<!-- 会员背景 -->
<div class="member_bg" v-if="tableActive == 'member_bg'">
<div class="card_wrap">
<div class="card">
<img class="bg" :src="selectItem.value" />
<img class="bg" :src="selectItem.value" v-if="selectItem.value" />
<div class="empty-tip member-empty-tip" v-else>暂无图片请上传</div>
<div class="content">
<div class="ewm">
<i class="icon el-icon-menu"></i>
@@ -155,18 +168,29 @@
</div>
</div>
</div>
<!-- 商品列表背景 -->
<div class="shopinfo_bg" v-if="tableActive == 'shopinfo_bg'">
<img class="bg" :src="selectItem.value" v-if="!isJsonArrayString(selectItem.value)" />
<el-carousel height="120px" v-else>
<el-carousel-item v-for="item in JSON.parse(selectItem.value)">
<!-- 多图轮播 -->
<el-carousel height="120px"
v-if="isJsonArrayString(selectItem.value) && JSON.parse(selectItem.value).length > 0">
<el-carousel-item v-for="(item, idx) in JSON.parse(selectItem.value)" :key="idx">
<img class="bg" :src="item" />
</el-carousel-item>
</el-carousel>
<!-- 单图渲染 -->
<img class="bg" v-else-if="selectItem.value" :src="selectItem.value" alt="" />
<!-- 空状态提示 -->
<div class="empty-tip shopinfo-empty-tip" v-else>暂无图片请上传</div>
<div class="shop_name">{{ shopName }}</div>
<img class="content" src="@/assets/images/shop_editor_bg.png" alt="" />
</div>
<!-- 小票logo -->
<div class="ticket_wrap" v-if="tableActive == 'ticket_logo'">
<img class="logo" :src="selectItem.value" />
<img class="logo" :src="selectItem.value" v-if="selectItem.value" />
<div class="empty-tip ticket-empty-tip" v-else>暂无图片请上传</div>
<img class="ewm" src="@/assets/images/1024.png" />
<div class="row">
<span class="num">123</span>
@@ -185,42 +209,48 @@
</div>
</div>
</div>
<div class="editor_wrap">
<div class="header" style="padding-bottom: 20px">
<div class="t1">
{{ selectItem.name }}
</div>
<div class="t1">{{ selectItem.name }}</div>
<div class="t2">点击图片更换</div>
</div>
<div class="form_item">
<div class="upload_wrap" style="display: flex;flex-direction: column;gap: 28px;"
<!-- 多图上传 -->
<div class="upload_wrap" style="display: flex; flex-direction: column; gap: 28px"
v-if="selectItem.autoKey == 'index_bg' || selectItem.autoKey == 'shopinfo_bg'">
<MultiImageUpload v-model="imgList" @uploadStart="uploading = true" @uploadAllSuccess="MultiOnSuccess" />
<div>
<el-button type="primary" size="large" @click="doSubmit" :loading="uploading"
loading-text="图片上传中...">确认修改</el-button>
</div>
</div>
<div v-else>
<!-- 单图上传 -->
<div v-else style="display: flex;">
<SingleImageUpload v-model="selectItem.value" @onSuccess="onSuccess">
<img v-if="selectItem.value" :src="selectItem.value" class="avatar" />
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</SingleImageUpload>
</div>
<!-- <el-upload
:headers="headers"
class="avatar-uploader"
:action="qiNiuUploadApi"
:show-file-list="false"
:on-success="handleSuccess"
style="width: 200px; height: 200px"
>
<img v-if="selectItem.value" :src="selectItem.value" class="avatar" />
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload> -->
<!-- <div class="title" style="padding-left: 20px;">跳转路径</div>
<el-input style="width: 300px;margin-left: 20px;" />
<el-button type="primary" style="margin-left: 20px;">修改</el-button> -->
<div class="model_wapra">
<!-- 个人中心模板图 -->
<template v-if="selectItem.autoKey == 'my_bg'">
<div class="title">个人中心模板图</div>
<div class="user-bg-list" v-if="userBgList.length > 0">
<img v-for="(url, index) in userBgList" :key="index" :src="url" @click="selectUserBg(url)"
class="user-bg-item" />
</div>
</template>
<!-- VIP背景模板图 -->
<template v-else-if="selectItem.autoKey == 'member_bg'">
<div class="title">会员卡背景模板图</div>
<div class="user-bg-list" v-if="vipBgList.length > 0">
<img v-for="(url, index) in vipBgList" :key="index" :src="url" @click="selectVipBg(url)"
class="user-bg-item" />
</div>
</template>
</div>
<div>
<el-button type="primary" size="large" @click="doSubmit" :loading="uploading" loading-text="图片上传中...">
确认修改
</el-button>
</div>
</div>
</div>
</div>
@@ -230,7 +260,13 @@
<script>
import { ElMessage } from "element-plus";
import shopExtendApi from "@/api/account/shopExtend";
// 确保SvgIcon组件已正确引入
import SvgIcon from "@/components/SvgIcon/index.vue";
export default {
components: {
SvgIcon,
},
data() {
return {
tableActive: "index_bg",
@@ -238,91 +274,151 @@ export default {
selectItem: {},
imageUrl: "",
imgList: [],
shopName: '',
uploading: false
shopName: "",
uploading: false,
userBgList: [
'https://cashier-oss.oss-cn-beijing.aliyuncs.com/upload/2/5c8673150d8e449ba035e3f65866b4ed.png',
'https://cashier-oss.oss-cn-beijing.aliyuncs.com/upload/2/c442af63d29443c687b24d2a3abbf02b.png',
'https://cashier-oss.oss-cn-beijing.aliyuncs.com/upload/2/1da89980fa0f4c7abffbcdacc2c3a059.png',
'https://cashier-oss.oss-cn-beijing.aliyuncs.com/upload/2/16296e40f2634d0da0cf1ff377b2b848.png',
'https://cashier-oss.oss-cn-beijing.aliyuncs.com/upload/2/f442c0d005b94664ac4b37f7fa19731b.png',
],
vipBgList: [
'https://cashier-oss.oss-cn-beijing.aliyuncs.com/upload/2/c2add99b9b21464889bc14823b97c40e.png',
'https://cashier-oss.oss-cn-beijing.aliyuncs.com/upload/2/c0e65f576a1a485da7fac34658b87c24.png',
'https://cashier-oss.oss-cn-beijing.aliyuncs.com/upload/2/629b8c12b8904140bf84df3a57b3c021.png',
'https://cashier-oss.oss-cn-beijing.aliyuncs.com/upload/2/cd06e01ad7e848359cdca718f5a06231.png'
]
};
},
mounted() {
this.getList();
},
methods: {
// 刷新列表数据
// 选择会员背景模板图
selectVipBg(url) {
this.selectItem.value = url;
// this.doSubmit();
},
// 选择个人中心背景模板图
selectUserBg(url) {
this.selectItem.value = url;
// this.doSubmit();
},
// 提交修改
async doSubmit() {
// console.log('this.selectItem.value', this.selectItem.value);
// return
// 多图类型强制同步imgList到selectItem.value
if (["index_bg", "shopinfo_bg"].includes(this.selectItem.autoKey)) {
this.selectItem.value =
this.imgList.length > 0 ? JSON.stringify(this.imgList) : "";
}
// this.selectItem.value = JSON.stringify(this.imgList)
await shopExtendApi.edit({
let data = {
...this.selectItem,
autokey: this.selectItem.autoKey,
});
ElMessage({
message: "编辑成功",
type: "success",
});
this.getList();
};
if (data.value === "") {
ElMessage({
message: "请上传图片",
type: "warning",
});
return;
}
try {
await shopExtendApi.edit(data);
ElMessage({
message: "编辑成功",
type: "success", // 修复提示类型错误
});
this.getList();
} catch (error) {
ElMessage({
message: "编辑失败:" + (error.message || "未知错误"),
type: "error",
});
}
},
// 单图上传成功
onSuccess(response) {
this.doSubmit();
},
// 多图上传成功
async MultiOnSuccess(response) {
console.log(response);
// console.log(this.imgList);
// console.log(this.imgList);
// await nextTick()
this.uploading = false
this.selectItem.value = JSON.stringify(this.imgList)
console.log('onSuccess.selectItem.value', this.selectItem.value);
this.uploading = false;
// 确保imgList有值再序列化
this.selectItem.value =
this.imgList.length > 0 ? JSON.stringify(this.imgList) : "";
// 强制触发视图更新
this.$forceUpdate();
ElMessage({
message: "图片上传成功",
type: "success",
});
},
/**
* 判断字符串是否为合法的 JSON 数组
* @param {string} str - 待判断的字符串
* @returns {boolean} true=是JSON数组字符串 / false=普通字符串/其他
*/
isJsonArrayString(str) {
// 1. 非字符串直接返回 false
if (typeof str !== 'string') {
if (typeof str !== "string" || str.trim() === "") {
return false;
}
// 2. 空字符串返回 false根据业务可调整
if (str.trim() === '') {
return false;
}
try {
// 3. 尝试解析 JSON
const parsed = JSON.parse(str);
// 4. 校验解析结果是否为数组
return Array.isArray(parsed);
} catch (e) {
// 解析失败(普通字符串/非法 JSON→ 返回 false
return false;
}
},
// 切换类型
selectItemChange(key) {
this.tableActive = key;
const { autoKey, id, name, value } = this.tableData.find((item) => item.autoKey == key);
this.selectItem = { autoKey, id, name, value };
if (this.isJsonArrayString(value)) {
this.imgList = JSON.parse(value)
const targetItem = this.tableData.find((item) => item.autoKey === key) || {};
// 深拷贝避免引用问题
this.selectItem = { ...targetItem };
// 多图类型特殊处理
if (["index_bg", "shopinfo_bg"].includes(key)) {
if (this.isJsonArrayString(targetItem.value)) {
this.imgList = JSON.parse(targetItem.value);
} else {
// 非JSON数组时若有单图值则转为数组否则置空
this.imgList = targetItem.value ? [targetItem.value] : [];
}
// 同步更新selectItem.value为JSON数组
this.selectItem.value = JSON.stringify(this.imgList);
} else {
this.imgList = []
this.imgList = [];
// 单图类型保留原有值
this.selectItem.value = targetItem.value || "";
}
console.log(this.selectItem);
},
// 获取装修数据
async getList() {
try {
let res = await shopExtendApi.get({});
this.tableData = res;
this.tableActive = !this.tableActive ? res[0].autoKey : this.tableActive;
this.selectItemChange(this.tableActive);
const res = await shopExtendApi.get({});
this.tableData = res || [];
// 确保有数据时再切换
if (this.tableData.length > 0) {
this.tableActive = this.tableActive || this.tableData[0].autoKey;
this.selectItemChange(this.tableActive);
}
// 补充店铺名称(根据实际接口返回调整)
this.shopName = "我的店铺";
} catch (error) {
console.log(error);
console.error("获取装修数据失败:", error);
ElMessage({
message: "获取数据失败,请刷新重试",
type: "error",
});
}
},
},
@@ -336,7 +432,7 @@ export default {
gap: 30px;
.btn {
width: 100px;
padding: 0 20px;
height: 40px;
border: 1px solid $color;
border-radius: 4px;
@@ -366,6 +462,64 @@ export default {
background-color: #fff;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
// 空状态通用样式
.empty-tip {
width: 100%;
display: flex;
align-items: center;
justify-content: center;
color: #999;
font-size: 14px;
background-color: #f5f5f5;
}
// 首页空状态
.index_bg .empty-tip {
height: 500px;
}
// 商品列表空状态
.shopinfo_bg .empty-tip {
height: 120px;
}
// 个人中心空状态
.my-empty-tip {
height: 180px;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
color: #999;
font-size: 14px;
background-color: #f5f5f5;
}
// 会员背景空状态
.member-empty-tip {
height: 144px;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
color: #999;
font-size: 14px;
background-color: #f5f5f5;
}
// 小票logo空状态
.ticket-empty-tip {
height: 80px;
width: 80px;
display: flex;
align-items: center;
justify-content: center;
color: #999;
font-size: 12px;
background-color: #f5f5f5;
border-radius: 4px;
}
.index_bg {
padding-bottom: 50px;
@@ -653,35 +807,36 @@ export default {
}
.info_wrap {
padding: 20px 10px;
padding: 10px;
display: flex;
align-items: center;
gap: 10px;
.avatar {
width: 40px;
height: 40px;
border-radius: 50%;
background-color: #efefef;
background-color: #f5f5f5;
display: flex;
align-items: center;
justify-content: center;
.icon {
color: #555;
font-size: 24px;
font-size: 20px;
color: #999;
}
}
.info {
flex: 1;
.t1 {
font-size: 14px;
color: #333;
}
.t2 {
font-size: 10px;
font-size: 12px;
color: #999;
margin-top: 4px;
}
}
}
@@ -696,52 +851,55 @@ export default {
}
.shop_name {
padding: 10px 10px 0 10px;
padding: 10px;
font-size: 16px;
color: #333;
font-weight: 500;
}
.content {
width: 100%;
height: auto;
display: block;
}
}
.ticket_wrap {
padding: 15px;
position: relative;
padding: 10px;
.logo {
width: 80px;
height: 80px;
object-fit: contain;
margin-bottom: 10px;
border-radius: 4px;
}
.ewm {
width: 80px;
height: 80px;
position: absolute;
top: 10px;
right: 15px;
}
.logo {
width: 90px;
height: 30px;
object-fit: cover;
object-fit: contain;
margin-bottom: 10px;
}
.row {
margin-top: 5px;
display: flex;
align-items: center;
font-size: 12px;
color: #666;
margin-bottom: 4px;
.num {
font-size: 14px;
color: #333;
font-weight: 500;
margin-right: 10px;
}
.sku {
font-size: 12px;
color: #666;
color: #999;
}
.b {
font-weight: bold;
}
.num {
font-size: 18px;
font-weight: bold;
margin-right: 10px;
font-weight: 500;
color: #333;
}
}
}
@@ -749,28 +907,63 @@ export default {
}
.editor_wrap {
padding-left: 20px;
.header {
.t2 {
color: #999;
font-size: 12px;
}
}
flex: 1;
padding-left: 40px;
.form_item {
display: flex;
align-items: center;
flex-direction: column;
gap: 30px;
.avatar {
display: block;
width: 200px;
height: 200px;
object-fit: cover;
.model_wapra {
.title {
font-size: 16px;
color: #333;
margin-bottom: 10px;
}
}
.title {
flex-shrink: 0;
.user-bg-list {
display: flex;
gap: 10px;
.user-bg-item {
width: 120px;
height: 80px;
object-fit: cover;
border-radius: 4px;
cursor: pointer;
border: 2px solid transparent;
&:hover {
border-color: #40a9ff;
}
}
}
.avatar-uploader {
width: 200px;
height: 200px;
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
.avatar-uploader-icon {
font-size: 28px;
color: #8c8c8c;
width: 178px;
height: 178px;
line-height: 178px;
text-align: center;
}
.avatar {
width: 100%;
height: 100%;
display: block;
}
}
}
}

View File

@@ -93,13 +93,8 @@
<div>
实收金额
<span style="color: red">{{ detail.payAmount }}</span>
<el-button
v-if="detail.status != 'unpaid' && detail.refundAmount < detail.payAmount"
size="small"
type="danger"
class="u-m-l-10"
@click="tuikuan()"
>
<el-button v-if="detail.status != 'unpaid' && detail.refundAmount < detail.payAmount" size="small"
type="danger" class="u-m-l-10" @click="tuikuan()">
<span>退款</span>
</el-button>
</div>
@@ -130,35 +125,22 @@
<div style="margin-bottom: 16px; font-size: 16px">商品信息</div>
<template v-for="(item, index) in detail.detailMap" :key="index">
<h4>{{ index }}次下单</h4>
<el-table
:data="item"
:ref="'refTable' + index"
@select-all="tableSelectAll($event, index)"
>
<el-table :data="item" :ref="'refTable' + index" @select-all="tableSelectAll($event, index)">
<!-- <el-table-column type="selection" width="55" /> -->
<el-table-column label="数量" type="selection">
<template v-slot="scope">
<div v-if="detail.status == 'unpaid'">
<el-checkbox
v-if="scope.row.num - scope.row.returnNum > 0"
v-model="scope.row.checked"
/>
<el-checkbox v-if="scope.row.num - scope.row.returnNum > 0" v-model="scope.row.checked" />
</div>
<div v-else>
<el-checkbox
v-if="scope.row.num - scope.row.refundNum > 0"
v-model="scope.row.checked"
/>
<el-checkbox v-if="scope.row.num - scope.row.refundNum > 0" v-model="scope.row.checked" />
</div>
</template>
</el-table-column>
<el-table-column label="商品" width="150">
<template v-slot="scope">
<div class="shop_info">
<el-image
:src="scope.row.productImg"
style="width: 40px; height: 40px"
></el-image>
<el-image :src="scope.row.productImg" style="width: 40px; height: 40px"></el-image>
<div class="info">
<span :class="[scope.row.isVip == 1 ? 'colorStyle' : '']">
{{ scope.row.productName }}
@@ -191,32 +173,17 @@
<el-table-column label="实付">
<template v-slot="scope">{{ scope.row.payAmount }}</template>
</el-table-column>
<el-table-column
v-if="detail.status == 'unpaid'"
label="可退菜数量"
align="center"
width="130px"
>
<el-table-column v-if="detail.status == 'unpaid'" label="可退菜数量" align="center" width="130px">
<template v-slot="scope">
<el-input-number
v-if="scope.row.checked"
:min="0"
style="width: 100px"
v-model="scope.row.selNumber"
:max="scope.row.num - scope.row.returnNum"
></el-input-number>
<el-input-number v-if="scope.row.checked" :min="0" style="width: 100px" v-model="scope.row.selNumber"
:max="scope.row.num - scope.row.returnNum"></el-input-number>
<span class="" v-else>{{ scope.row.num - scope.row.returnNum }}</span>
</template>
</el-table-column>
<el-table-column v-else label="可退款数量" align="center" width="130px">
<template v-slot="scope">
<el-input-number
v-if="scope.row.checked"
:min="0"
style="width: 100px"
v-model="scope.row.selNumber"
:max="scope.row.num - scope.row.refundNum - scope.row.returnNum"
></el-input-number>
<el-input-number v-if="scope.row.checked" :min="0" style="width: 100px" v-model="scope.row.selNumber"
:max="scope.row.num - scope.row.refundNum - scope.row.returnNum"></el-input-number>
<span class="" v-else>
{{ scope.row.num - scope.row.refundNum - scope.row.returnNum }}
</span>
@@ -231,23 +198,13 @@
<el-table-column label="操作" fixed="right">
<template v-slot="scope">
<template v-if="detail.status != 'unpaid'">
<el-button
v-if="canTuikuan(scope.row)"
link
size="small"
@click="tuikuan(scope.row)"
>
<el-button v-if="canTuikuan(scope.row)" link size="small" @click="tuikuan(scope.row)">
<span>退款</span>
</el-button>
<span class="color-999" v-if="scope.row.status == 'refund'">已退款</span>
</template>
<template v-if="detail.status == 'unpaid'">
<el-button
v-if="canTuicai(scope.row)"
link
size="small"
@click="tuicai(scope.row)"
>
<el-button v-if="canTuicai(scope.row)" link size="small" @click="tuicai(scope.row)">
<span>退菜</span>
</el-button>
<span class="color-999" v-else>已退菜</span>
@@ -257,41 +214,25 @@
</el-table>
</template>
<!-- 退款 -->
<div
class="u-p-20 u-flex u-row-right"
v-if="
detail.status !== 'refund' &&
detail.status !== 'unpaid' &&
detail.status !== 'cancelled'
"
>
<el-checkbox
v-model="allSelected"
@change="allSelectedChange"
label="全选"
></el-checkbox>
<div class="u-p-20 u-flex u-row-right" v-if="
detail.status !== 'refund' &&
detail.status !== 'unpaid' &&
detail.status !== 'cancelled'
">
<el-checkbox v-model="allSelected" @change="allSelectedChange" label="全选"></el-checkbox>
<el-button type="danger" class="u-m-l-20" @click.stop="tuikuan('all')">退款</el-button>
</div>
<!-- 退菜 -->
<div class="u-p-20 u-flex u-row-right" v-if="detail.status == 'unpaid'">
<el-checkbox
v-model="allSelected"
@change="allSelectedChange"
label="全选"
></el-checkbox>
<el-checkbox v-model="allSelected" @change="allSelectedChange" label="全选"></el-checkbox>
<el-button type="danger" class="u-m-l-20" @click.stop="tuicai('all')">退菜</el-button>
</div>
</div>
</div>
</el-drawer>
<!-- 退款 -->
<return-money
:modal="false"
ref="refReturnMoney"
:max="selGoods.num"
:goods="selGoods"
@confirm="refReturnMoneyConfirm"
></return-money>
<return-money :modal="false" ref="refReturnMoney" :max="selGoods.num" :goods="selGoods"
@confirm="refReturnMoneyConfirm"></return-money>
<!-- 退菜 -->
<order-return-cart ref="refReturnCart" @confirm="refReturnCartConfirm"></order-return-cart>
</div>
@@ -466,6 +407,12 @@ export default {
},
tuikuan(item) {
if (!item) {
let arrs = []
for (let i in this.detail.detailMap) {
this.detail.detailMap[i].map((v) => {
arrs.push(v);
});
}
this.$refs.refReturnMoney.open([], this.detail);
return;
}
@@ -484,6 +431,9 @@ export default {
if (arr.length == 0) {
return ElMessage.error("请选择要退款的商品和数量");
}
console.log('tuikuan===', arr);
this.$refs.refReturnMoney.open(arr, this.detail);
},
tuicai(item) {
@@ -502,7 +452,7 @@ export default {
if (arr.length == 0) {
return ElMessage.error("请选择要退菜的商品和数量");
}
console.log(arr);
console.log('tuicai===', arr);
this.$refs.refReturnCart.open(arr, this.detail);
},

View File

@@ -4,11 +4,7 @@
<div class="u-flex u-col-top" v-if="goodsList && goodsList.length">
<span class="u-m-0">退款商品</span>
<div class="u-p-l-20 goods-list">
<div
class="u-flex u-font-12 goods-list-item"
v-for="(goods, index) in goodsList"
:key="index"
>
<div class="u-flex u-font-12 goods-list-item" v-for="(goods, index) in goodsList" :key="index">
<span class="">
{{ goods.productName }}
</span>
@@ -35,12 +31,7 @@
<div class="flex u-row-between">
<span class="color-red">退款金额</span>
<div class="u-flex u-flex-1 u-p-l-20">
<el-input-number
type="number"
v-model="number"
:min="min"
:max="canReturnMoney"
></el-input-number>
<el-input-number type="number" v-model="number" :min="min" :max="canReturnMoney"></el-input-number>
<span class="u-m-l-10">可退{{ canReturnMoney }}</span>
</div>
</div>
@@ -73,13 +64,8 @@
</div>
<div class="u-flex u-flex-wrap tags">
<div
class="tag"
v-for="(tag, index) in tags"
@click="changeSel(tag)"
:key="index"
:class="{ active: tag.checked }"
>
<div class="tag" v-for="(tag, index) in tags" @click="changeSel(tag)" :key="index"
:class="{ active: tag.checked }">
{{ tag.label }}
</div>
</div>
@@ -94,16 +80,22 @@
</template>
</el-dialog>
<safe-password ref="refPassword" @confirm="pwdConfirm"></safe-password>
<!-- 退款退菜推库存的操作弹窗 -->
<refundConsModal ref="refundConsModalRef" :list="refundList" @success="refundConsModalSuccess" />
</div>
</template>
<script>
<script>
import { ElMessage } from "element-plus";
import safePassword from "./password.vue";
import { useUserStore } from "@/store/modules/user";
import { useCartsStore } from "@/store/modules/carts";
import categoryApi from "@/api/product/productclassification";
import refundConsModal from "@/components/refundConsModal.vue";
const shopUser = useUserStore();
export default {
components: {
safePassword,
refundConsModal
},
props: {
modal: {
@@ -135,6 +127,8 @@ export default {
goods: {
productId: -999,
},
refundList: [],
refundStock: ''
};
},
computed: {
@@ -196,18 +190,26 @@ export default {
.join(",");
const note = selTag + (this.note.length > 0 ? "," + this.note : "");
console.log(note);
this.$emit("confirm", {
const data = {
refundAmount: this.number,
cash: this.cash,
refundReason: note,
refundDetails: this.goodsList.map((v) => {
return { id: v.id, num: v.num };
return { id: v.id, num: v.selNumber };
}),
...e,
});
refundStock: this.refundStock
}
this.$emit("confirm", data);
this.close();
},
confirm() {
// 退款推库存的操作
refundConsModalSuccess(e) {
this.refundStock = e
this.emitTuikuan()
},
async confirm() {
const selTag = this.tags
.filter((item) => item.checked)
.map((item) => item.label)
@@ -220,14 +222,58 @@ export default {
this.$refs.refPassword.open();
return;
}
// 在这里给订单的商品补全库存信息 start
const carts = useCartsStore();
let categorys = JSON.parse(localStorage.getItem('categorys'))
let shopInfo = JSON.parse(localStorage.getItem('userInfo'))
if (!categorys) {
categorys = await categoryApi.getList({})
}
this.goodsList.forEach(item => {
carts.goods.forEach(val => {
if (item.productId == val.id) {
if (shopInfo.refundMode == 1) {
// 跟随分类退款模式
categorys.forEach(v => {
if (val.categoryId == v.id) {
item.refundMode = v.refundMode
}
})
} else {
// 跟随商品退款模式及
item.refundMode = val.refundMode
}
}
})
})
console.log('this.goodsList===', this.goodsList);
// 在这里给订单的商品补全库存信息 end
this.goodsList.forEach(item => {
if (item.refundMode == 3) {
this.refundList.push({
name: item.productName,
num: item.selNumber
})
}
})
if (this.refundList.length > 0) {
this.$refs.refundConsModalRef.show()
return
}
this.emitTuikuan();
},
},
mounted() {},
mounted() { },
};
</script>
<style lang="scss" scoped>
<style lang="scss" scoped>
:deep(.el-tag) {
margin-top: 10px;
margin-right: 10px;
@@ -237,6 +283,7 @@ export default {
line-height: 35px;
height: 35px;
}
.tags {
.tag {
margin: 10px 10px 0 0;
@@ -246,6 +293,7 @@ export default {
font-size: 14px;
color: #000;
cursor: pointer;
&.active {
color: #1890ff;
background: #e8f4ff;
@@ -253,14 +301,17 @@ export default {
}
}
}
:deep(.number-box .el-input__inner::-webkit-inner-spin-button) {
-webkit-appearance: none;
margin: 0;
}
:deep(.number-box .el-input__inner::-webkit-outer-spin-button) {
-webkit-appearance: none;
margin: 0;
}
.goods-list-item {
height: 24px;
line-height: 24px;
@@ -268,6 +319,7 @@ export default {
border-radius: 3px;
padding: 0 10px;
}
.goods-list-item:not(:first-child) {
margin-top: 6px;
}

View File

@@ -15,7 +15,11 @@
{{ scope.row[scope.prop] == 1 ? "启用" : "禁用" }}
</el-tag>
</template>
<template #refundMode="scope">
<!-- <el-switch v-model="scope.row[scope.prop]" :active-value="1" :inactive-value="0"
@click="isRefundStockChange($event, scope)" /> -->
<el-text>{{ returnLabel(scope.row[scope.prop]) }}</el-text>
</template>
<template #slotNameimage="scope">
<el-image v-if="scope.row.pic" :src="scope.row.pic" lazy style="width: 40px; height: 40px" />
<div v-else></div>
@@ -28,20 +32,20 @@
<copy-button v-if="scope.row[scope.prop]" :text="scope.row[scope.prop]" style="margin-left: 2px" />
</template>
</page-content>
<addCategory ref="addCategoryRef" @success="handleQueryClick" />
<!-- 新增 -->
<page-modal ref="addModalRef" :modal-config="addModalConfig" @submit-click="handleSubmitClick">
<!-- <page-modal ref="addModalRef" :modal-config="addModalConfig" @submit-click="handleSubmitClick">
<template #gender="scope">
<Dict v-model="scope.formData[scope.prop]" code="gender" />
</template>
</page-modal>
</page-modal> -->
<!-- 编辑 -->
<page-modal ref="editModalRef" :modal-config="editModalConfig" @submit-click="handleSubmitClick">
<!-- <page-modal ref="editModalRef" :modal-config="editModalConfig" @submit-click="handleSubmitClick">
<template #gender="scope">
<Dict v-model="scope.formData[scope.prop]" code="gender" v-bind="scope.attrs" />
</template>
</page-modal>
</page-modal> -->
</template>
<template v-else>
<page-content ref="contentRef" :content-config="contentConfig2" @operat-click="handleOperatClick">
@@ -67,6 +71,20 @@ import contentConfig2 from "./categoryconfig/content2";
import editModalConfig from "./categoryconfig/edit";
import searchConfig from "./categoryconfig/search";
import { isSyncStatus, downloadFile } from "@/utils/index";
import addCategory from "./components/addCategory.vue";
const addCategoryRef = ref(null)
const options = ref([
{ label: "退菜退库存", value: 1 },
{ label: "仅退菜不退库存", value: 2 },
{ label: "每次询问-退菜后弹窗提示,可手动选择", value: 3 },
])
function returnLabel(value: number) {
const option = options.value.find((option) => option.value === value);
return option ? option.label : "未知";
}
const {
searchRef,
@@ -106,9 +124,20 @@ async function handleExportClick() {
}
}
// 退菜是否退库存开关切换
async function isRefundStockChange(e: any, scope: any) {
try {
await UserAPI.update(scope.row);
} catch (error) {
console.log(error);
ElMessage.error("操作失败");
}
}
// 新增
async function handleAddClick() {
addModalRef.value?.setModalVisible();
addCategoryRef.value.open()
// addModalRef.value?.setModalVisible();
// 加载部门下拉数据源
// addModalConfig.formItems[2]!.attrs!.data = await DeptAPI.getOptions();
// 加载角色下拉数据源
@@ -116,15 +145,16 @@ async function handleAddClick() {
}
// 编辑
async function handleEditClick(row: IObject) {
editModalRef.value?.handleDisabled(false);
// 加载部门下拉数据源
// editModalConfig.formItems[2]!.attrs!.data = await DeptAPI.getOptions();
// 加载角色下拉数据源
// editModalConfig.formItems[4]!.options = await RoleAPI.getOptions();
// 根据id获取数据进行填充
const data = await UserAPI.getunitinfo(row.id);
editModalRef.value?.setFormData(data);
editModalRef.value?.setModalVisible();
addCategoryRef.value.open(row)
// editModalRef.value?.handleDisabled(false);
// // 加载部门下拉数据源
// // editModalConfig.formItems[2]!.attrs!.data = await DeptAPI.getOptions();
// // 加载角色下拉数据源
// // editModalConfig.formItems[4]!.options = await RoleAPI.getOptions();
// // 根据id获取数据进行填充
// const data = await UserAPI.getunitinfo(row.id);
// editModalRef.value?.setFormData(data);
// editModalRef.value?.setModalVisible();
}
// 其他工具栏
function handleToolbarClick(name: string) {

View File

@@ -10,7 +10,7 @@ const modalConfig: IModalConfig<UserForm> = {
draggable: true,
},
form: {
labelWidth: 100,
labelWidth: 120,
},
formAction: UserAPI.addunit,
beforeSubmit(data) {
@@ -31,6 +31,26 @@ const modalConfig: IModalConfig<UserForm> = {
prop: "pic",
type: "UpImage",
},
{
label: '退菜是否退库存',
prop: 'refundMode',
type: 'radio',
options: [
{ label: "退菜退库存", value: 1 },
{ label: "仅退菜不退库存", value: 2 },
{ label: "每次询问-退菜后弹窗提示,可手动选择", value: 3 },
],
initialValue: 1,
},
{
label: "排序",
prop: "sort",
type: "input-number",
attrs: {
placeholder: "请输入排序",
},
initialValue: 1,
},
{
label: "开关",
prop: "status",
@@ -44,15 +64,6 @@ const modalConfig: IModalConfig<UserForm> = {
inactiveValue: 0,
},
initialValue: 1,
},
{
label: "排序",
prop: "sort",
type: "input-number",
attrs: {
placeholder: "请输入排序",
},
initialValue: 1,
}
],
};

View File

@@ -77,6 +77,13 @@ const contentConfig: IContentConfig<UserPageQuery> = {
templet: "switch",
slotName: "status",
},
{
label: "退菜是否退库存",
align: "center",
prop: "refundMode",
templet: "custom",
slotName: "refundMode",
},
{
label: "操作",
align: "center",

View File

@@ -35,6 +35,26 @@ const modalConfig: IModalConfig<UserForm> = {
prop: "pic",
type: "UpImage",
},
{
label: '退菜是否退库存',
prop: 'refundMode',
type: 'radio',
options: [
{ label: "退菜退库存", value: 1 },
{ label: "仅退菜不退库存", value: 2 },
{ label: "每次询问-退菜后弹窗提示,可手动选择", value: 3 },
],
initialValue: 1,
},
{
label: "排序",
prop: "sort",
type: "input-number",
attrs: {
placeholder: "请输入排序",
},
initialValue: 1,
},
{
label: "开关",
prop: "status",
@@ -48,15 +68,6 @@ const modalConfig: IModalConfig<UserForm> = {
inactiveValue: 0,
},
initialValue: 1,
},
{
label: "排序",
prop: "sort",
type: "input-number",
attrs: {
placeholder: "请输入排序",
},
initialValue: 1,
}
],
};

View File

@@ -0,0 +1,100 @@
<template>
<el-dialog :title="form.id ? '添加分类' : '编辑分类'" width="650px" v-model="visible" @close="closeHandle">
<el-form ref="formRef" :rules="rules" :model="form" label-position="right" label-width="120px">
<el-form-item label="分类名称" prop="name">
<el-input placeholder="请输入分类名称" v-model="form.name"></el-input>
</el-form-item>
<el-form-item label="分类图片">
<SingleImageUpload v-model="form.pic" />
</el-form-item>
<el-form-item label="退菜是否退库存">
<el-radio-group v-model="form.refundMode">
<el-radio label="退菜退库存" :value="1"></el-radio>
<el-radio label="仅退菜不退库存" :value="2"></el-radio>
<el-radio label="每次询问-退菜后弹窗提示" :value="3"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="排序">
<el-input-number v-model="form.sort" :min="1" :step="1"></el-input-number>
</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">
<el-button @click="visible = false"> </el-button>
<el-button type="primary" @click="handleOk" :loading="loading"> </el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import _ from 'lodash'
import { ref } from 'vue'
import UserAPI from "@/api/product/productclassification";
const visible = ref(false)
const loading = ref(false)
const formRef = ref(null)
const form = ref({
id: '',
name: '',
pic: '',
refundMode: 1, // 退菜是否退库存 1退菜退库存 2仅退菜不退库存 3每次询问-退菜后弹窗提示
sort: 1,
status: 1
})
const rules = ref({
name: [
{
required: true,
message: '请输入单位名称',
trigger: 'blur'
}
]
})
const emit = defineEmits(['success'])
function handleOk() {
formRef.value.validate(async (valid) => {
try {
if (valid) {
loading.value = true
if (form.value.id == '') {
await UserAPI.addunit(form.value)
} else {
await UserAPI.update(form.value)
}
setTimeout(() => {
visible.value = false
ElMessage.success(form.value.id ? '添加成功' : '编辑成功')
emit('success')
}, 500);
}
} catch (error) {
console.log(error);
} finally {
setTimeout(() => {
loading.value = false
}, 500);
}
})
}
function closeHandle() {
form.value.name = ''
}
function open(obj) {
visible.value = true
if (obj && obj.id) {
form.value = _.cloneDeep(obj)
}
}
defineExpose({
open
})
</script>

View File

@@ -0,0 +1,417 @@
<template>
<el-dialog :title="form.id ? '编辑模板' : '新增模板'" width="900px" v-model="visible" @close="onClose">
<div class="form_wrap">
<div class="form_left">
<el-form ref="formRef" :model="form" :rules="rules" label-width="100" label-position="right">
<el-form-item label="模板名称" prop="name">
<el-input v-model="form.name" :maxlength="20" placeholder="如:主食、酒水、辣度、大小等"></el-input>
</el-form-item>
<transition-group name="slide">
<div class="sortable-parent" v-for="(item, index) in form.children" :key="item._id"
:data-parent-index="index"> <!-- 修复Vue3绑定语法 -->
<el-form-item label="规格名称">
<div class="center">
<el-input v-model="item.name" placeholder="如:口味、忌口、温度、分量等"></el-input>
<el-icon v-if="index > 0" size="20" @click="form.children.splice(index, 1)">
<Delete />
</el-icon>
</div>
</el-form-item>
<transition-group name="fade" tag="div">
<el-form-item label="规格值" v-for="(val, i) in item.children" :key="val._id" :data-child-index="i">
<!-- 修复Vue3绑定语法 -->
<div class="center">
<div style="flex:1;">
<el-input v-model="val.name" placeholder="如:五香、微辣、大份、小份等"></el-input>
</div>
<el-icon v-if="i > 0" size="20" @click="form.children[index].children.splice(i, 1)">
<Delete />
</el-icon>
</div>
</el-form-item>
</transition-group>
<el-form-item style="margin-top:-10px;">
<el-button size="small" @click="addSpecValue(index)">添加规格值</el-button>
</el-form-item>
</div>
</transition-group>
<el-form-item label-width="0">
<div class="flex_end">
<el-button type="primary" @click="addSpec">添加规格</el-button>
</div>
</el-form-item>
</el-form>
</div>
<div class="form_right">
<div class="preview_wrap">
<div class="title_wrap">
<el-text size="large">效果预览</el-text>
<el-text size="small">可拖动排序</el-text>
</div>
<div class="row_wrap" id="parent-sort">
<!-- 修复index未定义 + 绑定语法错误 -->
<div class="row" v-for="(item, index) in form.children" :key="item._id" :data-index="index">
<div class="row_top">
<el-text size="large">{{ item.name || '请输入规格名称' }}</el-text>
<el-icon size="20" class="drag-handle">
<Rank />
</el-icon>
</div>
<div class="row_content" v-if="item.children.length" :id="`child-sort-${item._id}`">
<div class="tag" v-for="val in item.children" :key="val._id">
<el-text>{{ val.name || '请输入规格值' }}</el-text>
</div>
</div>
<div class="tag empty-tag" v-else>
<el-text>请添加规格值</el-text>
</div>
</div>
</div>
</div>
</div>
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="visible = false"> </el-button>
<el-button type="primary" @click="handleOk" :loading="loading"> </el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import _ from 'lodash'
import { ref, onMounted, watch, nextTick } from 'vue'
import { ElMessage } from 'element-plus'
import { Delete, Rank } from '@element-plus/icons-vue'
import Sortable from 'sortablejs'
import UserAPI from "@/api/product/specificationsconfig";
const generateId = () => {
return Date.now() + '_' + Math.floor(Math.random() * 10000)
}
const childrenObj = ref({
_id: generateId(),
name: '',
children: [{ _id: generateId(), name: '' }]
})
const formObj = {
_id: '',
name: '',
children: [_.cloneDeep(childrenObj.value)]
}
const loading = ref(false)
const formRef = ref(null)
const form = ref(formObj)
const visible = ref(false)
const rules = ref({
name: [{ required: true, message: '模板名称不能为空', trigger: 'blur' }]
})
function onClose() {
form.value = _.cloneDeep(formObj)
// if (parentSortable) parentSortable.destroy()
// childSortableMap.forEach(s => s.destroy())
// childSortableMap.clear()
// setTimeout(() => {
// }, 50);
visible.value = false
}
function addSpec() {
form.value.children.push({
_id: generateId(),
name: '',
children: [{ _id: generateId(), name: '' }]
})
}
function addSpecValue(parentIndex) {
form.value.children[parentIndex].children.push({
_id: generateId(),
name: ''
})
}
const emit = defineEmits(['success'])
function handleOk() {
formRef.value.validate(async (valid) => {
try {
if (valid) {
let flag = true
let pIndex = 0
let index = 0
form.value.children.forEach((item, index) => {
if (item.name == '') {
flag = false
pIndex = index
return
}
item.children.forEach((val, i) => {
if (val.name == '') {
flag = false
pIndex = index
index = i
return
}
})
})
if (!flag) {
ElMessage.error('请补全规格信息')
return
}
loading.value = true
console.log('最终提交数据:', form.value)
await UserAPI.quickAdd(form.value)
ElMessage.success(form.value.id ? '编辑成功' : '添加成功')
visible.value = false
loading.value = false
emit('success')
}
} catch (error) {
console.log(error);
} finally {
loading.value = false
}
})
}
function open(obj) {
visible.value = true
if (obj && obj.id) {
obj.children.forEach(item => {
item._id = generateId()
item.children.forEach(val => {
val._id = generateId()
})
})
form.value = _.cloneDeep(obj)
console.log(form.value);
}
}
defineExpose({
open
})
let parentSortable = null
const childSortableMap = new Map()
onMounted(() => {
nextTick(() => {
initParentSort()
initAllChildSort()
})
})
watch(() => form.value.children, () => {
nextTick(() => {
initParentSort()
initAllChildSort()
})
}, { deep: true })
function initParentSort() {
const el = document.getElementById('parent-sort')
if (!el) return
if (parentSortable) parentSortable.destroy()
parentSortable = Sortable.create(el, {
handle: '.drag-handle', // 仅拖拽手柄生效
animation: 200,
ghostClass: 'sort-ghost', // 仅保留占位提示
forceFallback: true,
fallbackOnBody: false, // 禁用body上的克隆预览
fallbackClone: false, // 禁用拖拽克隆元素(核心:移除跟随预览)
// 移除dragClass禁用拖拽预览样式
onEnd(evt) {
const { oldIndex, newIndex } = evt
if (oldIndex === newIndex) return
const moved = form.value.children.splice(oldIndex, 1)[0]
form.value.children.splice(newIndex, 0, moved)
nextTick(initAllChildSort)
}
})
}
function initAllChildSort() {
childSortableMap.forEach(s => s.destroy())
childSortableMap.clear()
form.value.children.forEach(item => {
const id = `child-sort-${item._id}`
const el = document.getElementById(id)
if (!el) return
const s = Sortable.create(el, {
animation: 200,
ghostClass: 'sort-ghost', // 仅保留占位提示
direction: 'horizontal',
forceFallback: true,
fallbackOnBody: false, // 禁用body上的克隆预览
fallbackClone: false, // 禁用拖拽克隆元素(核心)
draggable: '.tag', // 仅tag可拖拽
// 移除dragClass禁用拖拽预览样式
onEnd(evt) {
const { oldIndex, newIndex } = evt
if (oldIndex === newIndex) return
const list = item.children
const moved = list.splice(oldIndex, 1)[0]
list.splice(newIndex, 0, moved)
}
})
childSortableMap.set(id, s)
})
}
</script>
<style scoped lang="scss">
.flex_end {
width: 100%;
display: flex;
justify-content: flex-end;
}
.sortable-parent {
border: 1px solid #ececec;
padding: 14px;
border-radius: 4px;
margin-bottom: 14px;
}
.form_wrap {
display: flex;
gap: 24px;
.form_left {
flex: 1;
height: 60vh;
overflow-y: auto;
}
.form_right {
flex: 1;
}
.preview_wrap {
border: 1px solid #ececec;
border-radius: 4px;
padding: 14px;
.title_wrap {
display: flex;
justify-content: center;
align-items: flex-end;
gap: 10px;
}
.row_wrap {
padding-top: 14px;
.row {
margin-bottom: 16px;
position: relative;
.row_top {
display: flex;
align-items: center;
justify-content: space-between;
cursor: default;
padding-bottom: 10px;
.drag-handle {
cursor: grab;
&:active {
cursor: grabbing;
}
}
}
.row_content {
display: flex;
flex-wrap: wrap;
gap: 10px;
min-height: 36px;
}
.tag {
padding: 4px 20px;
border-radius: 4px;
background: #e6e6e6;
cursor: grab;
user-select: none;
white-space: nowrap;
display: flex;
align-items: center;
&:active {
cursor: grabbing;
}
}
.empty-tag {
background: #f5f5f5;
color: #999;
cursor: not-allowed;
}
}
}
}
}
.center {
width: 100%;
display: flex;
align-items: center;
gap: 10px;
}
// 仅保留占位提示样式(无跟随预览)
:deep(.sort-ghost) {
opacity: 0.4;
background: #f5f5f5 !important;
border: 1px dashed #409eff !important;
}
// 移除所有拖拽预览相关样式
:deep(.parent-dragging),
:deep(.child-dragging) {
display: none !important;
}
.slide-move-active,
.fade-move-active {
transition: all 0.3s ease;
}
.slide-enter-from,
.slide-leave-to {
opacity: 0;
transform: translateX(-20px);
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
transform: scale(0.95);
}
</style>

View File

@@ -0,0 +1,80 @@
<template>
<el-dialog :title="form.id ? '添加单位' : '编辑单位'" width="500px" v-model="visible" @close="closeHandle">
<el-form ref="formRef" :rules="rules" :model="form" label-position="right" label-width="80px">
<el-form-item label="单位名称" prop="name">
<el-input placeholder="请输入单位名称" v-model="form.name"></el-input>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="visible = false"> </el-button>
<el-button type="primary" @click="handleOk" :loading="loading"> </el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import _ from 'lodash'
import { ref } from 'vue'
import UserAPI from "@/api/product/commonUnits";
const visible = ref(false)
const loading = ref(false)
const formRef = ref(null)
const form = ref({
id: '',
name: ''
})
const rules = ref({
name: [
{
required: true,
message: '请输入单位名称',
trigger: 'blur'
}
]
})
const emit = defineEmits(['success'])
function handleOk() {
formRef.value.validate(async (valid) => {
try {
if (valid) {
loading.value = true
if (form.value.id == '') {
await UserAPI.addunit(form.value)
} else {
await UserAPI.update(form.value)
}
setTimeout(() => {
visible.value = false
ElMessage.success(form.value.id ? '添加成功' : '编辑成功')
emit('success')
}, 500);
}
} catch (error) {
console.log(error);
} finally {
setTimeout(() => {
loading.value = false
}, 500);
}
})
}
function closeHandle() {
form.value.name = ''
}
function open(obj) {
visible.value = true
if (obj && obj.id) {
form.value = _.cloneDeep(obj)
}
}
defineExpose({
open
})
</script>

View File

@@ -2,26 +2,15 @@
<div class="app-container">
<!-- 列表 -->
<!-- 搜索 -->
<page-search
ref="searchRef"
:search-config="searchConfig"
@query-click="newHandleQueryClick"
@reset-click="handleResetClick2"
/>
<page-search ref="searchRef" :search-config="searchConfig" @query-click="newHandleQueryClick"
@reset-click="handleResetClick2" />
<!-- 顶部数据 -->
<Statistics :data="gongjiData"></Statistics>
<!-- <Statistics :data="gongjiData"></Statistics> -->
<!-- 列表 -->
<page-content
ref="contentRef"
:content-config="contentConfig"
@add-click="handleAddClick"
@edit-click="handleEditClick"
@export-click="handleExportClick"
@search-click="handleSearchClick"
@toolbar-click="handleToolbarClick"
@operat-click="handleOperatClick"
@filter-change="handleFilterChange"
>
<page-content ref="contentRef" :content-config="contentConfig" @add-click="handleAddClick"
@edit-click="handleEditClick" @export-click="handleExportClick" @search-click="handleSearchClick"
@toolbar-click="handleToolbarClick" @operat-click="handleOperatClick" @filter-change="handleFilterChange"
@selectChange="handleSelectionChange">
<!-- <template #status="scope">
<el-tag :type="scope.row[scope.prop] == 1 ? 'success' : 'info'">
{{ scope.row[scope.prop] == 1 ? "启用" : "禁用" }}
@@ -29,6 +18,12 @@
</template> -->
<template #custom>
<importData :type="3" @close="newHandleQueryClick" />
<el-select :disabled="batchOperationSelectRows.length == 0"
:placeholder="`批量操作${batchOperationSelectRows.length == 0 ? '(请选择商品后操作)' : ''}`" v-model="batchOperationValue"
style="width: 240px;margin-left: 16px;">
<el-option v-for="item in batchOperationList" :key="item.id" :value="item.id" :label="item.lable"
@click="batchOperationChange(item.id)"></el-option>
</el-select>
</template>
<template #type="scope">
{{ typeFilter(scope.row[scope.prop]) }}
@@ -37,22 +32,12 @@
<DictLabel v-model="scope.row[scope.prop]" code="gender" />
</template>
<template #shangjia="scope">
<el-switch
v-model="scope.row[scope.prop]"
:active-value="1"
:inactive-value="0"
@click="handleSwitchChange(scope.row)"
></el-switch>
<el-switch v-model="scope.row[scope.prop]" :active-value="1" :inactive-value="0"
@click="handleSwitchChange(scope.row)"></el-switch>
</template>
<template #isStock="scope">
<el-switch
v-if="!scope.row.productId"
v-model="scope.row[scope.prop]"
:active-value="1"
:inactive-value="0"
:disabled="!scope.row.type"
@click="isStockChange(scope.row)"
></el-switch>
<el-switch v-if="!scope.row.productId" v-model="scope.row[scope.prop]" :active-value="1" :inactive-value="0"
:disabled="!scope.row.type" @click="isStockChange(scope.row)"></el-switch>
</template>
<template #kucunedit="scope">
@@ -65,29 +50,21 @@
</template>
<template #tuikuantuihui="scope">
<el-switch
v-if="!scope.row.productId"
v-model="scope.row[scope.prop]"
:active-value="1"
:inactive-value="0"
@click="handleSwitchhaocai(scope.row)"
></el-switch>
<el-switch v-if="!scope.row.productId" v-model="scope.row[scope.prop]" :active-value="1" :inactive-value="0"
@click="handleSwitchhaocai(scope.row)"></el-switch>
<!-- <el-text>{{ returnLabel(scope.row[scope.prop]) }}</el-text> -->
</template>
<template #sellOut="scope">
<el-switch
v-model="scope.row[scope.prop]"
:active-value="1"
:inactive-value="0"
@click="handleSwitchChangeTwo(scope.row)"
></el-switch>
<el-switch v-model="scope.row[scope.prop]" :active-value="1" :inactive-value="0"
@click="handleSwitchChangeTwo(scope.row)"></el-switch>
</template>
<template #autoSellOut="scope">
<el-switch v-model="scope.row[scope.prop]" :active-value="1" :inactive-value="0"
@click="handleSwitchChangeTwo2($event, scope.row)"></el-switch>
</template>
<template #mobile="scope">
<el-text>{{ scope.row[scope.prop] }}</el-text>
<copy-button
v-if="scope.row[scope.prop]"
:text="scope.row[scope.prop]"
style="margin-left: 2px"
/>
<copy-button v-if="scope.row[scope.prop]" :text="scope.row[scope.prop]" style="margin-left: 2px" />
</template>
<template #consumables="scope">
<template v-if="scope.row.type != null">
@@ -110,11 +87,7 @@
</page-modal>
<!-- 编辑 -->
<page-modal
ref="editModalRef"
:modal-config="editModalConfig"
@submit-click="handleSubmitClick"
>
<page-modal ref="editModalRef" :modal-config="editModalConfig" @submit-click="handleSubmitClick">
<template #gender="scope">
<Dict v-model="scope.formData[scope.prop]" code="gender" v-bind="scope.attrs" />
</template>
@@ -135,7 +108,7 @@
<el-input-number v-model="datas.number" :min="1" label="描述文字"></el-input-number>
</el-form-item>
<el-form-item label="报损照片" label-width="">
<MultiImageUpload v-model="datas.images" />
<MultiImageUpload v-model="datas.imgUrls" />
</el-form-item>
<el-form-item label="备注" label-width="">
<el-input v-model="datas.remark" type="textarea" />
@@ -143,7 +116,7 @@
</el-form>
</MyDialog>
<!-- 耗材绑定 -->
<MyDialog ref="myDialogRefhaocai" @confirm="confirmhaocai" width="50%" title="耗材绑定">
<MyDialog ref="myDialogRefhaocai" @confirm="confirmhaocai" width="60%" title="耗材绑定">
<el-row>
<el-col :span="12">
<el-form-item label="商品名">
@@ -161,31 +134,17 @@
<el-table-column label="序号" type="index" width="60"></el-table-column>
<el-table-column label="耗材名称" prop="consInfoId">
<template v-slot="scope">
<el-select
filterable
v-model="scope.row.consInfoId"
reserve-keyword
placeholder="请输入关键词"
@change="selectionChange($event, scope.row)"
>
<el-option
v-for="item in options"
:key="item.id * 1"
:label="item.conName"
:value="item.id * 1"
></el-option>
<el-select filterable v-model="scope.row.consInfoId" reserve-keyword placeholder="请输入关键词"
@change="selectionChange($event, scope.row)">
<el-option v-for="item in options" :key="item.id * 1" :label="item.conName"
:value="item.id * 1"></el-option>
</el-select>
<!-- <div class="tips" v-if="scope.row.stockNumber">库存{{ scope.row.stockNumber }}</div> -->
</template>
</el-table-column>
<el-table-column label="单位" prop="conUnit">
<template v-slot="scope">
<el-input
v-model="scope.row.conUnit"
readonly
disabled
placeholder="请选择耗材"
></el-input>
<el-input v-model="scope.row.conUnit" readonly disabled placeholder="请选择耗材"></el-input>
<!-- <el-select v-model="scope.row.conUnit" reserve-keyword placeholder="请输入关键词">
<el-option
v-for="item in returnConUnits(scope.row.consInfoId)"
@@ -204,20 +163,13 @@
<el-table-column label="操作" width="100">
<template v-slot="scope">
<div class="table_btn_wrap">
<div
class="btn sub"
v-if="haocaiData.consList.length > 1"
@click="haocaiData.consList.splice(scope.$index, 1)"
>
<div class="btn sub" v-if="haocaiData.consList.length > 1"
@click="haocaiData.consList.splice(scope.$index, 1)">
<el-icon>
<RemoveFilled />
</el-icon>
</div>
<div
class="btn add"
v-if="scope.$index == haocaiData.consList.length - 1"
@click="createItem(scope.row)"
>
<div class="btn add" v-if="scope.$index == haocaiData.consList.length - 1" @click="createItem(scope.row)">
<el-icon>
<CirclePlusFilled />
</el-icon>
@@ -231,12 +183,45 @@
<MyDialog ref="myDialogRefkucun" @confirm="confirmkucun" width="30%" title="库存修改">
<el-input-number v-model="kucundata.stockNumbers" :min="0" />
</MyDialog>
<!-- 报损记录 -->
<MyDialog ref="myDialogRefRecord" :title="dataAll.dataTitle" width="70%" @confirm="myDialogRefRecord.close()">
<!-- 表格 -->
<Table :list="dataAll.tableData"
v-if="dataAll.dataType == 'inSumTotal' || dataAll.dataType == 'winInNum' || dataAll.dataType == 'outSumTotal' || dataAll.dataType == 'lossOutNum'">
</Table>
<TableTwo :list="dataAll.tableData" v-else-if="dataAll.dataType == 'refundInNum'"></TableTwo>
<TableThree :list="dataAll.tableData" v-else-if="dataAll.dataType == 'salesNum'"></TableThree>
<TableFour :list="dataAll.tableData" v-else-if="dataAll.dataType == 'damageNum'"></TableFour>
<!-- 分页 -->
<Paging :pagingConfig="dataAll.pagingConfig" @sizeChange="sizeChange" @currentChange="currentChange"></Paging>
</MyDialog>
<!-- 批量操作更改分类弹窗 -->
<el-dialog title="更改分类" width="400px" v-model="visibleBatchOperation" @close="bateOperationForm.categoryId = ''">
<el-form ref="bateOperationFormRef" :model="bateOperationForm" :rules="bateOperationFormRules"
label-position="right" label-width="120">
<el-form-item label="请选择分类" prop="categoryId">
<el-select v-model="bateOperationForm.categoryId" placeholder="请选择">
<el-option v-for="item in batchOperationCategorys" :key="item.id" :label="item.name"
:value="item.id"></el-option>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="visibleBatchOperation = false"> </el-button>
<el-button type="primary" @click="batchOperationCategoryConfirm" :loading="batchOperationLoading">
</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import importData from "@/components/importData/index.vue";
import UserAPI from "@/api/product/index";
import CategoryAPI from '@/api/product/productclassification'
import { useRouter } from "vue-router";
import type { IObject, IOperatData } from "@/components/CURD/types";
import usePage from "@/components/CURD/usePage";
@@ -249,6 +234,89 @@ import editModalConfig from "./indexconfig/edit";
import searchConfig from "./indexconfig/search";
import MyDialog from "@/components/mycomponents/myDialog.vue";
import Statistics from "./indexconfig/statistics.vue";
import TableFour from './indexconfig/TableFour.vue'
const myDialogRefRecord = ref(null)
const refoundModeOptions = ref([
{ label: "退菜退库存", value: 1 },
{ label: "仅退菜不退库存", value: 2 },
{ label: "每次询问-退菜后弹窗提示,可手动选择", value: 3 },
])
function returnLabel(value: number) {
const option = refoundModeOptions.value.find((option) => option.value == value);
return option ? option.label : "未知";
}
let dataAll = reactive({
dataList: [],
tableData: [],
pagingConfig: {
total: 0, // 总数
pageSize: 10, // 每页数据数量
pageNumber: 1, // 当前页码
},
dataType: "",
dataTitle: ""
});
function sizeChange(val) {
dataAll.pagingConfig.pageSize = val
clickEvent(dataAll.dataType)
}
function currentChange(val) {
dataAll.pagingConfig.pageNumber = val
clickEvent(dataAll.dataType)
}
async function clickEvent(key) {
dataAll.dataType = key
let obj = {}
switch (key) {
case 'inSumTotal':
dataAll.dataTitle = '增加数据统计'
obj = { page: dataAll.pagingConfig.pageNumber, size: dataAll.pagingConfig.pageSize, productId: "", inOutType: "in", inOutItem: "" }
break;
case 'winInNum':
dataAll.dataTitle = '手动增加统计'
obj = { page: dataAll.pagingConfig.pageNumber, size: dataAll.pagingConfig.pageSize, productId: "", inOutType: "in", inOutItem: "win-in" }
break;
case 'refundInNum':
dataAll.dataTitle = '退货统计'
obj = { page: dataAll.pagingConfig.pageNumber, size: dataAll.pagingConfig.pageSize, productId: "", inOutType: "in", inOutItem: "order-in" }
break;
case 'outSumTotal':
dataAll.dataTitle = '减少数量统计'
obj = { page: dataAll.pagingConfig.pageNumber, size: dataAll.pagingConfig.pageSize, productId: "", inOutType: "out", inOutItem: "" }
break;
case 'lossOutNum':
dataAll.dataTitle = '手动减少统计'
obj = { page: dataAll.pagingConfig.pageNumber, size: dataAll.pagingConfig.pageSize, productId: "", inOutType: "out", inOutItem: "loss-out" }
break;
case 'salesNum':
dataAll.dataTitle = '销量统计'
obj = { page: dataAll.pagingConfig.pageNumber, size: dataAll.pagingConfig.pageSize, productId: "", inOutType: "out", inOutItem: "order-out" }
break;
case 'damageNum':
dataAll.dataTitle = '报损统计'
obj = { page: dataAll.pagingConfig.pageNumber, size: dataAll.pagingConfig.pageSize, productId: datas.productId, inOutType: "out", inOutItem: "damage-out" }
break;
}
dataAll.tableData = []
let res = await UserAPI.stockFlow(obj);
dataAll.tableData = res.records
dataAll.tableData.forEach((item, index) => {
if (item.imgUrls.length > 7) {
item.imagesone = JSON.parse(item.imgUrls)[0]
}
})
dataAll.pagingConfig.total = res.totalRow
dataAll.pagingConfig.pageSize = res.pageSize
dataAll.pagingConfig.pageNumber = res.pageNumber
myDialogRefRecord.value.open()
}
const importDataRef = ref(null);
@@ -295,7 +363,7 @@ const form = reactive({
let datas = reactive({
number: 0,
remark: "",
images: [],
imgUrls: [],
productId: null, // Added productId property
});
@@ -434,6 +502,16 @@ function handleSwitchChangeTwo(data: any) {
UserAPI.markIsSoldOut(obj);
ElMessage.success("修改成功!");
}
// 自动售罄
function handleSwitchChangeTwo2(e: any, data: any) {
UserAPI.markIsAutoSoldOut({
id: data.id,
isAutoSoldStock: data.isAutoSoldStock
});
ElMessage.success("修改成功!");
}
function handleSwitchChangethree(data: any) {
let obj = {
type: data.type == "sku" ? "sku" : "product",
@@ -510,10 +588,22 @@ async function confirmhaocai() {
consList: haocaiData.value.consList,
id: haocaiData.value.id,
};
let res = await UserAPI.bind(obj);
ElMessage.success("成功");
handleResetClick();
myDialogRefhaocai.value.close();
let flag = true
obj.consList.forEach((el: any) => {
if (el.consInfoId == '') {
flag = false
}
});
if (flag) {
let res = await UserAPI.bind(obj);
ElMessage.success("成功");
handleResetClick();
myDialogRefhaocai.value.close();
} else {
ElMessage.error("请选择耗材");
}
}
// 商品规格
function typeFilter(item: any) {
@@ -531,17 +621,166 @@ function typeFilter(item: any) {
}
// 其他操作列
async function handleOperatClick(data: IOperatData) {
console.log(data);
datas.productId = data.row.id;
myDialogRefbaosun.value.open();
datas.number = 1;
datas.remark = "";
switch (data.name) {
case 'cons':
myDialogRefbaosun.value.open();
datas.number = 1;
datas.remark = "";
break;
case 'consRecord':
clickEvent('damageNum')
// myDialogRefRecord.value.open()
break;
default:
break;
}
}
async function gethaocaiList() {
let res = await UserAPI.productconsList({ id: route.query.id });
options.value = res || [];
}
const batchOperationList = ref([
{
id: 1,
value: '1',
type: 'isSale',
lable: '批量上架'
},
{
id: 2,
value: '0',
type: 'isSale',
lable: '批量下架'
},
{
id: 3,
type: 'category',
lable: '批量更改商品分类'
},
{
id: 4,
value: '1',
type: 'is_sold_stock',
lable: '批量售罄'
},
{
id: 5,
value: '0',
type: 'is_sold_stock',
lable: '批量取消售罄'
},
{
id: 6,
value: '1',
type: 'isAutoSoldStock',
lable: '批量自动售罄'
},
{
id: 7,
value: '0',
type: 'isAutoSoldStock',
lable: '批量取消自动售罄'
},
])
function handleSelectionChange(rows) {
// console.log('handleSelectionChange', rows);
batchOperationSelectRows.value = rows
}
// 选择批量操作项目
const batchOperationValue = ref('')
const batchOperationSelectItem = ref('')
const batchOperationSelectRows = ref([])
function batchOperationChange(e: any) {
if (batchOperationSelectRows.value.length == 0) {
ElMessage.error('请选择商品')
return
}
batchOperationSelectItem.value = batchOperationList.value.find(item => item.id == e)
if (batchOperationSelectItem.value.type == 'category') {
visibleBatchOperation.value = true
} else {
ElMessageBox.confirm(`是否确认对勾选商品执行「${batchOperationSelectItem.value.lable}」操作,操作不可撤销`, '提示').then(() => {
batchOperationConfirm()
}).catch(() => { })
}
}
// 确认最后的操作
async function batchOperationConfirm() {
try {
const ids = batchOperationSelectRows.value.map(item => item.id)
let value = ''
if (batchOperationSelectItem.value.type == 'category') {
value = bateOperationForm.value.categoryId
} else {
value = batchOperationSelectItem.value.value
}
await UserAPI.batchOperate({
ids: ids.join(','), // 商品id集合
type: batchOperationSelectItem.value.type, // 操作类型
value: value, // 操作之除分类外取value
})
newHandleQueryClick()
ElMessage.success('操作成功')
} catch (error) {
console.log(error);
}
}
const batchOperationCategorys = ref([])
const visibleBatchOperation = ref(false)
const batchOperationLoading = ref(false)
const bateOperationFormRef = ref(null)
const bateOperationForm = ref({
categoryId: ''
})
const bateOperationFormRules = ref({
categoryId: [
{
required: true,
message: '请选择分类',
blur: 'trigger'
}
]
})
// 批量操作更改分类
function batchOperationCategoryConfirm() {
bateOperationFormRef.value.validate(async vaild => {
if (vaild) {
batchOperationLoading.value = true
await batchOperationConfirm()
batchOperationLoading.value = false
visibleBatchOperation.value = false
}
})
}
// 获取商品分类
async function getCategorys() {
try {
batchOperationCategorys.value = await CategoryAPI.getList()
} catch (error) {
console.log(error);
}
}
onMounted(() => {
getCategorys()
})
</script>
<style>
/* 关键:隐藏子行的选择框 */
.el-table__row--level-1 .el-checkbox {
display: none !important;
}
</style>
<style scoped lang="scss">
.table_btn_wrap {
display: flex;

View File

@@ -2,8 +2,8 @@
<div style="margin-top: 10px;">
<el-table :data="props.list" border style="">
<el-table-column prop="productName" align="center" label="商品名称/规格名称" />
<el-table-column prop="beforeNumber" align="center" label="原库存" />
<el-table-column prop="afterNumber" align="center" label="变动后库存" />
<!-- <el-table-column prop="beforeNumber" align="center" label="原库存" />
<el-table-column prop="afterNumber" align="center" label="变动后库存" /> -->
<el-table-column prop="inOutNumber" align="center" label="报损数量" />
<el-table-column prop="createUserName" align="center" label="报损人" />
<el-table-column prop="remark" align="center" label="备注" />

View File

@@ -1,185 +1,201 @@
<template>
<div class="addgoods">
<el-form ref="ruleFormRef" :model="ruleForm" :rules="rules" label-width="150px" class="demo-ruleForm" status-icon>
<el-form-item label="商品名称" required>
<el-col :span="12">
<el-form-item prop="name">
<el-input v-model="ruleForm.name" placeholder="请输入商品名称" :disabled="isSyncStatus()" />
</el-form-item>
</el-col>
</el-form-item>
<el-form-item label="商品介绍">
<el-col :span="12">
<el-form-item>
<el-input v-model="ruleForm.shortTitle" type="textarea" placeholder="请输入商品介绍" :disabled="isSyncStatus()" />
</el-form-item>
</el-col>
</el-form-item>
<el-form-item label="单位" required>
<el-col :span="12">
<el-form-item prop="unitId">
<el-select v-model="ruleForm.unitId" placeholder="请选择单位" :disabled="isSyncStatus()">
<el-option :label="item.name" :value="item.id" v-for="item in datas.Company" :key="item.id" />
</el-select>
</el-form-item>
</el-col>
</el-form-item>
<el-form-item label="商品分类" required>
<el-col :span="12">
<el-form-item prop="region">
<el-select v-model="ruleForm.categoryId" placeholder="请选择商品分类" :disabled="isSyncStatus()">
<el-option :label="item.name" :value="item.id" v-for="item in datas.classification" :key="item.id" />
</el-select>
</el-form-item>
</el-col>
</el-form-item>
<!-- <el-form-item label="商品图片" required prop="images">
<MultiImageUpload v-model="ruleForm.images" />
</el-form-item> -->
<el-form-item label="商品图片" required prop="images">
<div style="display: flex; flex-wrap: wrap">
<div v-for="(item, index) in ruleForm.images" :key="index" style="position: relative" class="showStyle">
<el-icon class="buttonstyle" @click="deleteEvent(item)">
<DeleteFilled />
</el-icon>
<img style="width: 148px; height: 148px; margin-right: 6px" class="imgStyle" :src="item" alt="" />
</div>
<div class="upImgStyle" @click="addimgEvent">+</div>
</div>
</el-form-item>
<el-form-item>
<div class="tips">第一张图为商品封面图图片尺寸为750×750</div>
</el-form-item>
<!-- 选择图片 -->
<AddImg ref="addImg" @successEvent="successEvent"></AddImg>
<el-form-item label="商品类型">
<el-radio-group v-model="ruleForm.type" @change="changeTypeEnum(ruleForm.type)" :disabled="isSyncStatus()">
<el-radio value="single">单规格商品</el-radio>
<el-radio value="sku">多规格商品</el-radio>
<el-radio value="package">套餐商品</el-radio>
<el-radio value="weight">称重商品</el-radio>
<!-- <el-radio label="coupon">团购券</el-radio> -->
</el-radio-group>
</el-form-item>
<el-form-item label="套餐商品" v-if="ruleForm.type == 'package'">
<div style="display: block; width: 100%">
<div class="head-container">
<el-radio-group v-model="ruleForm.groupType" @change="typeChange">
<el-radio-button :label="0">固定套餐</el-radio-button>
<el-radio-button :label="1">可选套餐</el-radio-button>
</el-radio-group>
</div>
<div v-if="ruleForm.groupType == 0">
<el-table border :data="item.goods" v-for="(item, index) in ruleForm.proGroupVo" :key="index">
<el-table-column label="名称" prop="proName"></el-table-column>
<el-table-column label="规格" prop="skuName"></el-table-column>
<el-table-column label="价格" prop="price"></el-table-column>
<el-table-column label="数量" prop="number">
<template v-slot="scope">
<el-input-number v-model="scope.row.number" :min="0" />
</template>
</el-table-column>
<el-table-column width="150">
<template #header>
<el-button type="primary" @click="addgoods(-1)" :disabled="isSyncStatus()">
添加商品
</el-button>
</template>
<template v-slot="scope">
<el-button type="text" :disabled="scope.row.type != 'sku' && isSyncStatus()"
@click="showSelectSkuHandle(scope.row, scope.$index, index)">设置规格</el-button>
<el-button type="text"
@click="ruleForm.proGroupVo[index].goods.splice(scope.$index, 1)">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div v-if="ruleForm.groupType == 1">
<div class="group_wrap" v-for="(item, index) in ruleForm.proGroupVo" :key="index">
<el-form inline :model="item">
<el-form-item label="规格组名">
<el-input v-model="item.title" :disabled="isSyncStatus()" />
</el-form-item>
<el-form-item :label="`本组菜品${item.goods.length}选`">
<el-input v-model="item.number" :disabled="isSyncStatus()" />
<div class="form_wrap">
<el-form ref="ruleFormRef" :model="ruleForm" :rules="rules" label-width="150px" class="demo-ruleForm" status-icon>
<el-steps :active="stepsActiveName" finish-status="success" simple>
<el-step title="基础信息" />
<el-step title="耗材设置/关联推荐商品" />
</el-steps>
<div style="padding-top: 20px;">
<!-- 基础信息 -->
<div v-show="stepsActiveName == 0">
<el-form-item label="商品名称" required>
<el-col :span="12">
<el-form-item prop="name">
<el-input v-model="ruleForm.name" placeholder="请输入商品名称" :disabled="isSyncStatus()" />
</el-form-item>
</el-col>
</el-form-item>
<el-form-item label="商品介绍">
<el-col :span="12">
<el-form-item>
<el-button @click="ruleForm.proGroupVo.splice(index, 1)" :disabled="isSyncStatus()">
删除
</el-button>
<el-input v-model="ruleForm.shortTitle" type="textarea" placeholder="请输入商品介绍"
:disabled="isSyncStatus()" />
</el-form-item>
</el-form>
<div style="margin-top: 20px">
<el-table border :data="item.goods">
<el-table-column label="名称" prop="proName"></el-table-column>
<el-table-column label="规格" prop="skuName"></el-table-column>
<el-table-column label="价格" prop="price"></el-table-column>
<el-table-column label="数量" prop="number">
<template v-slot="scope">
<el-input-number v-model="scope.row.number" :min="0" :disabled="isSyncStatus()" />
</template>
</el-table-column>
<el-table-column width="150">
<template #header>
<el-button type="primary" @click="addgoods(index)" :disabled="isSyncStatus()">
添加商品
</el-button>
</template>
<template v-slot="scope">
<el-button type="text" :disabled="scope.row.type != 'sku' && isSyncStatus()"
@click="showSelectSkuHandle(scope.row, scope.$index, index)">设置规格</el-button>
<el-button type="text" @click="ruleForm.proGroupVo[index].goods.splice(scope.$index, 1)"
:disabled="isSyncStatus()">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-col>
</el-form-item>
<el-form-item label="单位" required prop="unitId">
<el-select v-model="ruleForm.unitId" placeholder="请选择单位" :disabled="isSyncStatus()" style="width: 330px;">
<el-option :label="item.name" :value="item.id" v-for="item in datas.Company" :key="item.id" />
</el-select>
<el-button type="primary" icon="Plus" style="margin-left: 14px;"
@click="addUnitRef.open()">添加单位</el-button>
</el-form-item>
<el-form-item label="商品分类" required prop="categoryId">
<el-select v-model="ruleForm.categoryId" placeholder="请选择商品分类" :disabled="isSyncStatus()"
style="width: 330px;">
<el-option :label="item.name" :value="item.id" v-for="item in datas.classification" :key="item.id" />
</el-select>
<el-button type="primary" icon="Plus" style="margin-left: 14px;"
@click="addCategoryRef.open()">添加分类</el-button>
</el-form-item>
<el-form-item label="商品分组">
<el-select v-model="ruleForm.proGroupIds" multiple placeholder="请选择商品分组" style="width: 330px;">
<el-option :label="item.name" :value="item.id" v-for="item in goodsGroupList" :key="item.id" />
</el-select>
<el-button type="primary" icon="Plus" style="margin-left: 14px;"
@click="addGoodsGroupRef.open()">添加分组</el-button>
</el-form-item>
<!-- <el-form-item label="商品图片" required prop="images">
<MultiImageUpload v-model="ruleForm.images" />
</el-form-item> -->
<el-form-item label="商品图片" required prop="images">
<div style="display: flex; flex-wrap: wrap">
<div v-for="(item, index) in ruleForm.images" :key="index" style="position: relative" class="showStyle">
<el-icon class="buttonstyle" @click="deleteEvent(item)">
<DeleteFilled />
</el-icon>
<img style="width: 148px; height: 148px; margin-right: 6px" class="imgStyle" :src="item" alt="" />
</div>
<div class="upImgStyle" @click="addimgEvent">+</div>
</div>
</div>
<el-button type="primary" @click="addtaocan" :disabled="isSyncStatus()">
添加套餐组
</el-button>
</div>
</div>
</el-form-item>
<el-form-item label="选择规格" v-if="ruleForm.type == 'sku'">
<el-select v-model="ruleForm.specId" placeholder="请选择规格" style="width: 500px" @change="selectSpecHandle"
:disabled="isSyncStatus()">
<el-option :label="item.name" :value="item.id" v-for="item in datas.specificationsconfig"
:key="item.id"></el-option>
</el-select>
</el-form-item>
<el-form-item :label="ele.name" v-if="datas.selectSpeclist.length" v-for="ele in datas.selectSpeclist"
:key="ele.name">
<el-checkbox-group v-model="ele.selectSpecResult" @change="selectSpecResultChange">
<el-checkbox :value="element.name" :label="element.name" v-for="(element, index) in ele.children"
:key="index"></el-checkbox>
</el-checkbox-group>
</el-form-item>
<!-- 规格属性 -->
<!-- 单规格称重 -->
<SpecificationAttribute v-if="ruleForm.type != 'sku'" :info="ruleForm" :list="list"
ref="specificationAttributeRef">
</SpecificationAttribute>
<SpecificationAttribute v-if="ruleForm.type == 'sku' && list.length" :specTableHeaders="datas.specTableHeaders"
:info="ruleForm" :list="list" ref="specificationAttributeRef"></SpecificationAttribute>
<el-form-item label="重量">
<el-col :span="12">
<div style="display: block">
<el-input v-model="ruleForm.weight" placeholder="" :disabled="isSyncStatus()">
<template #append>千克</template>
</el-input>
<!-- <div style="color: #999;">用于快递或配送运费计重</div> -->
</div>
</el-col>
</el-form-item>
<el-form-item label="是否允许临时改价">
<el-radio-group v-model="ruleForm.isAllowTempModifyPrice" :disabled="isSyncStatus()">
<el-radio :value="1">允许</el-radio>
<el-radio :value="0">不允许</el-radio>
</el-radio-group>
</el-form-item>
<!-- <el-form-item label="每日销量上限">
</el-form-item>
<el-form-item>
<div class="tips">第一张图为商品封面图图片尺寸为750×750</div>
</el-form-item>
<!-- 选择图片 -->
<AddImg ref="addImg" @successEvent="successEvent"></AddImg>
<el-form-item label="商品类型">
<el-radio-group v-model="ruleForm.type" @change="changeTypeEnum(ruleForm.type)"
:disabled="isSyncStatus()">
<el-radio value="single">单规格商品</el-radio>
<el-radio value="sku">多规格商品</el-radio>
<el-radio value="package">套餐商品</el-radio>
<el-radio value="weight">称重商品</el-radio>
<!-- <el-radio label="coupon">团购券</el-radio> -->
</el-radio-group>
</el-form-item>
<el-form-item label="套餐商品" v-if="ruleForm.type == 'package'">
<div style="display: block; width: 100%">
<div class="head-container">
<el-radio-group v-model="ruleForm.groupType" @change="typeChange">
<el-radio-button :label="0">固定套餐</el-radio-button>
<el-radio-button :label="1">可选套餐</el-radio-button>
</el-radio-group>
</div>
<div v-if="ruleForm.groupType == 0">
<el-table border :data="item.goods" v-for="(item, index) in ruleForm.proGroupVo" :key="index">
<el-table-column label="名称" prop="proName"></el-table-column>
<el-table-column label="规格" prop="skuName"></el-table-column>
<el-table-column label="价格" prop="price"></el-table-column>
<el-table-column label="数量" prop="number">
<template v-slot="scope">
<el-input-number v-model="scope.row.number" :min="0" />
</template>
</el-table-column>
<el-table-column width="150">
<template #header>
<el-button type="primary" @click="addgoods(-1)" :disabled="isSyncStatus()">
添加商品
</el-button>
</template>
<template v-slot="scope">
<el-button type="text" :disabled="scope.row.type != 'sku' && isSyncStatus()"
@click="showSelectSkuHandle(scope.row, scope.$index, index)">设置规格</el-button>
<el-button type="text"
@click="ruleForm.proGroupVo[index].goods.splice(scope.$index, 1)">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div v-if="ruleForm.groupType == 1">
<div class="group_wrap" v-for="(item, index) in ruleForm.proGroupVo" :key="index">
<el-form inline :model="item">
<el-form-item label="规格组名">
<el-input v-model="item.title" :disabled="isSyncStatus()" />
</el-form-item>
<el-form-item :label="`本组菜品${item.goods.length}选`">
<el-input v-model="item.number" :disabled="isSyncStatus()" />
</el-form-item>
<el-form-item>
<el-button @click="ruleForm.proGroupVo.splice(index, 1)" :disabled="isSyncStatus()">
删除
</el-button>
</el-form-item>
</el-form>
<div style="margin-top: 20px">
<el-table border :data="item.goods">
<el-table-column label="名称" prop="proName"></el-table-column>
<el-table-column label="规格" prop="skuName"></el-table-column>
<el-table-column label="价格" prop="price"></el-table-column>
<el-table-column label="数量" prop="number">
<template v-slot="scope">
<el-input-number v-model="scope.row.number" :min="0" :disabled="isSyncStatus()" />
</template>
</el-table-column>
<el-table-column width="150">
<template #header>
<el-button type="primary" @click="addgoods(index)" :disabled="isSyncStatus()">
添加商品
</el-button>
</template>
<template v-slot="scope">
<el-button type="text" :disabled="scope.row.type != 'sku' && isSyncStatus()"
@click="showSelectSkuHandle(scope.row, scope.$index, index)">设置规格</el-button>
<el-button type="text" @click="ruleForm.proGroupVo[index].goods.splice(scope.$index, 1)"
:disabled="isSyncStatus()">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
</div>
<el-button type="primary" @click="addtaocan" :disabled="isSyncStatus()">
添加套餐组
</el-button>
</div>
</div>
</el-form-item>
<el-form-item label="选择规格" v-if="ruleForm.type == 'sku'">
<div class="center">
<el-select v-model="ruleForm.specId" placeholder="请选择规格" style="width: 500px" @change="selectSpecHandle"
:disabled="isSyncStatus()">
<el-option :label="item.name" :value="item.id" v-for="item in datas.specificationsconfig"
:key="item.id"></el-option>
</el-select>
<el-button type="primary" icon="Plus" style="margin-left: 14px;"
@click="addSkuModalRef?.open()">添加规格</el-button>
</div>
</el-form-item>
<el-form-item :label="ele.name" v-if="datas.selectSpeclist.length" v-for="ele in datas.selectSpeclist"
:key="ele.name">
<el-checkbox-group v-model="ele.selectSpecResult" @change="selectSpecResultChange">
<el-checkbox :value="element.name" :label="element.name" v-for="(element, index) in ele.children"
:key="index"></el-checkbox>
</el-checkbox-group>
</el-form-item>
<!-- 规格属性 -->
<!-- 单规格称重 -->
<SpecificationAttribute v-if="ruleForm.type != 'sku'" :info="ruleForm" :list="list"
ref="specificationAttributeRef">
</SpecificationAttribute>
<SpecificationAttribute v-if="ruleForm.type == 'sku' && list.length"
:specTableHeaders="datas.specTableHeaders" :info="ruleForm" :list="list" ref="specificationAttributeRef">
</SpecificationAttribute>
<el-form-item label="重量" v-if="ruleForm.type == 'weight'">
<el-col :span="12">
<div style="display: block">
<el-input v-model="ruleForm.weight" placeholder="纯文本描述"></el-input>
<!-- <div style="color: #999;">用于快递或配送运费计重</div> -->
</div>
</el-col>
</el-form-item>
<el-form-item label="是否允许临时改价">
<el-radio-group v-model="ruleForm.isAllowTempModifyPrice" :disabled="isSyncStatus()">
<el-radio :value="1">允许</el-radio>
<el-radio :value="0">不允许</el-radio>
</el-radio-group>
</el-form-item>
<!-- <el-form-item label="每日销量上限">
<el-input-number v-model="ruleForm.dayLimit" :min="0" />
</el-form-item>
<el-form-item label="每单限购">
@@ -188,90 +204,182 @@
<el-form-item label="每人限购">
<el-input-number v-model="ruleForm.dayLimit" :min="0" />
</el-form-item> -->
<el-form-item label="定时上下架">
<el-checkbox-group v-model="ruleForm.days">
<el-checkbox v-for="(item, index) in datas.cycle" :key="item.value" :value="item.value">
{{ item.label }}
</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item>
<el-col :span="20">
<!-- <el-time-picker v-model="ruleForm.useTime" is-range range-separator="至" start-placeholder="开始时间"
<el-form-item label="售卖时间">
<el-checkbox-group v-model="ruleForm.days">
<el-checkbox v-for="(item, index) in datas.cycle" :key="item.value" :value="item.value">
{{ item.label }}
</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item>
<el-col :span="20">
<!-- <el-time-picker v-model="ruleForm.useTime" is-range range-separator="至" start-placeholder="开始时间"
end-placeholder="结束时间" value-format="HH:mm:ss" format="HH:mm:ss" />
-->
<el-time-picker v-model="ruleForm.startTime" value-format="HH:mm:ss" format="HH:mm:ss" placeholder="选择开始时间">
</el-time-picker>-
<el-time-picker v-model="ruleForm.endTime" value-format="HH:mm:ss" format="HH:mm:ss" placeholder="选择结束时间">
</el-time-picker>
</el-col>
</el-form-item>
<el-form-item label="上架">
<el-switch v-model="ruleForm.isSale" :active-value="1" :inactive-value="0" />
</el-form-item>
<el-form-item label="库存开关">
<div style="display: block;">
<el-switch v-model="ruleForm.isStock" :active-value="1" :inactive-value="0" :disabled="isSyncStatus()" />
<div style="color: #999;">关闭则不计算出库数据</div>
</div>
</el-form-item>
<el-form-item label="设为推荐" prop="delivery">
<el-switch v-model="ruleForm.isHot" :active-value="1" :inactive-value="0" />
</el-form-item>
<el-form-item label="库存数量">
<el-input-number v-model="ruleForm.stockNumber" :min="0" />
</el-form-item>
<el-form-item label="打包费" prop="delivery">
<div style="display: block;">
<el-input-number v-model="ruleForm.packFee" controls-position="right"
:disabled="isSyncStatus()"></el-input-number>
<div style="color: #999;">单份商品打包费店铺开启外卖模式下该数据才生效</div>
</div>
</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>
<el-time-picker v-model="ruleForm.startTime" value-format="HH:mm:ss" format="HH:mm:ss"
placeholder="选择开始时间">
</el-time-picker> <el-text type="info">-</el-text> <el-time-picker v-model="ruleForm.endTime"
value-format="HH:mm:ss" format="HH:mm:ss" placeholder="选择结束时间">
</el-time-picker>
</el-col>
</el-form-item>
<el-form-item label="上架">
<el-switch v-model="ruleForm.isSale" :active-value="1" :inactive-value="0" />
</el-form-item>
<!-- <el-form-item label="库存开关">
<div style="display: block;">
<el-switch v-model="ruleForm.isStock" :active-value="1" :inactive-value="0" :disabled="isSyncStatus()" />
<div style="color: #999;">关闭则不计算出库数据</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>
</el-form-item> -->
<el-form-item label="设为推荐" prop="delivery">
<div class="center">
<el-switch v-model="ruleForm.isHot" :active-value="1" :inactive-value="0" />
<el-text>推荐后商品会展示在用户小程序端热销模块内</el-text>
</div>
</el-form-item>
<!-- <el-form-item label="库存数量">
<el-input-number v-model="ruleForm.stockNumber" :min="0" />
</el-form-item> -->
<el-form-item label="打包费" prop="delivery">
<div style="display: block;">
<el-input-number v-model="ruleForm.packFee" controls-position="right"
:disabled="isSyncStatus()"></el-input-number>
<div style="color: #999;">单份商品打包费店铺开启外卖模式下该数据才生效</div>
</div>
</el-form-item>
</div>
<!-- 耗材设置 -->
<div v-show="stepsActiveName == 1">
<el-tabs v-model="tabsActiveName">
<el-tab-pane label="耗材设置" :name="1">
<el-form-item label="耗材列表">
<el-table :data="ruleForm.consList" border>
<el-table-column label="序号" type="index" width="60"></el-table-column>
<el-table-column label="耗材名称" prop="consInfoId">
<template v-slot="scope">
<el-select filterable v-model="scope.row.consInfoId" reserve-keyword placeholder="请输入关键词"
@change="selectionChange($event, scope.row)">
<el-option v-for="item in consOptions" :key="item.id" :label="item.conName"
:value="item.id"></el-option>
</el-select>
<!-- <div class="tips" v-if="scope.row.stockNumber">库存{{ scope.row.stockNumber }}</div> -->
</template>
</el-table-column>
<el-table-column label="单位" prop="conUnit">
<template v-slot="scope">
<el-input v-model="scope.row.conUnit" readonly disabled placeholder="请选择耗材"></el-input>
<!-- <el-select v-model="scope.row.conUnit" reserve-keyword placeholder="请输入关键词">
<el-option
v-for="item in returnConUnits(scope.row.consInfoId)"
:key="item"
:label="item"
:value="item"
></el-option>
</el-select> -->
</template>
</el-table-column>
<el-table-column label="使用数量" prop="surplusStock" min-width="150px">
<template v-slot="scope">
<el-input-number v-model="scope.row.surplusStock" :min="0" />
</template>
</el-table-column>
<el-table-column label="操作" width="100">
<template #header>
<el-button type="primary" size="small" @click="createItem">添加</el-button>
</template>
<template v-slot="scope">
<div class="table_btn_wrap">
<div class="btn sub" @click="ruleForm.consList.splice(scope.$index, 1)">
<el-icon>
<RemoveFilled />
</el-icon>
</div>
<!-- <div class="btn add" v-if="scope.$index == ruleForm.consList.length - 1"
@click="createItem(scope.row)">
<el-icon>
<CirclePlusFilled />
</el-icon>
</div> -->
</div>
</template>
</el-table-column>
</el-table>
</el-form-item>
<el-form-item label="自动售罄">
<div class="center">
<el-switch v-model="ruleForm.isAutoSoldStock" :active-value="1" :inactive-value="0"></el-switch>
<el-text type="info">当绑定的任何一种耗材库存不足时商品自动售罄</el-text>
</div>
</el-form-item>
<el-form-item label="退菜是否退库存">
<div class="column">
<div>
<el-radio-group v-model="ruleForm.refundMode">
<el-radio label="退菜退库存" :value="1"></el-radio>
<el-radio label="仅退菜不退库存" :value="2"></el-radio>
<el-radio label="每次询问-退菜后弹窗提示,可手动选择" :value="3"></el-radio>
</el-radio-group>
</div>
<div>
<el-text type="danger">当前店铺退菜退库存规则{{ shopInfoTypeFilter(shopInfo.refundMode) }}</el-text>
</div>
</div>
</el-form-item>
</el-tab-pane>
<el-tab-pane label="关联商品设置" :name="2">
<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 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-tab-pane>
</el-tabs>
</div>
</div>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm(ruleFormRef)">确定</el-button>
<el-button @click="resetForm(ruleFormRef)">取消</el-button>
</el-form-item>
</el-form>
<el-form-item>
<el-button type="primary" @click="nextStep" v-if="stepsActiveName == 0">下一步</el-button>
<el-button type="primary" @click="submitForm(ruleFormRef)" v-if="stepsActiveName == 1">保存</el-button>
<el-button @click="resetForm(ruleFormRef)" v-if="stepsActiveName == 0">取消</el-button>
<el-button @click="stepsActiveName = 0" v-if="stepsActiveName != 0">上一步</el-button>
</el-form-item>
</el-form>
</div>
<!-- 选择商品 -->
<shopList ref="shopListRef" @success="selectShopRes" />
<!-- <shopList ref="shopListRef" @success="selectShopRes" /> -->
<GoodsSelect ref="shopListRef" @success="selectShopRes" />
<el-dialog :title="datas.selectSkuItem.proName" v-model="datas.showSelectSku">
<div class="pro_sku">
<div class="item">
@@ -293,6 +401,14 @@
</el-dialog>
<!-- 选择商品弹窗 -->
<selecProductDialog ref="selecProductDialogRef" @success="selecProductSuccess" />
<!-- 添加单位 -->
<addUnit ref="addUnitRef" @success="getList" />
<!-- 添加商品分类 -->
<addCategory ref="addCategoryRef" @success="getList" />
<!-- 添加商品分组 -->
<addGoodsGroup ref="addGoodsGroupRef" @success="getGoodsGroupList" />
<!-- 添加规格 -->
<addSkuModal ref="addSkuModalRef" @success="tbProductSpecGet" />
</div>
</template>
@@ -306,17 +422,108 @@ import UserAPI from "@/api/product/productclassification";
import UserAPI2 from "@/api/product/commonUnits";
import UserAPI3 from "@/api/product/index";
import UserAPI4 from "@/api/product/specificationsconfig";
import GoodsGroupAPI from "@/api/onlineShop/goodsGroupconfig";
import shopList from "@/components/mycomponents/shopList.vue";
import AddImg from "@/components/mycomponents/addImg.vue";
import { useRouter } from "vue-router";
import Sortable from "sortablejs";
import { useTagsViewStore } from "@/store";
import selecProductDialog from "@/views/marketing_center/group_booking/components/selecProductDialog.vue";
import addUnit from "../components/addUnit.vue";
import addCategory from "../components/addCategory.vue";
import addGoodsGroup from "@/views/online-shop/components/addGoodsGroup.vue";
import GoodsSelect from '@/components/GoodsSelect/index.vue'
import addSkuModal from "../components/addSkuModal.vue";
const route = useRoute();
const selecProductDialogRef = ref(null)
const goodsListMax = ref(9)
const addUnitRef = ref(null)
const consOptions = ref([])
const addCategoryRef = ref(null)
const addGoodsGroupRef = ref(null)
const addSkuModalRef = ref(null)
// let haocaiData = ref({
// consList: [
// {
// productId: '',
// consInfoId: "",
// surplusStock: "0",
// }
// ]
// });
const stepsActiveName = ref(0);
const tabsActiveName = ref(1);
const stepRules = [
'name',
'unitId',
'categoryId',
'images',
]
const goodsGroupList = ref([])
async function getGoodsGroupList() {
try {
goodsGroupList.value = await GoodsGroupAPI.getList()
} catch (error) {
console.log(error);
}
}
onMounted(() => {
getGoodsGroupList()
})
// 校验基础信息的表单是否通过
async function nextStep() {
try {
await ruleFormRef.value.validateField(stepRules)
stepsActiveName.value++
} catch (error) {
console.log(error);
}
}
// 生成新商品绑定耗材关系项
function createItem(val) {
let item = {
productId: '',
consInfoId: "",
surplusStock: "",
};
ruleForm.consList.push(item);
}
// 商品选择耗材
function selectionChange(e, row) {
let item = consOptions.value.find((item) => item.id == e);
row.name = item.conName;
row.conUnit = item.conUnit;
}
// 获取耗材列表
async function gethaocaiList() {
try {
let res = await UserAPI3.productconsList({ id: route.query.id });
console.log('获取耗材列表', res);
consOptions.value = res || [];
} catch (error) {
console.log(error);
}
}
const shopInfo = ref('')
function shopInfoTypeFilter(t) {
if (t == 1) return '跟随商品分类'
if (t == 2) return '跟随单商品'
}
onMounted(() => {
shopInfo.value = JSON.parse(localStorage.getItem('userInfo'))
gethaocaiList()
// Sortable 需要在 el-table 渲染 tbody 后初始化,尝试多次以确保 DOM 可用
const initSortable = async () => {
await nextTick();
@@ -475,6 +682,7 @@ const ruleForm = reactive<RuleForm>({
unitId: "",
// 分类id
categoryId: "",
proGroupIds: [], // 商品分组集合
// 封面图url
coverImg: "",
// 详情图urls
@@ -497,16 +705,16 @@ const ruleForm = reactive<RuleForm>({
// sku集合
skuList: [],
// 重量
weight: 0,
weight: '',
// 是否允许改价
isAllowTempModifyPrice: 1,
// 定时上下架周期
days: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
useTime: ["00:00:00", "23:59:59"],
// 开始时间
startTime: "",
startTime: "00:00:00",
// 结束时间
endTime: "",
endTime: "23:59:59",
// 是否上架
isSale: "1",
// 是否开启库存
@@ -520,7 +728,10 @@ const ruleForm = reactive<RuleForm>({
// 排序值
sort: 1,
selectSpecInfo: {},
relatedRecommendJson: [] // 关联推荐商品
relatedRecommendJson: [], // 关联推荐商品
consList: [], // 耗材列表
refundMode: 1, // 退菜是否退库存 1退菜退库存 2仅退菜不退库存 3每次询问-退菜后弹窗提示
isAutoSoldStock: 0, // 是否自动售罄
});
const rules = reactive<FormRules<RuleForm>>({
name: [{ required: true, message: "请输入商品名称", trigger: "blur" }],
@@ -584,6 +795,14 @@ async function tbProductGetDetail(id: any) {
} else {
list.value = ruleForm.skuList;
}
// if (res.consList.length == 0) {
// ruleForm.consList.push({
// productId: '',
// consInfoId: "",
// surplusStock: "0",
// })
// }
}
// 选择套餐商品sku
function selectSkuHandle(item: any, index: number) {
@@ -643,11 +862,17 @@ function showSelectSkuHandle(row: any, index: any, tabIndex: any) {
datas.selectSkuItem = obj;
}
// 添加商品
// 显示添加套餐商品弹窗
function addgoods(index: number = -1) {
datas.addGroupIndex = index;
(shopListRef.value as any)?.opens();
if (ruleForm.groupType == 0) {
(shopListRef.value as any)?.show(ruleForm.proGroupVo[0].goods || []);
} else {
(shopListRef.value as any)?.show(ruleForm.proGroupVo[index].goods || []);
}
}
function deleteEvent(d: any) {
if (isSyncStatus()) {
ElMessage.error('当前同步启用状态下不可修改')
@@ -723,7 +948,7 @@ function createSkuHeader() {
// 可选套餐弹窗
function addtaocan() {
datas.addGroupIndex = -1;
(shopListRef.value as any)?.opens();
(shopListRef.value as any)?.show();
}
// 生成多规格表体
function createSkuBody() {
@@ -811,6 +1036,7 @@ function changeTypeEnum(item: string) {
list.value = [datas.defaultSku];
}
}
// 笛卡尔积算法
function cartesian(arr) {
if (arr.length < 2) return arr[0] || [];
@@ -826,6 +1052,7 @@ function cartesian(arr) {
return res;
});
}
// 套餐类型切换
function typeChange() {
// ruleForm.typeEnum = 'normal'
@@ -974,8 +1201,13 @@ const resetForm = (formEl: FormInstance | undefined) => {
</script>
<style scoped lang="scss">
.addgoods {
padding: 20px;
background-color: #fff;
padding: 14px;
.form_wrap {
background-color: #fff;
border-radius: 14px;
padding: 14px;
}
}
.pro_sku {
@@ -1088,4 +1320,8 @@ const resetForm = (formEl: FormInstance | undefined) => {
font-size: 14px;
}
}
.header-wrap {
padding-bottom: 20px;
}
</style>

View File

@@ -13,7 +13,8 @@ const contentConfig: IContentConfig<UserPageQuery> = {
rowKey: 'id',
treeProps: {
children: 'skuList' // 指定子节点存储的字段为 customChildren
}
},
selectOnIndeterminate: false,
},
pagination: {
background: true,
@@ -73,13 +74,13 @@ const contentConfig: IContentConfig<UserPageQuery> = {
name: "sync",
auth: "import",
},
{
icon: "edit",
text: "库存预警",
type: "danger",
name: "custom1",
auth: "import",
},
// {
// icon: "edit",
// text: "库存预警",
// type: "danger",
// name: "custom1",
// auth: "import",
// },
{
icon: "Download",
text: "导出",
@@ -89,24 +90,27 @@ const contentConfig: IContentConfig<UserPageQuery> = {
}
],
cols: [
// { type: "selection", width: 50, align: "center" },
{
type: "selection", width: 50, align: "center", selectable: (row) => !row.barCode,
},
{ label: "封面图", align: "center", prop: "coverImg", templet: "image" },
{ label: "商品名称", align: "center", prop: "name", },
{ label: "分类", align: "center", prop: "categoryName", },
{ label: "售价", align: "center", prop: "lowPrice" },
{
label: "商品规格", align: "center", prop: "type", templet: "custom",
slotName: "type",
},
// { label: "库存", align: "center", prop: "stockNumber" },
{ label: "库存", align: "center", slotName: "kucunedit", templet: "custom", prop: "stockNumber" },
// { label: "库存", align: "center", slotName: "kucunedit", templet: "custom", prop: "stockNumber" },
{ label: "耗材信息", align: "center", prop: "consName", slotName: "consumables", templet: "custom", },
{
label: "库存开关",
align: "center",
prop: "isStock",
templet: "custom",
slotName: "isStock",
},
// {
// label: "库存开关",
// align: "center",
// prop: "isStock",
// templet: "custom",
// slotName: "isStock",
// },
{
label: "上架",
align: "center",
@@ -122,9 +126,16 @@ const contentConfig: IContentConfig<UserPageQuery> = {
slotName: "sellOut",
},
{
label: "退款退回库存",
label: "自动售罄",
align: "center",
prop: "isRefundStock",
prop: "isAutoSoldStock",
templet: "custom",
slotName: "autoSellOut",
},
{
label: "退菜退库存",
align: "center",
prop: "refundMode",
templet: "custom",
slotName: "tuikuantuihui",
},
@@ -134,7 +145,11 @@ const contentConfig: IContentConfig<UserPageQuery> = {
fixed: "right",
width: 280,
templet: "tool",
operat: [{ text: "报损", name: '' }, { text: "编辑", icon: 'edit', name: "edit" }, { text: "删除", icon: 'delete', type: 'danger', name: "delete" }],
operat: [
{ text: "报损", icon: '', name: 'cons' },
{ text: "报损记录", name: 'consRecord' },
{ text: "编辑", icon: 'edit', name: "edit" },
{ text: "删除", icon: 'delete', type: 'danger', name: "delete" }],
},
],
};

View File

@@ -86,7 +86,7 @@ const searchConfig: ISearchConfig = {
label: "排序",
prop: "orderBy",
attrs: {
placeholder: "请选择商品分类",
placeholder: "请选择排序方式",
clearable: true,
style: {
width: "200px",

View File

@@ -29,10 +29,12 @@
<copy-button v-if="scope.row[scope.prop]" :text="scope.row[scope.prop]" style="margin-left: 2px" />
</template>
<template #operates="scope">
<el-button type="text" size="small" v-if="scope.row.level < 3 && !isSyncStatus()"
@click="addlowerLevel(scope.row)">添加下一级</el-button>
<el-button type="text" size="small" v-if="!isSyncStatus()" @click="handleEditClick(scope.row)">编辑</el-button>
<el-button type="text" size="small" v-if="!isSyncStatus()" @click="deleteClick(scope.row)">删除</el-button>
<!-- <el-button type="text" size="small" v-if="scope.row.level < 3 && !isSyncStatus()"
@click="addlowerLevel(scope.row)">添加下一级</el-button> -->
<el-button type="primary" link size="small" v-if="!isSyncStatus() && scope.row.pid == 0"
@click="handleEditClick(scope.row)">编辑</el-button>
<el-button type="danger" link size="small" v-if="!isSyncStatus()"
@click="deleteClick(scope.row)">删除</el-button>
</template>
</page-content>
<!-- 添加下一级-编辑 -->
@@ -110,6 +112,7 @@
</page-content>
</template>
</div>
<addSkuModal ref="addSkuModalRef" @success="handleResetClick" />
</template>
<script setup lang="ts">
@@ -124,6 +127,9 @@ import editModalConfig from "./specificationsconfig/edit";
import searchConfig from "./specificationsconfig/search";
import { pid } from "process";
import { isSyncStatus } from "@/utils/index";
import addSkuModal from "./components/addSkuModal.vue";
const addSkuModalRef = ref(null)
const validateSku1 = (rule, value, callback) => {
if (!datas.skuForm.label) {
@@ -300,36 +306,38 @@ function deleteClick(item: any) {
}
// 新增
async function handleAddClick() {
myDialogRef.value?.open();
addModalRef.value?.setModalVisible();
addSkuModalRef.value?.open()
// myDialogRef.value?.open();
// addModalRef.value?.setModalVisible();
// 加载上级规格下拉数据源
addModalConfig.formItems[2]!.attrs!.data = await UserAPI.getPage({ name: "" });
// addModalConfig.formItems[2]!.attrs!.data = await UserAPI.getPage({ name: "" });
// 加载角色下拉数据源
// addModalConfig.formItems[4]!.options = await RoleAPI.getOptions();
}
// 编辑
async function handleEditClick(row: IObject) {
editModalRef.value?.handleDisabled(false);
// 加载部门下拉数据源
editModalConfig.formItems[2]!.attrs!.data = [{
createTime: "",
fullName: "顶级",
id: "0", level: 1,
name: "顶级",
pid: "0",
pids: "0,",
shopId: "1",
sort: 1,
status: 1,
updateTime: "",
children: await UserAPI.getPage({ name: "" })
}]
addSkuModalRef.value?.open(row)
// editModalRef.value?.handleDisabled(false);
// // 加载部门下拉数据源
// editModalConfig.formItems[2]!.attrs!.data = [{
// createTime: "",
// fullName: "顶级",
// id: "0", level: 1,
// name: "顶级",
// pid: "0",
// pids: "0,",
// shopId: "1",
// sort: 1,
// status: 1,
// updateTime: "",
// children: await UserAPI.getPage({ name: "" })
// }]
// 加载角色下拉数据源
// editModalConfig.formItems[4]!.options = await RoleAPI.getOptions();
// 根据id获取数据进行填充
const data = await UserAPI.getunitinfo(row.id);
editModalRef.value?.setFormData(data);
editModalRef.value?.setModalVisible();
// const data = await UserAPI.getunitinfo(row.id);
// editModalRef.value?.setFormData(data);
// editModalRef.value?.setModalVisible();
}
// 其他工具栏

View File

@@ -62,9 +62,10 @@ const contentConfig: IContentConfig<UserPageQuery> = {
// },
{
label: "操作",
width: '150',
slotName: "operates",
templet: "custom",
align: "center",
align: "left",
},
],
};

View File

@@ -23,20 +23,8 @@
<copy-button v-if="scope.row[scope.prop]" :text="scope.row[scope.prop]" style="margin-left: 2px" />
</template>
</page-content>
<!-- 新增 -->
<page-modal ref="addModalRef" :modal-config="addModalConfig" @submit-click="handleSubmitClick">
<template #gender="scope">
<Dict v-model="scope.formData[scope.prop]" code="gender" />
</template>
</page-modal>
<!-- 编辑 -->
<page-modal ref="editModalRef" :modal-config="editModalConfig" @submit-click="handleSubmitClick">
<template #gender="scope">
<Dict v-model="scope.formData[scope.prop]" code="gender" v-bind="scope.attrs" />
</template>
</page-modal>
<!-- 添加/编辑单位 -->
<addUnit ref="addUnitRef" @success="handleQueryClick" />
</template>
<template v-else>
<page-content ref="contentRef" :content-config="contentConfig2" @operat-click="handleOperatClick">
@@ -62,6 +50,9 @@ import contentConfig2 from "./unitconfig/content2";
import editModalConfig from "./unitconfig/edit";
import searchConfig from "./unitconfig/search";
import { isSyncStatus, downloadFile } from "@/utils/index";
import addUnit from "./components/addUnit.vue";
const addUnitRef = ref(null)
const {
searchRef,
@@ -101,23 +92,25 @@ async function handleExportClick() {
// 新增
async function handleAddClick() {
addModalRef.value?.setModalVisible();
// 加载部门下拉数据源
addModalConfig.formItems[2]!.attrs!.data = await DeptAPI.getOptions();
// 加载角色下拉数据源
addModalConfig.formItems[4]!.options = await RoleAPI.getOptions();
addUnitRef.value.open()
// addModalRef.value?.setModalVisible();
// // 加载部门下拉数据源
// addModalConfig.formItems[2]!.attrs!.data = await DeptAPI.getOptions();
// // 加载角色下拉数据源
// addModalConfig.formItems[4]!.options = await RoleAPI.getOptions();
}
// 编辑
async function handleEditClick(row: IObject) {
editModalRef.value?.handleDisabled(false);
// 加载部门下拉数据源
// editModalConfig.formItems[2]!.attrs!.data = await DeptAPI.getOptions();
// 加载角色下拉数据源
// editModalConfig.formItems[4]!.options = await RoleAPI.getOptions();
// 根据id获取数据进行填充
const data = await UserAPI.getunitinfo(row.id);
editModalRef.value?.setFormData(data);
editModalRef.value?.setModalVisible();
addUnitRef.value.open(row)
// editModalRef.value?.handleDisabled(false);
// // 加载部门下拉数据源
// // editModalConfig.formItems[2]!.attrs!.data = await DeptAPI.getOptions();
// // 加载角色下拉数据源
// // editModalConfig.formItems[4]!.options = await RoleAPI.getOptions();
// // 根据id获取数据进行填充
// const data = await UserAPI.getunitinfo(row.id);
// editModalRef.value?.setFormData(data);
// editModalRef.value?.setModalVisible();
}
// 其他工具栏

View File

@@ -7,7 +7,7 @@
<el-input v-model="state.query.name" clearable placeholder="请输入分店名称" style="width: 100%" class="filter-item"
@keyup.enter="getTableData" />
</el-col>
<el-col :span="16">
<el-button type="primary" @click="getTableData">查询</el-button>
@@ -22,35 +22,43 @@
</el-row>
</el-card>
</div>
<div style="padding-bottom: 14px;">
<el-alert title="分店需由工作人员协助开通" type="warning" :closable="false" show-icon />
</div>
<div class="head-container">
<el-card shadow="never">
<el-table v-loading="state.tableData.loading" :data="state.tableData.list">
<el-table-column prop="id" label="ID" width="80" />
<el-table-column label="店铺信息">
<template v-slot="scope">
<div>{{ scope.row.shopName }}{{ scope.row.shopType == 'chain' ? '(连锁店)' : scope.row.shopType == 'join' ? '(加盟店)' : '' }}</div>
<div>{{ scope.row.shopName }}{{ scope.row.shopType == 'chain' ? '(连锁店)' : scope.row.shopType == 'join' ?
'(加盟店)' : '' }}</div>
<div>账号{{ scope.row.account }}</div>
<div>联系电话{{ scope.row.phone }}</div>
</template>
</el-table-column>
<el-table-column prop="isEnableProdSync" label="商品同步" width="120">
<template v-slot="scope">
<el-tag :type="scope.row.isEnableProdSync == 1 ? 'success' : 'error'" effect="dark"> {{ scope.row.isEnableProdSync == 1 ? '启用' : '禁用' }} </el-tag>
<el-tag :type="scope.row.isEnableProdSync == 1 ? 'success' : 'error'" effect="dark"> {{
scope.row.isEnableProdSync == 1 ? '启用' : '禁用' }} </el-tag>
</template>
</el-table-column>
<el-table-column prop="isEnableVipSync" label="会员同步" width="120">
<template v-slot="scope">
<el-tag :type="scope.row.isEnableVipSync == 1 ? 'success' : 'error'" effect="dark"> {{ scope.row.isEnableVipSync == 1 ? '启用' : '禁用' }} </el-tag>
<el-tag :type="scope.row.isEnableVipSync == 1 ? 'success' : 'error'" effect="dark"> {{
scope.row.isEnableVipSync == 1 ? '启用' : '禁用' }} </el-tag>
</template>
</el-table-column>
<el-table-column prop="isEnableConsSync" label="耗材同步" width="120">
<template v-slot="scope">
<el-tag :type="scope.row.isEnableConsSync == 1 ? 'success' : 'error'" effect="dark"> {{ scope.row.isEnableConsSync == 1 ? '启用' : '禁用' }} </el-tag>
<el-tag :type="scope.row.isEnableConsSync == 1 ? 'success' : 'error'" effect="dark"> {{
scope.row.isEnableConsSync == 1 ? '启用' : '禁用' }} </el-tag>
</template>
</el-table-column>
<el-table-column prop="isAllowAccountLogin" label="账号状态" width="120">
<template v-slot="scope">
<el-tag :type="scope.row.isAllowAccountLogin == 1 ? 'success' : 'error'" effect="dark"> {{ scope.row.isAllowAccountLogin == 1 ? '启用' : '禁用' }} </el-tag>
<el-tag :type="scope.row.isAllowAccountLogin == 1 ? 'success' : 'error'" effect="dark"> {{
scope.row.isAllowAccountLogin == 1 ? '启用' : '禁用' }} </el-tag>
</template>
</el-table-column>
<el-table-column prop="createdAt" label="备注" />
@@ -105,7 +113,7 @@ onMounted(() => {
getTableData();
getDataSync()
});
async function getDataSync () {
async function getDataSync() {
let res = await ShopBranchApi.getDataSync()
state.par.dataSyncMethod = res
}

View File

@@ -21,10 +21,8 @@
<el-table-column label="头像">
<template v-slot="scope">
<div class="shop_info">
<el-image
:src="scope.row.avatar"
style="width: 50px; height: 50px; border-radius: 4px; background-color: #efefef"
>
<el-image :src="scope.row.avatar"
style="width: 50px; height: 50px; border-radius: 4px; background-color: #efefef">
<template #error>
<div class="img_error">
<i class="icon el-icon-document-delete"></i>
@@ -35,12 +33,10 @@
</template>
</el-table-column>
<el-table-column label="昵称" prop="nickname"></el-table-column>
<el-table-column label="描述提示" prop="content"></el-table-column>
<el-table-column label="订阅类型">
<template v-slot="scope">
<el-checkbox-group
:model-value="scope.row.typeInfo"
@change="typeInfoChange($event, scope.row)"
>
<el-checkbox-group :model-value="scope.row.typeInfo" @change="typeInfoChange($event, scope.row)">
<el-checkbox label="耗材推送" value="con"></el-checkbox>
<el-checkbox label="商品推送" value="pro"></el-checkbox>
<el-checkbox label="操作预警" value="ope"></el-checkbox>
@@ -59,14 +55,9 @@
</el-table>
</div>
<div class="head-container">
<el-pagination
:total="tableData.total"
@size-change="handleSizeChange"
:current-page="tableData.page"
:page-size="tableData.size"
@current-change="paginationChange"
layout="total, sizes, prev, pager, next, jumper"
></el-pagination>
<el-pagination :total="tableData.total" @size-change="handleSizeChange" :current-page="tableData.page"
:page-size="tableData.size" @current-change="paginationChange"
layout="total, sizes, prev, pager, next, jumper"></el-pagination>
</div>
</div>
</template>

View File

@@ -2,25 +2,15 @@
<div>
<el-form ref="form" :model="form" label-width="120px" label-position="left">
<el-form-item label="操作密码">
<el-input
v-model="form.password"
type="number"
@input="jiantingshuru"
:disabled="disabled"
:placeholder="disabled ? '******' : '请输入操作密码'"
style="width: 200px"
></el-input>
<el-input v-model="form.password" type="number" @input="jiantingshuru" :disabled="disabled"
:placeholder="disabled ? '******' : '请输入操作密码'" style="width: 200px"></el-input>
<el-button type="primary" @click="resetting">重置</el-button>
</el-form-item>
<el-form-item label="安全手机号">
{{ phoneFilter(form.phone) }}
</el-form-item>
<el-form-item label="验证码">
<el-input
v-model="form.prepareAmount"
placeholder="点击发送"
style="width: 200px"
></el-input>
<el-input v-model="form.prepareAmount" placeholder="点击发送" style="width: 200px"></el-input>
<el-button type="primary" @click="onSubmit">发送</el-button>
</el-form-item>
<el-form-item>
@@ -31,8 +21,8 @@
<el-form ref="form" :model="form" label-width="120px" label-position="left">
<el-form-item label="校验安全密码">
<el-checkbox v-model="form.isReturnPwd">退款</el-checkbox>
<el-checkbox v-model="form.isMemberInPwd">会员充值</el-checkbox>
<el-checkbox v-model="form.isMemberReturnPwd">会员退款</el-checkbox>
<el-checkbox v-model="form.isMemberInPwd">余额充值</el-checkbox>
<el-checkbox v-model="form.isMemberReturnPwd">余额退款</el-checkbox>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitHandles">保存</el-button>

View File

@@ -1,27 +1,16 @@
<template>
<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-tabs v-model="activeName">
<el-tab-pane label="基础设置" name="first">
<el-form-item label="门店名称" prop="shopName">
<el-input
v-model.trim="form.shopName"
placeholder="请输入门店名称"
style="width: 500px"
></el-input>
<el-input v-model.trim="form.shopName" placeholder="请输入门店名称" style="width: 500px"></el-input>
</el-form-item>
<el-form-item label="连锁店扩展店名">
<el-input
v-model.trim="form.chainName"
placeholder="请输入连锁店扩展店名"
style="width: 500px"
></el-input>
<el-input v-model.trim="form.chainName" placeholder="请输入连锁店扩展店名" style="width: 500px"></el-input>
</el-form-item>
<el-form-item label="门店logo">
<div class="img_box">
<single-image-upload
style="width: 80px; height: 80px"
v-model="form.logo"
></single-image-upload>
<single-image-upload style="width: 80px; height: 80px" v-model="form.logo"></single-image-upload>
</div>
</el-form-item>
<!-- <el-form-item label="门店照片">
@@ -38,22 +27,14 @@
<el-form-item label="门店收款码">
<div class="img_box">
<canvas ref="canvas" id="QRCode_header" style="width: 80px; height: 80px"></canvas>
<el-button
size="small"
plain
v-if="form.paymentQrcode"
@click="downloadCanvas(form.paymentQrcode)"
>
<el-button size="small" plain v-if="form.paymentQrcode" @click="downloadCanvas(form.paymentQrcode)">
下载
</el-button>
</div>
</el-form-item>
<el-form-item label="微信二维码">
<div class="img_box">
<single-image-upload
style="width: 80px; height: 80px"
v-model="form.shopQrcode"
></single-image-upload>
<single-image-upload style="width: 80px; height: 80px" v-model="form.shopQrcode"></single-image-upload>
</div>
</el-form-item>
<!-- <el-form-item label="店铺小程序码">
@@ -65,50 +46,12 @@
</el-button>
</div>
</el-form-item> -->
<el-form-item label="经营模式「单选」">
<el-radio-group v-model="form.registerType">
<el-radio value="before">快餐版先支付后下单</el-radio>
<el-radio value="after">餐饮版先下单后支付</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
label="是否允许用户自行支付"
prop="isUserPay"
v-if="form.registerType === 'after'"
>
<div class="column">
<div class="center" style="display: flex; align-items: center; gap: 14px">
<el-switch v-model="form.isUserPay" :active-value="1" :inactive-value="0"></el-switch>
<div class="tips" style="font-size: 14px; color: #999">
关闭后用户将不能自行支付
</div>
</div>
</div>
</el-form-item>
<el-form-item label="就餐模式「多选」" prop="eatModel">
<el-checkbox-group v-model="form.eatModel">
<el-checkbox value="dine-in">堂食自取</el-checkbox>
<el-checkbox value="take-out">允许打包</el-checkbox>
</el-checkbox-group>
</el-form-item>
<!-- <el-form-item label="积分群体">
<el-radio-group v-model="form.consumeColony">
<el-radio label="all">所有</el-radio>
<el-radio label="vip">仅针对会员</el-radio>
</el-radio-group>
</el-form-item> -->
<el-form-item label="联系电话" prop="phone">
<el-input
v-model.trim="form.phone"
placeholder="请输入联系电话"
style="width: 500px"
></el-input>
<el-input v-model.trim="form.phone" placeholder="请输入联系电话" style="width: 500px"></el-input>
</el-form-item>
<el-form-item label="店铺简介">
<el-input type="textarea" v-model.trim="form.detail" placeholder="请输入店铺简介" style="width: 500px"></el-input>
</el-form-item>
<!-- <el-form-item label="外卖起送金额">
<el-input-number v-model="form.takeaway_money" placeholder="0.00" controls-position="right"
:min="0"></el-input-number>
</el-form-item> -->
<el-form-item label="店铺经度" prop="provinces">
<el-row :gutter="6">
<el-col :span="9" v-if="form.provinces">
@@ -129,62 +72,74 @@
<div style="color: #999">准确的定位便于用户导航到店铺</div>
</el-form-item>
<el-form-item label="门店详细地址">
<el-input
type="textarea"
v-model.trim="form.address"
placeholder="请输入门店详细地址"
style="width: 500px"
></el-input>
<el-input type="textarea" v-model.trim="form.address" placeholder="请输入门店详细地址" style="width: 500px"></el-input>
</el-form-item>
</el-tab-pane>
<el-tab-pane label="经营设置" name="third">
<el-form-item label="经营模式「单选」">
<el-radio-group v-model="form.registerType">
<el-radio value="before">快餐版先支付后下单</el-radio>
<el-radio value="after">餐饮版先下单后支付</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="是否允许用户自行支付" prop="isUserPay" v-if="form.registerType === 'after'">
<div class="column">
<div class="center" style="display: flex; align-items: center; gap: 14px">
<el-switch v-model="form.isUserPay" :active-value="1" :inactive-value="0"></el-switch>
<div class="tips" style="font-size: 14px; color: #999">
关闭后用户将不能自行支付
</div>
</div>
</div>
</el-form-item>
<el-form-item label="就餐模式「多选」" prop="eatModel">
<el-checkbox-group v-model="form.eatModel">
<el-checkbox value="dine-in">堂食自取</el-checkbox>
<el-checkbox value="take-out">允许打包</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item label="退菜退库存模式">
<el-radio-group :model-value="form.refundMode" @change="refundModeChange">
<el-radio label="跟随商品分类" :value="1"></el-radio>
<el-radio label="跟随单商品" :value="2"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="后厨单菜二维码">
<el-switch v-model="form.isKitchenCode" :active-value="1" :inactive-value="0"></el-switch>
</el-form-item>
<!-- <el-form-item label="积分群体">
<el-radio-group v-model="form.consumeColony">
<el-radio label="all">所有</el-radio>
<el-radio label="vip">仅针对会员</el-radio>
</el-radio-group>
</el-form-item> -->
<!-- <el-form-item label="外卖起送金额">
<el-input-number v-model="form.takeaway_money" placeholder="0.00" controls-position="right"
:min="0"></el-input-number>
</el-form-item> -->
<el-form-item label="营业时间">
<div class="u-flex gap-2" style="width: 50%">
<el-select v-model="form.businessStartDay" placeholder="周几开始">
<el-option
:value="item.label"
:label="item.label"
v-for="item in weeks"
:key="item.value"
></el-option>
<el-option :value="item.label" :label="item.label" v-for="item in weeks" :key="item.value"></el-option>
</el-select>
<el-select v-model="form.businessEndDay" placeholder="周几结束">
<el-option
:value="item.label"
:label="item.label"
v-for="item in weeks"
:key="item.value"
></el-option>
<el-option :value="item.label" :label="item.label" v-for="item in weeks" :key="item.value"></el-option>
</el-select>
<el-time-picker
placeholder="起始时间"
v-model="startTime"
:picker-options="{
selectableRange: '00:00:00 - 23:59:59',
format: 'HH:mm',
}"
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>
<el-time-picker placeholder="起始时间" v-model="startTime" :picker-options="{
selectableRange: '00:00:00 - 23:59:59',
format: 'HH:mm',
}" 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>
</el-form-item>
<el-form-item label="桌位费/位/元">
<el-input-number :disabled="!!form.isTableFee" v-model="form.tableFee" :min="0" />
<!-- <el-checkbox v-model="form.isTableFee" :label="1">免餐位费</el-checkbox> -->
<el-switch
class="u-m-l-10"
v-model.trim="form.isTableFee"
:active-value="1"
:inactive-value="0"
active-text="免餐位费"
></el-switch>
<el-switch class="u-m-l-10" v-model.trim="form.isTableFee" :active-value="1" :inactive-value="0"
active-text="免餐位费"></el-switch>
</el-form-item>
<!-- <el-form-item label="是否开启8折活动">
<el-switch v-model.trim="form.isOpenYhq" active-value="true" inactive-value="false"></el-switch>
@@ -217,42 +172,22 @@
>
</el-time-picker>
</el-form-item> -->
<el-form-item label="店铺简介">
<el-input
type="textarea"
v-model.trim="form.detail"
placeholder="请输入店铺简介"
style="width: 500px"
></el-input>
</el-form-item>
<el-form-item label="台桌预订短信">
<el-input
type="textarea"
v-model.trim="form.bookingSms"
placeholder="请输入台桌预订短信"
style="width: 500px"
></el-input>
</el-form-item>
<!-- <el-form-item label="台桌预订短信">
<el-input type="textarea" v-model.trim="form.bookingSms" placeholder="请输入台桌预订短信"
style="width: 500px"></el-input>
</el-form-item> -->
<el-form-item label="电子围栏" prop="isOrderFence">
<div class="column">
<div class="center" style="display: flex; align-items: center; gap: 14px">
<el-switch
v-model="form.isOrderFence"
:active-value="1"
:inactive-value="0"
></el-switch>
<el-switch v-model="form.isOrderFence" :active-value="1" :inactive-value="0"></el-switch>
<div class="tips" style="font-size: 14px; color: #999">
开启后用户只能在店铺附近xx公里内点餐
</div>
</div>
<div class="center" v-if="form.isOrderFence == 1">
<el-input
v-model="form.orderFenceDistance"
placeholder="请输入"
@input="(e) => (form.orderFenceDistance = filterNumberInput(e))"
style="width: 250px"
input-style="text-align: center;"
>
<el-input v-model="form.orderFenceDistance" placeholder="请输入"
@input="(e) => (form.orderFenceDistance = filterNumberInput(e))" style="width: 250px"
input-style="text-align: center;">
<template #prepend>限制</template>
<template #append>公里</template>
</el-input>
@@ -263,23 +198,15 @@
<el-form-item label="上菜时间(分钟)" prop="isServeTimeControl">
<div class="column">
<div class="center" style="display: flex; align-items: center; gap: 14px">
<el-switch
v-model="form.isServeTimeControl"
:active-value="1"
:inactive-value="0"
></el-switch>
<el-switch v-model="form.isServeTimeControl" :active-value="1" :inactive-value="0"></el-switch>
<div class="tips" style="font-size: 14px; color: #999">
起菜到上菜的时间间隔开启后会有超时提示
</div>
</div>
<div class="center" v-if="form.isServeTimeControl == 1">
<el-input
v-model="form.serveTime"
placeholder="请输入"
@input="(e) => (form.serveTime = filterNumberInput(e))"
style="width: 250px"
input-style="text-align: center;"
>
<el-input v-model="form.serveTime" placeholder="请输入"
@input="(e) => (form.serveTime = filterNumberInput(e))" style="width: 250px"
input-style="text-align: center;">
<template #prepend>限制</template>
<template #append>分钟</template>
</el-input>
@@ -295,45 +222,32 @@
<el-radio :value="2">休息中</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitHandle" :loading="formLoading">
<span v-if="!formLoading">保存</span>
<span v-else>保存中...</span>
</el-button>
</el-form-item>
</el-form>
</div>
<ChooseAddress ref="refChooseAddress" @choose="chooseAddressConfirm"></ChooseAddress>
<el-dialog
v-model="showUpload"
:close-on-click-modal="false"
append-to-body
width="500px"
@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>
<template #tip>
<div style="display: block" class="el-upload__tip">请勿上传违法文件且文件不超过15M</div>
</template>
</el-upload>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="doSubmit">确认</el-button>
</div>
</el-tab-pane>
<el-form-item>
<el-button type="primary" @click="submitHandle" :loading="formLoading">
<span v-if="!formLoading">保存</span>
<span v-else>保存中...</span>
</el-button>
</el-form-item>
</el-tabs>
</el-form>
<ChooseAddress ref="refChooseAddress" @choose="chooseAddressConfirm"></ChooseAddress>
<el-dialog v-model="showUpload" :close-on-click-modal="false" append-to-body width="500px"
@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>
<template #tip>
<div style="display: block" class="el-upload__tip">请勿上传违法文件且文件不超过15M</div>
</template>
</el-dialog>
</div>
</el-upload>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="doSubmit">确认</el-button>
</div>
</template>
</el-dialog>
</template>
<script>
@@ -347,6 +261,7 @@ import { filterNumberInput } from "@/utils/index";
export default {
data() {
return {
activeName: "first",
filterNumberInput,
img_download_error,
showUpload: false,
@@ -459,6 +374,24 @@ export default {
},
},
methods: {
// 切换退菜退库存模式
refundModeChange(value) {
console.log('refundModeChange===', value);
const m = {
1: {
1: '跟随商品分类',
2: '商品分类'
},
2: {
1: '跟随单商品模式',
2: '商品列表'
}
}
ElMessageBox.confirm(`当前操作:将《退菜退库存》模式切换为「${m[value][1]}」本操作将会影响耗材库存数量的统计请谨慎操作修改后,可前往「商品管理-${m[value][2]}」中编辑/查看配置`, '提示', { showClose: false, showCancelButton: false }).then(() => {
this.form.refundMode = value
}).catch(() => { })
},
chooseAddressConfirm(e) {
console.log(e);
this.$refs.refChooseAddress.close();
@@ -576,7 +509,7 @@ export default {
handleBeforeRemove(file, fileList) {
for (let i = 0; i < this.files.length; i++) {
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;
}
}

View File

@@ -1,9 +1,10 @@
<template>
<el-dialog title="激活" width="500px" v-model="show" @close="reset">
<el-dialog title="激活" width="550px" v-model="show" @close="reset">
<el-form :model="form" ref="refForm" :rules="rules">
<el-form-item label="激活" prop="activateCode">
<el-input v-model="form.activateCode" placeholder="请输入激活码"></el-input>
<div class="tips">输入有效激活码表示添加的同时直接激活该店铺</div>
<el-form-item label="激活时长" prop="activateDuration">
<activeDate v-model="form.activateDuration" v-model:activeCost="form.activateAmount" />
<!-- <el-input v-model="form.activateDuration" placeholder="请输入激活码"></el-input>
<div class="tips">输入有效激活码表示添加的同时直接激活该店铺</div> -->
</el-form-item>
</el-form>
<template #footer>
@@ -15,18 +16,20 @@
<script setup>
import ShopApi from "@/api/account/shop";
import activeDate from "./activeDate.vue";
const show = ref(false);
const form = reactive({
activateCode: "",
activateDuration: "",
activateAmount: '',
id: "",
});
const rules = {
activateCode: [{ required: true, message: "请输入激活码", trigger: "blur" }],
activateDuration: [{ required: true, message: "请选择激活时长", trigger: "blur" }],
};
function reset() {
form.activateCode = "";
form.activateDuration = "";
form.id = "";
}
const emit = defineEmits(["submit"]);

View File

@@ -0,0 +1,32 @@
<template>
<el-select v-model="modelValue" style="width: 200px;" placeholder="请选择激活时长">
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"></el-option>
</el-select>
<el-input v-model="activeCost" placeholder="请输入激活费用(选填)" style="width: 200px; margin-left: 14px;" />
</template>
<script setup>
import { ref, onMounted } from "vue";
const modelValue = defineModel({
type: [String, Number],
default: "",
});
// 激活费用
const activeCost = defineModel('activeCost', {
type: String,
default: "",
})
const options = ref([])
onMounted(() => {
for (let i = 1; i <= 12; i++) {
options.value.push({
label: `${i}个月`,
value: i,
});
}
})
</script>

View File

@@ -0,0 +1,61 @@
<template>
<!-- 激活记录 -->
<el-dialog v-model="show" title="激活记录" width="500px">
<el-table :data="tableData.records" style="width: 100%">
<el-table-column prop="periodMonth" label="激活时长(月)"></el-table-column>
<el-table-column prop="amount" label="店铺签约金额">
<template v-slot="scope">
{{ formatMoney(scope.row.amount) }}
</template>
</el-table-column>
<el-table-column prop="createTime" label="操作时间"></el-table-column>
</el-table>
<el-pagination v-model:current-page="tableData.page" v-model:page-size="tableData.size"
:page-sizes="[10, 20, 50, 100]" :total="tableData.total" @current-change="getData" @size-change="getData"
style="margin-top: 16px; text-align: right;" />
</el-dialog>
</template>
<script setup>
import { ref, reactive } from "vue";
import ShopApi from "@/api/account/shop";
import { formatMoney } from "@/utils";
const shopInfo = ref({ id: '' })
const show = ref(false);
const tableData = reactive({
loading: false,
page: 1,
size: 10,
total: 0,
records: [],
});
async function getData() {
try {
tableData.loading = true;
const res = await ShopApi.registerRecord({
page: tableData.page,
size: tableData.size,
shopId: shopInfo.value.id
});
tableData.records = res.records;
tableData.total = res.totalRow;
} catch (error) {
console.log(error);
} finally {
tableData.loading = false;
}
}
function open(data) {
shopInfo.value = data;
show.value = true;
getData();
}
defineExpose({
open,
});
</script>

View File

@@ -1,72 +1,35 @@
<template>
<div>
<el-dialog
:title="state.form.id ? '编辑店铺' : '添加店铺'"
v-model="state.dialogVisible"
@close="reset"
>
<el-dialog :title="state.form.id ? '编辑店铺' : '添加店铺'" v-model="state.dialogVisible" @close="reset">
<div style="height: 50vh; overflow-y: auto">
<el-form
ref="refForm"
:model="state.form"
:rules="state.rules"
label-width="120px"
label-position="left"
>
<el-form ref="refForm" :model="state.form" :rules="state.rules" label-width="120px" label-position="left">
<el-form-item label="店铺名称" prop="shopName">
<el-input v-model="state.form.shopName" placeholder="请输入门店名称"></el-input>
</el-form-item>
<el-form-item label="店铺类型">
<el-radio-group
v-model="state.form.shopType"
:disabled="state.isEdit || state.type == 'addBranch'"
>
<el-radio-group v-model="state.form.shopType" :disabled="state.isEdit || state.type == 'addBranch'">
<el-radio-button value="only">单店</el-radio-button>
<el-radio-button value="chain">连锁店</el-radio-button>
<el-radio-button value="join">加盟店</el-radio-button>
</el-radio-group>
<div class="tips">
<el-alert
title="请谨慎修改"
type="warning"
size="7"
effect="dark"
show-icon
:closable="false"
/>
<el-alert title="请谨慎修改" type="warning" size="7" effect="dark" show-icon :closable="false" />
</div>
</el-form-item>
<el-form-item label="是否为主店" prop="isHeadShop" v-if="state.form.shopType != 'only'">
<el-radio-group
v-model="state.form.isHeadShop"
@change="state.form.mainId = ''"
:disabled="state.isEdit || state.type == 'addBranch'"
>
<el-radio-group v-model="state.form.isHeadShop" @change="state.form.mainId = ''"
:disabled="state.isEdit || state.type == 'addBranch'">
<el-radio :value="1"></el-radio>
<el-radio :value="0"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
label="选择主店"
prop="mainId"
v-if="state.form.isHeadShop == '0' && state.form.shopType != 'only'"
>
<el-form-item label="选择主店" prop="mainId" v-if="state.form.isHeadShop == '0' && state.form.shopType != 'only'">
<!-- <el-form-item label="主店账号" prop="mainId" v-if="state.form.shopType != 'only'"> -->
<el-select
v-model="state.form.mainId"
placeholder="请选择主店铺"
filterable
reserve-keyword
:remote-method="getTableData"
:loading="state.shopListLoading"
:disabled="state.isEdit || state.type == 'addBranch'"
>
<el-option
v-for="item in state.shopList"
:label="`${item.shopName}`"
:value="item.id"
:key="item.id"
></el-option>
<el-select v-model="state.form.mainId" placeholder="请选择主店铺" filterable reserve-keyword
:remote-method="getTableData" :loading="state.shopListLoading"
:disabled="state.isEdit || state.type == 'addBranch'">
<el-option v-for="item in state.shopList" :label="`${item.shopName}`" :value="item.id"
:key="item.id"></el-option>
</el-select>
</el-form-item>
<el-form-item label="连锁店扩展店名">
@@ -84,14 +47,7 @@
<el-radio-button value="after">后付费</el-radio-button>
</el-radio-group>
<div class="tips">
<el-alert
title="请谨慎修改"
type="warning"
size="7"
effect="dark"
show-icon
:closable="false"
/>
<el-alert title="请谨慎修改" type="warning" size="7" effect="dark" show-icon :closable="false" />
</div>
</el-form-item>
<el-form-item label="管理方式" v-if="state.form.shopType != 'only'">
@@ -100,14 +56,7 @@
<el-radio-button :value="1">直接管理</el-radio-button>
</el-radio-group>
<div class="tips">
<el-alert
title="请谨慎修改"
type="warning"
size="7"
effect="dark"
show-icon
:closable="false"
/>
<el-alert title="请谨慎修改" type="warning" size="7" effect="dark" show-icon :closable="false" />
</div>
</el-form-item>
<el-form-item label="商户版本">
@@ -116,53 +65,34 @@
<el-radio-button value="release">正式</el-radio-button>
</el-radio-group>
<div class="tips">
<el-alert
title="请谨慎修改"
type="warning"
size="7"
effect="dark"
show-icon
:closable="false"
/>
<el-alert title="请谨慎修改" type="warning" size="7" effect="dark" show-icon :closable="false" />
</div>
</el-form-item>
<el-form-item label="激活">
<el-input v-model="state.form.activateCode" placeholder="请输入激活码"></el-input>
<div class="tips">输入有效激活码表示添加的同时直接激活该店铺</div>
<el-form-item label="激活时长" prop="activateDuration">
<activeDate v-model="state.form.activateDuration" v-model:activeCost="state.form.activateAmount" />
<!-- <el-input v-model="state.form.activateDuration" placeholder="请输入激活码"></el-input>
<div class="tips">输入有效激活码表示添加的同时直接激活该店铺</div> -->
</el-form-item>
<el-form-item label="登录账号" prop="accountName">
<el-input v-model="state.form.accountName" placeholder="请输入登录账号"></el-input>
</el-form-item>
<el-form-item label="登录密码" prop="password" v-if="!state.form.id">
<el-input
type="password"
show-password
v-model="state.form.accountPwd"
placeholder="请输入登录密码"
></el-input>
<el-form-item label="登录密码" prop="accountPwd" v-if="!state.form.id">
<el-input type="password" show-password v-model="state.form.accountPwd" placeholder="请输入登录密码"></el-input>
</el-form-item>
<el-form-item label="联系电话" prop="phone">
<el-input v-model="state.form.phone" placeholder="请输入联系电话"></el-input>
</el-form-item>
<el-form-item label="设备数量">
<el-input-number
v-model="state.form.supportDeviceNumber"
controls-position="right"
:min="1"
:step="1"
step-strictly
></el-input-number>
<el-input-number v-model="state.form.supportDeviceNumber" controls-position="right" :min="1" :step="1"
step-strictly></el-input-number>
</el-form-item>
<!-- <el-form-item label="外卖起送金额">
<el-input-number v-model="form.takeaway_money" placeholder="0.00" controls-position="right"
:min="0"></el-input-number>
</el-form-item> -->
<el-form-item label="店铺地址" prop="districts">
<AddressSelect
v-model:prov="state.form.provinces"
v-model:city="state.form.cities"
v-model:area="state.form.districts"
></AddressSelect>
<AddressSelect v-model:prov="state.form.provinces" v-model:city="state.form.cities"
v-model:area="state.form.districts"></AddressSelect>
</el-form-item>
<div>
@@ -204,18 +134,10 @@
</el-row>
</el-form-item> -->
<el-form-item label="店铺详细地址">
<el-input
type="textarea"
v-model="state.form.address"
placeholder="请输入门店详细地址"
></el-input>
<el-input type="textarea" v-model="state.form.address" placeholder="请输入门店详细地址"></el-input>
</el-form-item>
<el-form-item label="店铺简介">
<el-input
type="textarea"
v-model="state.form.detail"
placeholder="请输入店铺简介"
></el-input>
<el-input type="textarea" v-model="state.form.detail" placeholder="请输入店铺简介"></el-input>
</el-form-item>
<el-form-item label="状态">
<el-radio-group v-model="state.form.status">
@@ -225,12 +147,7 @@
</el-form-item>
</el-form>
</div>
<el-dialog
title="选择地址"
v-model="state.showLocation"
:modal="false"
:modal-append-to-body="false"
>
<el-dialog title="选择地址" v-model="state.showLocation" :modal="false" :modal-append-to-body="false">
<div class="map_box">
<div class="map">
<el-amap ref="map" :center="state.amapOptions.center" @init="mapInit">
@@ -238,13 +155,8 @@
</el-amap>
</div>
<div class="search_box">
<el-input
v-model="state.searchOption.keyword"
placeholder="请输入关键字"
@focus="state.searchOption.focus = true"
@blur="autoCompleteSearchBlur"
@input="autoCompleteSearch(state.searchOption.keyword)"
>
<el-input v-model="state.searchOption.keyword" placeholder="请输入关键字" @focus="state.searchOption.focus = true"
@blur="autoCompleteSearchBlur" @input="autoCompleteSearch(state.searchOption.keyword)">
<template #append>
<el-button type="primary" @click="placeSearchSearch(state.searchOption.keyword)">
搜索
@@ -252,12 +164,8 @@
</template>
</el-input>
<div class="list" v-if="state.searchOption.focus && state.searchOption.show">
<div
class="item"
@click="autoCompleteListClick(item)"
v-for="item in state.autoCompleteList"
:key="item.id"
>
<div class="item" @click="autoCompleteListClick(item)" v-for="item in state.autoCompleteList"
:key="item.id">
{{ item.name }}
</div>
</div>
@@ -288,11 +196,7 @@
</template>
</el-dialog>
<el-dialog title="坐标搜索" v-model="latShow" width="60vw">
<iframe
style="width: 100%; height: 60vh"
src="https://lbs.baidu.com/maptool/getpoint"
frameborder="0"
></iframe>
<iframe style="width: 100%; height: 60vh" src="https://lbs.baidu.com/maptool/getpoint" frameborder="0"></iframe>
<template #footer>
<div class="dialog-footer">
<el-button @click="latShow = false">取消</el-button>
@@ -311,6 +215,7 @@ import { initMapLoad } from "@/utils/mapLoadUtil";
import { ElNotification } from "element-plus";
// { geocode, ShopApi.getList }
import ShopApi from "@/api/account/shop";
import activeDate from "./activeDate.vue";
const latShow = ref(false);
const validateLogo = (rule, value, callback) => {
@@ -333,7 +238,7 @@ async function latConfirm() {
state.form.lat = latLng[1];
state.form.lng = latLng[0];
latShow.value = false;
} else [ElMessage.error("请搜索地址后复制经纬度")];
} else[ElMessage.error("请搜索地址后复制经纬度")];
} catch (err) {
// 捕获异常(用户拒绝权限、浏览器不支持、非交互上下文等)
console.error("获取剪贴板失败:", err);
@@ -360,7 +265,8 @@ const state = reactive({
tubeType: 0,
registerType: "before",
profiles: "release",
activateCode: "",
activateDuration: "", // 激活时长
activateAmount: "", // 激活费用
accountName: "",
accountPwd: "",
phone: "",
@@ -381,11 +287,11 @@ const state = reactive({
type: "",
resetForm: "",
rules: {
activateCode: [
activateDuration: [
{
required: true,
message: "请输入激活码",
trigger: "blur",
message: "请选择激活时长",
trigger: "change",
},
],
shopName: [
@@ -458,6 +364,25 @@ const state = reactive({
trigger: "change",
},
],
// 密码长度6-18位需包含字母、数字、特殊符号中至少两种
accountPwd: [
{
required: true,
message: "请输入登录密码",
trigger: "blur",
},
{
min: 6,
max: 18,
message: "密码长度应为6-18位",
trigger: "blur",
},
{
pattern: /^(?![0-9]+$)(?![a-zA-Z]+$)(?![^\da-zA-Z]+$).{6,}$/,
message: "密码需包含字母、数字、特殊符号中至少两种",
trigger: "blur",
},
]
},
fileList: [],
files: [],
@@ -561,6 +486,7 @@ function show(obj, type) {
if (obj && obj.id) {
console.log(obj);
state.form = { ...obj };
state.form.accountName = obj.account ? obj.account : "";
}
if (obj && obj.mainId) {
Object.assign(state.form, obj);
@@ -730,6 +656,7 @@ defineExpose({
.amap-sug-result {
z-index: 2000;
}
.tips {
margin-left: 10px;
}

View File

@@ -1,52 +1,26 @@
<template>
<div class="app-container">
<div class="head-container">
<el-row :gutter="20">
<el-col :span="3">
<el-input
v-model="state.query.name"
clearable
placeholder="请输入店铺名称"
style="width: 100%"
class="filter-item"
@keyup.enter="getTableData"
/>
<el-row :gutter="24">
<el-col :span="4">
<el-input v-model="state.query.name" clearable placeholder="请输入店铺名称" style="width: 100%" class="filter-item"
@keyup.enter="getTableData" />
</el-col>
<el-col :span="3">
<el-input
v-model="state.query.phone"
clearable
placeholder="请输入手机号"
style="width: 100%"
class="filter-item"
@keyup.enter="getTableData"
/>
<el-col :span="4">
<el-input v-model="state.query.phone" clearable placeholder="请输入手机号" style="width: 100%" class="filter-item"
@keyup.enter="getTableData" />
</el-col>
<el-col :span="3">
<el-col :span="4">
<el-select v-model="state.query.status" placeholder="请选择店铺状态" style="width: 100%">
<el-option
v-for="item in state.status"
:key="item.type"
:label="item.label"
:value="item.type"
/>
<el-option v-for="item in state.status" :key="item.type" :label="item.label" :value="item.type" />
</el-select>
</el-col>
<el-col :span="3">
<el-select
v-model="state.query.profiles"
placeholder="请选择商户版本"
style="width: 100%"
>
<el-option
v-for="item in state.profiles"
:key="item.type"
:label="item.label"
:value="item.type"
/>
<el-col :span="4">
<el-select v-model="state.query.profiles" placeholder="请选择商户版本" style="width: 100%">
<el-option v-for="item in state.profiles" :key="item.type" :label="item.label" :value="item.type" />
</el-select>
</el-col>
<el-col :span="3">
<el-col :span="4">
<el-button type="primary" @click="getTableData">查询</el-button>
<el-button @click="resetHandle">重置</el-button>
</el-col>
@@ -60,10 +34,8 @@
<el-table-column label="店铺信息" width="200">
<template v-slot="scope">
<div class="shop_info">
<el-image
:src="scope.row.logo"
style="width: 50px; height: 50px; border-radius: 4px; background-color: #efefef"
>
<el-image :src="scope.row.logo"
style="width: 50px; height: 50px; border-radius: 4px; background-color: #efefef">
<template #error>
<div class="img_error">
<i class="icon el-icon-document-delete" />
@@ -73,16 +45,16 @@
<div class="info">
<span>{{ scope.row.shopName }}</span>
<div class="tag_wrap">
<el-tag v-if="scope.row.profiles == 'no'" type="info" effect="dark">
<el-tag v-if="scope.row.profiles == 'no'" type="info" disable-transitions effect="dark">
未激活
</el-tag>
<el-tag v-if="scope.row.profiles == 'probation'" type="warning" effect="dark">
<el-tag v-if="scope.row.profiles == 'probation'" type="warning" disable-transitions effect="dark">
试用
</el-tag>
<el-tag v-if="scope.row.profiles == 'release'" type="success" effect="dark">
<el-tag v-if="scope.row.profiles == 'release'" type="success" disable-transitions effect="dark">
正式
</el-tag>
<el-tag v-if="scope.row.isWxMaIndependent" type="primary" effect="dark">
<el-tag v-if="scope.row.isWxMaIndependent" type="primary" disable-transitions effect="dark">
独立小程序
</el-tag>
</div>
@@ -154,7 +126,7 @@
支付配置
</el-dropdown-item>
<el-dropdown-item :command="2">续费记录</el-dropdown-item>
<el-dropdown-item :command="3">前往店铺</el-dropdown-item>
<!-- <el-dropdown-item :command="3">前往店铺</el-dropdown-item> -->
<el-dropdown-item :command="4">重置密码</el-dropdown-item>
<el-dropdown-item divided :command="5">删除</el-dropdown-item>
</el-dropdown-menu>
@@ -165,19 +137,18 @@
</el-table>
</div>
<div class="head-container">
<el-pagination
v-model:current-page="state.tableData.page"
v-model:page-size="state.tableData.size"
:total="state.tableData.total"
:page-sizes="[10, 20, 30, 50, 100]"
layout="total, sizes , prev, pager ,next, jumper "
@current-change="paginationChange"
/>
<el-pagination v-model:current-page="state.tableData.page" v-model:page-size="state.tableData.size"
:total="state.tableData.total" :page-sizes="[10, 20, 30, 50, 100]"
layout="total, sizes , prev, pager ,next, jumper " @current-change="paginationChange" />
</div>
<addShop ref="refAddShop" @success="getTableData" />
<!-- <detailModal ref="refDetailModal" /> -->
<!-- 激活码 -->
<activateCode ref="refActivateCode" @success="getTableData" />
<!-- 重置密码 -->
<resetPassword ref="refResetPasswordRef" @success="getTableData" />
<!-- 激活记录 -->
<activateRecord ref="refActivateRecordRef" />
</div>
</template>
@@ -189,6 +160,12 @@ import { ElNotification, ElMessageBox } from "element-plus";
import addShop from "./components/addShop.vue";
import detailModal from "./components/detailModal.vue";
import { useRouter } from "vue-router";
import resetPassword from "@/components/resetPassword/index.vue";
import activateRecord from "./components/activeteRecord.vue";
const refActivateRecordRef = ref(null);
const refResetPasswordRef = ref(null);
const router = useRouter();
@@ -242,7 +219,7 @@ onMounted(() => {
const refDetailModal = ref(null);
function dropdownClick(e, row) {
console.log(e);
console.log(row);
// console.log(row);
if (e == 0) {
refAddShop.value.show({ mainId: row.id, shopType: row.shopType, isHeadShop: 0 }, "addBranch");
@@ -264,6 +241,15 @@ function dropdownClick(e, row) {
});
return;
}
if (e == 2) {
// 续费记录
refActivateRecordRef.value.open(row);
}
if (e == 4) {
// 重置密码
// console.log('重置密码', row);
refResetPasswordRef.value.show(row);
}
if (e == 5) {
ElMessageBox.confirm("是否确认删除该店铺?", "提示", {
confirmButtonText: "确认",
@@ -278,7 +264,7 @@ function dropdownClick(e, row) {
});
getTableData();
})
.catch(() => {});
.catch(() => { });
return;
}
}

View File

@@ -11,12 +11,12 @@
<div class="t">{{ route.query.shopName }} | 支付配置</div>
<div class="intro">管理您的支付渠道和进件信息</div>
</div>
<div class="center">
<!-- <div class="center">
<el-text size="large">当前模式</el-text>
<el-radio-group size="large" :model-value="payModel" @change="handleRadioChange">
<el-radio :label="item.label" :value="item.value" v-for="item in tabList" :key="item.value"></el-radio>
</el-radio-group>
</div>
</div> -->
</div>
<div class="gyq_content row mt14">
<tabHeader v-model="tabActiveIndex" :list="tabList" />
@@ -39,10 +39,10 @@ const router = useRouter()
const payModel = ref('')
const tabActiveIndex = ref(0)
const tabList = ref([
{
label: '支付进件',
value: 'native'
},
// {
// label: '支付进件',
// value: 'native'
// },
{
label: '聚合支付',
value: 'poly'

View File

@@ -39,6 +39,11 @@ const contentConfig: IContentConfig = {
align: "center",
prop: "code",
},
{
label: "员工角色",
align: "center",
prop: "roleName",
},
{
label: "最大优惠金额",
align: "center",
@@ -55,6 +60,7 @@ const contentConfig: IContentConfig = {
label: "操作",
align: "center",
templet: "tool",
width: 240,
operat: ["edit", "delete",
{
name: "change_pwd",

View File

@@ -27,6 +27,7 @@
</div>
<div class="status" v-if="
item.isSoldStock ||
item.isSoldOut ||
!item.isSale ||
!item.isSaleTime ||
(item.isStock && item.stockNumber * 1 <= 0)
@@ -37,8 +38,8 @@
ElMessage.error('该商品不在可售时间内') ||
isProductAvailable(item.days, item.startTime, item.endTime)
" v-else-if="!item.isSaleTime" iconClass="no-sale" color="#fff" size="60"></svg-icon>
<svg-icon @click="ElMessage.error('该商品已售罄')" v-else-if="item.isSoldStock" iconClass="shouqing" color="#fff"
size="60"></svg-icon>
<svg-icon @click="ElMessage.error('该商品已售罄')" v-else-if="item.isSoldStock || item.isSoldOut" iconClass="shouqing"
color="#fff" size="60"></svg-icon>
<svg-icon @click="ElMessage.error('库存不足')" v-else-if="item.isStock && item.stockNumber * 1 <= 0"
iconClass="stock_null" color="#fff" size="60"></svg-icon>
</div>

View File

@@ -29,7 +29,7 @@
</template>
</el-dialog>
</template>
<script>
<script>
import { ElMessage } from "element-plus";
export default {
data() {
@@ -72,9 +72,9 @@ export default {
this.$refs.form.validate((valid) => {
if (valid) {
console.log(valid);
if (this.form.discount_sale_amount * 1 <= 0 || this.form.discount_sale_amount * 1 <= 0) {
return ElMessage.error("价格和数量必须大于0");
}
// if (this.form.discount_sale_amount * 1 <= 0 || this.form.discount_sale_amount * 1 <= 0) {
// return ElMessage.error("价格和数量必须大于0");
// }
this.submit();
} else {
console.log("error submit!!");
@@ -83,27 +83,31 @@ export default {
});
},
},
mounted() {},
mounted() { },
};
</script>
<style lang="scss" scoped>
<style lang="scss" scoped>
:deep(.el-dialog__body) {
margin-bottom: 14px;
margin-top: 14px;
}
:deep(.el-form-item__label) {
text-align: left;
}
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
}
:deep(.el-input__inner::-webkit-inner-spin-button) {
-webkit-appearance: none;
margin: 0;
}
:deep(el-input__inner::-webkit-outer-spin-button) {
-webkit-appearance: none;
margin: 0;

View File

@@ -41,13 +41,8 @@
</div>
<div class="u-flex u-flex-wrap tags">
<div
class="tag"
v-for="(tag, index) in tags"
@click="changeSel(tag)"
:key="index"
:class="{ active: tag.checked }"
>
<div class="tag" v-for="(tag, index) in tags" @click="changeSel(tag)" :key="index"
:class="{ active: tag.checked }">
{{ tag.label }}
</div>
</div>
@@ -64,10 +59,15 @@
</div>
</template>
</el-dialog>
<!-- 退款退菜推库存的操作弹窗 -->
<refundConsModal ref="refundConsModalRef" :list="refundList" @success="refundConsModalSuccess" />
</template>
<script>
<script>
import { ElMessage } from "element-plus";
import { useCartsStore } from '@/store/modules/carts'
import refundConsModal from "@/components/refundConsModal.vue";
export default {
components: { refundConsModal },
props: {
modal: {
type: Boolean,
@@ -90,6 +90,9 @@ export default {
goods: {
productId: -999,
},
refundList: [],
refundStock: '',
note: ''
};
},
computed: {
@@ -132,34 +135,79 @@ export default {
this.show = false;
this.number = 1;
},
// 选择退库存模式后
refundConsModalSuccess(e) {
this.refundStock = e
this.refundNext()
},
refundNext() {
this.$emit("confirm", {
refundReason: this.note,
refundAmount: 0,
refundDetails: [{ id: this.goods.id, num: this.number, returnAmount: 0 }],
refundStock: this.refundStock
});
this.close();
},
// 确认退菜
confirm() {
const selTag = this.tags
.filter((item) => item.checked)
.map((item) => item.label)
.join(",");
const note = selTag + (this.note.length > 0 ? "," + this.note : "");
console.log(note);
if (!note) {
this.note = selTag + (this.note.length > 0 ? "," + this.note : "");
if (!this.note) {
return ElMessage.error("请输入退菜原因");
}
this.$emit("confirm", {
refundReason: note,
refundAmount: 0,
refundDetails: [{ id: this.goods.id, num: this.number, returnAmount: 0 }],
});
this.close();
const carts = useCartsStore();
let categorys = JSON.parse(localStorage.getItem('categorys'))
let shopInfo = JSON.parse(localStorage.getItem('userInfo'))
// 在这里给订单的商品补全库存信息 start
carts.goods.forEach(val => {
if (this.goods.productId == val.id) {
if (shopInfo.refundMode == 1) {
// 跟随分类退款模式
categorys.forEach(v => {
if (val.categoryId == v.id) {
this.goods.refundMode = v.refundMode
}
})
} else {
// 跟随商品退款模式及
this.goods.refundMode = val.refundMode
}
}
})
console.log('this.goods===', this.goods);
if (this.goods.refundMode == 3) {
this.refundList = [
{
name: this.goods.product_name,
num: this.number
}
]
this.$refs.refundConsModalRef.show()
return
}
// 在这里给订单的商品补全库存信息 end
this.refundNext()
},
},
mounted() {},
mounted() { },
};
</script>
<style lang="scss" scoped>
<style lang="scss" scoped>
:deep(.el-dialog__body) {
margin-bottom: 14px;
margin-top: 14px;
padding: 0 20px;
}
:deep(.el-tag) {
margin-top: 10px;
margin-right: 10px;
@@ -169,6 +217,7 @@ export default {
line-height: 35px;
height: 35px;
}
.tags {
.tag {
margin: 10px 10px 0 0;
@@ -178,6 +227,7 @@ export default {
font-size: 14px;
color: #000;
cursor: pointer;
&.active {
color: #1890ff;
background: #e8f4ff;
@@ -185,10 +235,12 @@ export default {
}
}
}
:deep(.number-box .el-input__inner::-webkit-inner-spin-button) {
-webkit-appearance: none;
margin: 0;
}
:deep(.number-box .el-input__inner::-webkit-outer-spin-button) {
-webkit-appearance: none;
margin: 0;

View File

@@ -665,6 +665,7 @@ function getCategoryList() {
size: 200,
})
.then((res) => {
localStorage.setItem('categorys', JSON.stringify(res))
res.unshift({ name: "全部", id: "" });
category.list = res;
});

View File

@@ -8,13 +8,18 @@
// adminIn 管理员充值
// adminOut管理员消费
export const $bizCode = {
cashIn: "现金充值",
cashIn: "会员充值",
cashback: "消费返现",
cashback_refund: "消费返现扣减",
freeIn: "霸王餐充值",
awardIn: "充值奖励",
wechatIn: "微信小程序充值",
alipayIn: "支付宝小程序充值",
awardIn: "充值奖励",
rechargeRefund: "充值退款",
orderPay: "订单消费",
orderPay: "订单支付奖励",
orderRefund: "订单退款",
adminIn: "管理员充值",
adminOut: "管理员消费",
rechargeRefund: "充值退款",
rechargeCashRefund: "会员现金退款",
adminIn: "管理员手动增减余额",
adminOut: "管理员退款充值",
rechargeRedemption: "充值兑换码"
}