add: 增加新功能
This commit is contained in:
15
.vscode/settings.json
vendored
15
.vscode/settings.json
vendored
@@ -2,14 +2,14 @@
|
||||
"typescript.tsdk": "./node_modules/typescript/lib",
|
||||
"npm.packageManager": "pnpm",
|
||||
"editor.tabSize": 2,
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.quickSuggestions": {
|
||||
"other": true,
|
||||
"comments": true,
|
||||
"strings": true
|
||||
},
|
||||
"editor.codeActionsOnSave": {
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll": "explicit",
|
||||
"source.fixAll.eslint": "explicit",
|
||||
"source.fixAll.stylelint": "explicit"
|
||||
@@ -67,7 +67,9 @@
|
||||
"i18n-ally.sortKeys": true,
|
||||
"i18n-ally.namespace": false,
|
||||
"i18n-ally.pathMatcher": "{namespaces}/{locale}.{ext}",
|
||||
"i18n-ally.enabledParsers": ["ts"],
|
||||
"i18n-ally.enabledParsers": [
|
||||
"ts"
|
||||
],
|
||||
"i18n-ally.sourceLanguage": "en",
|
||||
"i18n-ally.displayLanguage": "zh-CN",
|
||||
"i18n-ally.enabledFrameworks": [
|
||||
@@ -93,5 +95,8 @@
|
||||
"files.associations": {
|
||||
"*.ttml": "xml",
|
||||
"*.ttss": "css"
|
||||
},
|
||||
"[javascript]": {
|
||||
"editor.defaultFormatter": "vscode.typescript-language-features"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ const baseURL = Market_BaseUrl + "/admin/coupon";
|
||||
const API = {
|
||||
getList(params: getListRequest) {
|
||||
return request<any>({
|
||||
url: `${baseURL}`,
|
||||
url: `${baseURL}/page`,
|
||||
method: "get",
|
||||
params
|
||||
});
|
||||
|
||||
@@ -2,7 +2,8 @@ import request from "@/utils/request";
|
||||
import {
|
||||
Account_BaseUrl,
|
||||
Product_BaseUrl,
|
||||
Market_BaseUrl
|
||||
Market_BaseUrl,
|
||||
System_BaseUrl
|
||||
} from "@/api/config";
|
||||
|
||||
// 获取分店列表
|
||||
@@ -13,6 +14,14 @@ export function getBranchPage() {
|
||||
});
|
||||
}
|
||||
|
||||
// 店铺分店列表(下拉展示主店和分店使用,默认第一个是主店,其余是分店)
|
||||
export function getBranchList() {
|
||||
return request({
|
||||
url: `${Account_BaseUrl + "/admin/shopInfo/branchList"}`,
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
// 获取商品-列表
|
||||
export function getProductList() {
|
||||
return request({
|
||||
@@ -161,4 +170,266 @@ export function getShopUserList(params) {
|
||||
method: 'get',
|
||||
params
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 消费返现 配置信息获取
|
||||
export function consumeCashback() {
|
||||
return request({
|
||||
url: `${Market_BaseUrl + "/admin/consumeCashback"}`,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
// 消费返现 配置信息修改
|
||||
export function consumeCashbackPost(data) {
|
||||
return request({
|
||||
url: `${Market_BaseUrl + "/admin/consumeCashback"}`,
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 消费返现 记录获取
|
||||
export function consumeCashbackRecord(params) {
|
||||
return request({
|
||||
url: `${Market_BaseUrl + "/admin/consumeCashback/record"}`,
|
||||
method: 'get',
|
||||
params
|
||||
});
|
||||
}
|
||||
|
||||
// 满减活动 配置信息获取
|
||||
export function discountActivityPage(params) {
|
||||
return request({
|
||||
url: `${Market_BaseUrl + "/admin/discountActivity/page"}`,
|
||||
method: 'get',
|
||||
params
|
||||
});
|
||||
}
|
||||
|
||||
// 满减活动 新增
|
||||
export function discountActivity(data, method = 'post') {
|
||||
return request({
|
||||
url: `${Market_BaseUrl + "/admin/discountActivity"}`,
|
||||
method: method,
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 满减活动 删除
|
||||
export function discountActivityDelete(id) {
|
||||
return request({
|
||||
url: `${Market_BaseUrl + "/admin/discountActivity"}?id=${id}`,
|
||||
method: 'DELETE'
|
||||
});
|
||||
}
|
||||
|
||||
// 店铺详情
|
||||
export function shopInfoGet() {
|
||||
return request({
|
||||
url: `${Account_BaseUrl + "/admin/shopInfo/detail"}`,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
// 店铺编辑
|
||||
export function shopInfoPut(data) {
|
||||
return request({
|
||||
url: `${Account_BaseUrl + "/admin/shopInfo"}`,
|
||||
method: 'put',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 私域引流 配置信息获取
|
||||
export function drainageConfigGet() {
|
||||
return request({
|
||||
url: `${Market_BaseUrl + "/admin/drainageConfig"}`,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
// 私域引流 配置信息修改
|
||||
export function drainageConfigPost(data) {
|
||||
return request({
|
||||
url: `${Market_BaseUrl + "/admin/drainageConfig"}`,
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 短信模板 新增
|
||||
export function smsTemplate(data) {
|
||||
return request({
|
||||
url: `${Market_BaseUrl + "/admin/smsTemplate"}`,
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 短信模板 重新提交
|
||||
export function smsTemplateResubmit(data) {
|
||||
return request({
|
||||
url: `${Market_BaseUrl + "/admin/smsTemplate/resubmit"}`,
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 短信模板 列表
|
||||
export function smsTemplateGet() {
|
||||
return request({
|
||||
url: `${Market_BaseUrl + "/admin/smsTemplate"}`,
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
// 短信模板 列表 管理员专用
|
||||
export function smsTemplatePage(data) {
|
||||
return request({
|
||||
url: `${Market_BaseUrl + "/admin/smsTemplate/query"}`,
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 获取短信发送用户
|
||||
export function getPushEventUser(data) {
|
||||
return request({
|
||||
url: `${Account_BaseUrl + "/admin/shopUser/getPushEventUser"}`,
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 短信推送任务 新增
|
||||
export function pushEventPost(data, method = 'post') {
|
||||
return request({
|
||||
url: `${Market_BaseUrl + "/admin/pushEvent"}`,
|
||||
method: method,
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 短信推送任务 列表
|
||||
export function pushEventGet(params) {
|
||||
return request({
|
||||
url: `${Market_BaseUrl + "/admin/pushEvent"}`,
|
||||
method: 'get',
|
||||
params
|
||||
});
|
||||
}
|
||||
|
||||
// 短信推送任务 删除任务
|
||||
export function pushEventDel(id) {
|
||||
return request({
|
||||
url: `${Market_BaseUrl}/admin/pushEvent/${id}`,
|
||||
method: 'DELETE'
|
||||
});
|
||||
}
|
||||
|
||||
// 获取店铺短信余额明细
|
||||
export function smsMoneyDetail(params) {
|
||||
return request({
|
||||
url: `${Market_BaseUrl + "/admin/smsMoneyDetail"}`,
|
||||
method: 'get',
|
||||
params
|
||||
});
|
||||
}
|
||||
|
||||
// 霸王餐 配置信息获取
|
||||
export function freeDingGet(params) {
|
||||
return request({
|
||||
url: `${Account_BaseUrl + "/admin/freeDing"}`,
|
||||
method: 'get',
|
||||
params
|
||||
});
|
||||
}
|
||||
|
||||
// 霸王餐 修改霸王餐配置信息
|
||||
export function freeDingPut(data) {
|
||||
return request({
|
||||
url: `${Account_BaseUrl + "/admin/freeDing"}`,
|
||||
method: 'put',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 平台 模板状态/删除
|
||||
export function shopUseDelStatus(data) {
|
||||
return request({
|
||||
url: `${Market_BaseUrl + "/admin/smsTemplate/shopUse"}`,
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 配置信息获取
|
||||
export function adminSmsMoneyPage(params) {
|
||||
return request({
|
||||
url: `${Market_BaseUrl + "/admin/smsMoney/page"}`,
|
||||
method: 'get',
|
||||
params
|
||||
});
|
||||
}
|
||||
|
||||
// 变更店铺短信余额
|
||||
export function smsMoneyChange(data) {
|
||||
return request({
|
||||
url: `${Market_BaseUrl + "/admin/smsMoney/change"}`,
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 平台 获取店铺短信余额明细
|
||||
export function smsMoneyDetailQuery(data) {
|
||||
return request({
|
||||
url: `${Market_BaseUrl + "/admin/smsMoneyDetail/query"}`,
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 获取店铺短信余额
|
||||
export function smsMoneyGet() {
|
||||
return request({
|
||||
url: `${Market_BaseUrl + "/admin/smsMoney"}`,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
// 获取发送短信单价
|
||||
export function smsMoneyGetFee() {
|
||||
return request({
|
||||
url: `${System_BaseUrl + "/admin/sysParams/code/sms_fee"}`,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { BigNumber } from "bignumber.js";
|
||||
/**
|
||||
* Check if an element has a class
|
||||
* @param {HTMLElement} ele
|
||||
* @param {string} cls
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function hasClass(ele : HTMLElement, cls : string) {
|
||||
export function hasClass(ele: HTMLElement, cls: string) {
|
||||
return !!ele.className.match(new RegExp("(\\s|^)" + cls + "(\\s|$)"));
|
||||
}
|
||||
|
||||
@@ -13,7 +14,7 @@ export function hasClass(ele : HTMLElement, cls : string) {
|
||||
* @param {HTMLElement} ele
|
||||
* @param {string} cls
|
||||
*/
|
||||
export function addClass(ele : HTMLElement, cls : string) {
|
||||
export function addClass(ele: HTMLElement, cls: string) {
|
||||
if (!hasClass(ele, cls)) ele.className += " " + cls;
|
||||
}
|
||||
|
||||
@@ -22,7 +23,7 @@ export function addClass(ele : HTMLElement, cls : string) {
|
||||
* @param {HTMLElement} ele
|
||||
* @param {string} cls
|
||||
*/
|
||||
export function removeClass(ele : HTMLElement, cls : string) {
|
||||
export function removeClass(ele: HTMLElement, cls: string) {
|
||||
if (hasClass(ele, cls)) {
|
||||
const reg = new RegExp("(\\s|^)" + cls + "(\\s|$)");
|
||||
ele.className = ele.className.replace(reg, " ");
|
||||
@@ -35,7 +36,7 @@ export function removeClass(ele : HTMLElement, cls : string) {
|
||||
* @param {string} path
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export function isExternal(path : string) {
|
||||
export function isExternal(path: string) {
|
||||
const isExternal = /^(https?:|http?:|mailto:|tel:)/.test(path);
|
||||
return isExternal;
|
||||
}
|
||||
@@ -46,7 +47,7 @@ export function isExternal(path : string) {
|
||||
* @param growthRate
|
||||
* @returns
|
||||
*/
|
||||
export function formatGrowthRate(growthRate : number) {
|
||||
export function formatGrowthRate(growthRate: number) {
|
||||
if (growthRate === 0) {
|
||||
return "-";
|
||||
}
|
||||
@@ -62,7 +63,7 @@ export function formatGrowthRate(growthRate : number) {
|
||||
* @param {string} cFormat
|
||||
* @returns {string}
|
||||
*/
|
||||
export function parseTime(time : string | number | Date | null, cFormat : string | undefined) {
|
||||
export function parseTime(time: string | number | Date | null, cFormat: string | undefined) {
|
||||
if (arguments.length === 0) {
|
||||
return null;
|
||||
}
|
||||
@@ -90,7 +91,7 @@ export function parseTime(time : string | number | Date | null, cFormat : string
|
||||
s: date.getSeconds(),
|
||||
a: date.getDay()
|
||||
};
|
||||
const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key : keyof typeof formatObj) : string => {
|
||||
const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key: keyof typeof formatObj): string => {
|
||||
let value = formatObj[key];
|
||||
// Note: getDay() returns 0 on Sunday
|
||||
if (key === "a") {
|
||||
@@ -105,7 +106,7 @@ export function parseTime(time : string | number | Date | null, cFormat : string
|
||||
}
|
||||
|
||||
// 下载文件
|
||||
export function downloadFile(obj : BlobPart, name : string, suffix : string, useUnix = true) {
|
||||
export function downloadFile(obj: BlobPart, name: string, suffix: string, useUnix = true) {
|
||||
const url = window.URL.createObjectURL(new Blob([obj]));
|
||||
const link = document.createElement("a");
|
||||
link.style.display = "none";
|
||||
@@ -133,9 +134,9 @@ export function isSyncStatus() {
|
||||
/**
|
||||
* 判断是否有某权限
|
||||
*/
|
||||
export function hasPermission(params : any) {
|
||||
export function hasPermission(params: any) {
|
||||
let $PermissionObj = JSON.parse(localStorage.getItem("permission") || '[]')
|
||||
const obj = $PermissionObj.find((v : any) => v == params || v == params)
|
||||
const obj = $PermissionObj.find((v: any) => v == params || v == params)
|
||||
if (obj) {
|
||||
return obj
|
||||
}
|
||||
@@ -232,4 +233,30 @@ export function convertTimeToDate(timeStr, options = {}) {
|
||||
|
||||
// 5. 按指定格式返回日期字符串
|
||||
return dateObj.format(format);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 乘法计算并格式化结果
|
||||
* @param {string|number} num1 - 第一个乘数
|
||||
* @param {string|number} num2 - 第二个乘数
|
||||
* @returns {string} 保留两位小数的结果(不四舍五入,补零)
|
||||
*/
|
||||
export const multiplyAndFormat = (num1: any, num2: any = 1): string => {
|
||||
try {
|
||||
// 转换为BigNumber(使用字符串构造避免精度问题)
|
||||
const bigNum1 = new BigNumber(num1.toString());
|
||||
const bigNum2 = new BigNumber(num2.toString());
|
||||
|
||||
// 1. 乘法计算
|
||||
const product = bigNum1.multipliedBy(bigNum2);
|
||||
|
||||
// 2. 截断到两位小数(不四舍五入)
|
||||
const truncated = product.decimalPlaces(2, BigNumber.ROUND_DOWN);
|
||||
|
||||
// 3. 格式化保留两位小数(补零)
|
||||
return truncated.toFixed(2);
|
||||
} catch (error) {
|
||||
console.error('计算错误:', error);
|
||||
return '0.00'; // 出错时返回默认值
|
||||
}
|
||||
};
|
||||
@@ -1,13 +1,7 @@
|
||||
<template>
|
||||
<div class="login" :style="'background-image:url(' + Background + ');'">
|
||||
<el-form
|
||||
ref="loginForm"
|
||||
:model="state.loginForm"
|
||||
:rules="state.loginRules"
|
||||
label-position="left"
|
||||
label-width="0px"
|
||||
class="login-form"
|
||||
>
|
||||
<el-form ref="loginForm" :model="state.loginForm" :rules="state.loginRules" label-position="left" label-width="0px"
|
||||
class="login-form">
|
||||
<h3 class="title">银收客后台管理</h3>
|
||||
<el-form-item>
|
||||
<el-radio-group v-model="state.loginForm.loginType">
|
||||
@@ -16,39 +10,19 @@
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item prop="username">
|
||||
<el-input
|
||||
v-model="state.loginForm.username"
|
||||
type="text"
|
||||
auto-complete="off"
|
||||
placeholder="商户号"
|
||||
></el-input>
|
||||
<el-input v-model="state.loginForm.username" type="text" auto-complete="off" placeholder="商户号"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="staffUserName" v-if="state.loginForm.loginType == 1">
|
||||
<el-input
|
||||
v-model="state.loginForm.staffUserName"
|
||||
type="text"
|
||||
auto-complete="off"
|
||||
placeholder="账号"
|
||||
></el-input>
|
||||
<el-input v-model="state.loginForm.staffUserName" type="text" auto-complete="off" placeholder="账号"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="password">
|
||||
<el-input
|
||||
v-model="state.loginForm.password"
|
||||
type="password"
|
||||
auto-complete="off"
|
||||
placeholder="密码"
|
||||
@keyup.enter="handleLogin"
|
||||
></el-input>
|
||||
<el-input v-model="state.loginForm.password" type="password" auto-complete="off" placeholder="密码"
|
||||
@keyup.enter="handleLogin"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="code">
|
||||
<div class="code_wrap">
|
||||
<el-input
|
||||
v-model="state.loginForm.code"
|
||||
auto-complete="off"
|
||||
placeholder="验证码"
|
||||
style="width: 63%"
|
||||
@keyup.enter="handleLogin"
|
||||
></el-input>
|
||||
<el-input v-model="state.loginForm.code" auto-complete="off" placeholder="验证码" style="width: 63%"
|
||||
@keyup.enter="handleLogin"></el-input>
|
||||
<div class="login-code">
|
||||
<img :src="state.codeUrl" @click="getCode" />
|
||||
</div>
|
||||
@@ -56,13 +30,8 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item style="width: 100%">
|
||||
<el-button
|
||||
:loading="state.loading"
|
||||
size="default"
|
||||
type="primary"
|
||||
style="width: 100%"
|
||||
@click.prevent="handleLogin"
|
||||
>
|
||||
<el-button :loading="state.loading" size="default" type="primary" style="width: 100%"
|
||||
@click.prevent="handleLogin">
|
||||
<span v-if="!state.loading">登 录</span>
|
||||
<span v-else>登 录 中...</span>
|
||||
</el-button>
|
||||
@@ -94,9 +63,9 @@ const state = reactive({
|
||||
cookiePass: "",
|
||||
loginForm: {
|
||||
username: "",
|
||||
password: "",
|
||||
password: process.env.NODE_ENV === "development" ? `czg${new Date().getHours().toString().padStart(2, '0')}${new Date().getMinutes().toString().padStart(2, '0')}` : "",
|
||||
// rememberMe: false,
|
||||
code: "",
|
||||
code: process.env.NODE_ENV === "development" ? "666666" : "",
|
||||
uuid: "",
|
||||
staffUserName: "",
|
||||
loginType: 0,
|
||||
|
||||
@@ -6,15 +6,15 @@
|
||||
<div style="padding-top: 14px;">
|
||||
<el-tabs v-model="tabsValue">
|
||||
<el-tab-pane label="基础明细" :name="1">
|
||||
<el-form :model="form" :rules="rules" label-position="left" label-width="100px">
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-position="left" label-width="100px">
|
||||
<el-form-item label="可用门店">
|
||||
<el-radio-group v-model="form.useType">
|
||||
<el-radio label="全部门店" value="all"></el-radio>
|
||||
<el-radio label="指定门店可用" value="part"></el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="选择门店" v-if="form.shopType == 'part'">
|
||||
<selectBranchs ref="selectBranchsRef" @success="successShop" />
|
||||
<el-form-item label="选择门店" prop="shopIdList" v-if="form.useType == 'part'">
|
||||
<selectBranchs v-model="form.shopIdList" />
|
||||
</el-form-item>
|
||||
<el-form-item label="适用用户">
|
||||
<el-radio-group v-model="form.applicableUser">
|
||||
@@ -29,26 +29,28 @@
|
||||
<el-radio label="固定金额" value="fix"></el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="阶梯设置" prop="">
|
||||
<el-form-item label="阶梯设置" prop="stepValidate">
|
||||
<div class="row">
|
||||
<div class="item border" v-for="(item, index) in callbackList" :key="index">
|
||||
<div class="item border" v-for="(item, index) in form.cashbackStepList" :key="index">
|
||||
<div class="row_title">
|
||||
<span>{{ `第${index + 1}组` }}</span>
|
||||
<el-icon @click="callbackList.splice(index, 1)">
|
||||
<el-icon @click="form.cashbackStepList.splice(index, 1)">
|
||||
<Delete />
|
||||
</el-icon>
|
||||
</div>
|
||||
<div class="ipt">
|
||||
<div class="ipt_row">
|
||||
<span>返现门槛</span>
|
||||
<el-input placeholder="请输入返现门槛" v-model="item.amount">
|
||||
<el-input placeholder="请输入返现门槛" :maxlength="8" v-model="item.amount"
|
||||
@input="e => item.amount = filterNumberInput(e)">
|
||||
<template #append>元</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="ipt_row">
|
||||
<span>{{ form.cashbackType == 'percentage' ? '返现比例' : '返现金额' }}</span>
|
||||
<el-input :placeholder="form.cashbackType == 'percentage' ? '请输入返现比例' : '请输入返现金额'"
|
||||
v-model="item.cashbackAmount">
|
||||
<el-input :maxlength="8"
|
||||
:placeholder="form.cashbackType == 'percentage' ? '请输入返现比例' : '请输入返现金额'"
|
||||
v-model="item.cashbackAmount" @input="e => item.cashbackAmount = filterNumberInput(e)">
|
||||
<template #append>
|
||||
{{ form.cashbackType == 'percentage' ? '%' : '元' }}
|
||||
</template>
|
||||
@@ -56,19 +58,19 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="item" v-if="shopInfo.isHeadShop == 1 && shopInfo.shopType != 'only'">
|
||||
<el-button type="primary" @click="addCashBackItem">添加阶梯</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary">保存</el-button>
|
||||
<el-button>取消</el-button>
|
||||
<el-form-item style="margin-top: 50px;" v-if="shopInfo.isHeadShop == 1 && shopInfo.shopType != 'only'">
|
||||
<el-button type="primary" @click="submitHandle">保存</el-button>
|
||||
<el-button @click="router.back()">取消</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="返现明细" :name="2">
|
||||
<div>312321</div>
|
||||
<record />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
@@ -78,12 +80,17 @@
|
||||
|
||||
<script setup>
|
||||
import HeaderCard from "../components/headerCard.vue";
|
||||
import record from "./record.vue";
|
||||
import selectBranchs from "../components/selectBranchs.vue";
|
||||
import { ref } from 'vue'
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useRouter } from "vue-router";
|
||||
import { consumeCashback, consumeCashbackPost } from '@/api/coupon/index.js'
|
||||
import { filterNumberInput } from '@/utils'
|
||||
|
||||
const selectBranchsRef = ref(null)
|
||||
const router = useRouter();
|
||||
const tabsValue = ref(1)
|
||||
|
||||
const formRef = ref(null)
|
||||
const shopInfo = ref(JSON.parse(localStorage.getItem('userInfo')))
|
||||
const form = ref({
|
||||
useType: 'all', // all 全部可用 part部分门店可用
|
||||
shopIdList: [], // 门店列表
|
||||
@@ -94,23 +101,88 @@ const form = ref({
|
||||
})
|
||||
|
||||
const rules = ref({
|
||||
shopIdList: [
|
||||
{
|
||||
required: true,
|
||||
validator: (rule, value, callback) => {
|
||||
if (form.value.shopIdList.length <= 0) {
|
||||
callback(new Error('请选择门店'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
},
|
||||
trigger: 'change'
|
||||
}
|
||||
],
|
||||
stepValidate: [
|
||||
{
|
||||
required: true,
|
||||
validator: (rule, value, callback) => {
|
||||
if (form.value.cashbackStepList.length <= 0) {
|
||||
callback(new Error('请添加阶梯'))
|
||||
return
|
||||
}
|
||||
|
||||
let flag = true;
|
||||
form.value.cashbackStepList.map(item => {
|
||||
if (!item.amount || !item.cashbackAmount || item.cashbackAmount > item.amount) {
|
||||
flag = false
|
||||
}
|
||||
})
|
||||
|
||||
if (!flag) {
|
||||
callback(new Error('输入有误,请检查返现金额是不是大于返现门槛'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
},
|
||||
trigger: 'change'
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
// 已选泽的店铺
|
||||
function successShop(e) {
|
||||
console.log('successShop===', e);
|
||||
form.value.shops = e
|
||||
}
|
||||
|
||||
// 添加返现阶梯
|
||||
const callbackList = ref([])
|
||||
function addCashBackItem() {
|
||||
callbackList.value.push({
|
||||
form.value.cashbackStepList.push({
|
||||
amount: '',
|
||||
cashbackAmount: ''
|
||||
})
|
||||
}
|
||||
|
||||
// 提交保存
|
||||
function submitHandle() {
|
||||
formRef.value.validate(async (valid) => {
|
||||
try {
|
||||
if (valid) {
|
||||
await consumeCashbackPost(form.value);
|
||||
ElNotification({
|
||||
title: "注意",
|
||||
message: "保存成功",
|
||||
type: "success",
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 配置信息获取
|
||||
async function consumeCashbackAjax() {
|
||||
try {
|
||||
const res = await consumeCashback()
|
||||
if (res.cashbackStepList == null) {
|
||||
res.cashbackStepList = []
|
||||
}
|
||||
form.value = res
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
consumeCashbackAjax()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@@ -126,9 +198,12 @@ function addCashBackItem() {
|
||||
|
||||
.row {
|
||||
.item {
|
||||
margin-bottom: 24px;
|
||||
position: relative;
|
||||
|
||||
&:not(:first-child) {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.row_title {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
|
||||
183
src/views/marketing_center/cashback/record.vue
Normal file
183
src/views/marketing_center/cashback/record.vue
Normal file
@@ -0,0 +1,183 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form :model="queryForm" inline>
|
||||
<el-form-item>
|
||||
<selectBranchs :multiple="false" v-model="queryForm.shopId" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-date-picker style="width: 300px" v-model="times" type="daterange" range-separator="至"
|
||||
start-placeholder="开始日期" end-placeholder="结束日期" value-format="YYYY-MM-DD"
|
||||
@change="selectTimeChange"></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item label="" prop="">
|
||||
<el-input placeholder="请输入关键词" v-model="queryForm.key">
|
||||
<template #prepend>名称</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="" prop="">
|
||||
<el-button type="primary" @click="searchHandle">搜索</el-button>
|
||||
<el-button @click="resetSearch">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div class="data_show">
|
||||
<div class="data_list">
|
||||
<div class="item">
|
||||
<div class="title">
|
||||
返现总金额
|
||||
</div>
|
||||
<div class="num">
|
||||
¥{{ tableData.totalAmount }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table" style="width: 100%;">
|
||||
<el-table :data="tableData.list" stripe border v-loading="tableData.loading" height="500px">
|
||||
<el-table-column label="ID" prop="id"></el-table-column>
|
||||
<el-table-column label="关联订单号" prop="orderNo"></el-table-column>
|
||||
<el-table-column label="消费门店" prop="shopName"></el-table-column>
|
||||
<el-table-column label="用户信息" prop="nickName">
|
||||
<template #default="scope">
|
||||
<el-link type="primary" @click="router.push({
|
||||
path: '/user/index',
|
||||
query: {
|
||||
phone: scope.row.phone
|
||||
}
|
||||
})">
|
||||
<div class="column">
|
||||
<span>{{ scope.row.nickName }}</span>
|
||||
<span>{{ scope.row.phone }}</span>
|
||||
</div>
|
||||
</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="支付金额(元)" prop="amount"></el-table-column>
|
||||
<el-table-column label="返现金额(元)" prop="cashbackAmount"></el-table-column>
|
||||
<el-table-column label="创建时间" prop="createTime"></el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<div class="row" style="margin-top: 14px;">
|
||||
<el-pagination v-model:current-page="tableData.page" v-model:page-size="tableData.size"
|
||||
:page-sizes="[100, 200, 300, 400]" background layout="total, sizes, prev, pager, next, jumper"
|
||||
:page-count="tableData.total" @size-change="handleSizeChange" @current-change="handleCurrentChange" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import dayjs from 'dayjs'
|
||||
import { ref, onMounted } from 'vue'
|
||||
import selectBranchs from '../components/selectBranchs.vue'
|
||||
import { consumeCashbackRecord } from '@/api/coupon/index.js'
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
const router = useRouter();
|
||||
const times = ref([])
|
||||
const queryForm = ref({
|
||||
shopId: '',
|
||||
key: '',
|
||||
startTime: '',
|
||||
endTime: ''
|
||||
})
|
||||
|
||||
const tableData = reactive({
|
||||
totalAmount: 0,
|
||||
loading: false,
|
||||
page: 1,
|
||||
size: 10,
|
||||
list: []
|
||||
})
|
||||
|
||||
// 搜索
|
||||
function searchHandle() {
|
||||
tableData.page = 1;
|
||||
consumeCashbackRecordAjax()
|
||||
}
|
||||
|
||||
// 重置搜索
|
||||
function resetSearch() {
|
||||
queryForm.value.shopId = ''
|
||||
queryForm.value.key = ''
|
||||
queryForm.value.startTime = ''
|
||||
queryForm.value.endTime = ''
|
||||
|
||||
times.value = []
|
||||
|
||||
searchHandle()
|
||||
}
|
||||
|
||||
// 选择日期
|
||||
function selectTimeChange(e) {
|
||||
console.log(e);
|
||||
queryForm.value.startTime = dayjs(e[0]).format('YYYY-MM-DD 00:00:00')
|
||||
queryForm.value.endTime = dayjs(e[1]).format('YYYY-MM-DD 23:59:59')
|
||||
}
|
||||
|
||||
// 分页大小发生变化
|
||||
function handleSizeChange(e) {
|
||||
tableData.pageSize = e;
|
||||
consumeCashbackRecordAjax();
|
||||
}
|
||||
|
||||
// 分页发生变化
|
||||
function handleCurrentChange(e) {
|
||||
tableData.page = e;
|
||||
consumeCashbackRecordAjax();
|
||||
}
|
||||
|
||||
// 获取消费返现记录
|
||||
async function consumeCashbackRecordAjax(params) {
|
||||
try {
|
||||
tableData.loading = true
|
||||
const res = await consumeCashbackRecord({
|
||||
...queryForm.value,
|
||||
page: 1,
|
||||
size: 10,
|
||||
total: 0
|
||||
})
|
||||
tableData.totalAmount = res.totalAmount
|
||||
tableData.list = res.records
|
||||
tableData.total = +res.totalRow
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
tableData.loading = false
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
consumeCashbackRecordAjax()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.data_show {
|
||||
padding: 0 0 14px;
|
||||
|
||||
.data_list {
|
||||
display: flex;
|
||||
|
||||
.item {
|
||||
width: 200px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
padding: 14px;
|
||||
|
||||
.title {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.num {
|
||||
font-size: 24px;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
line-height: 16px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,49 +1,22 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
:title="titleOptions.title"
|
||||
width="80%"
|
||||
top="4vh"
|
||||
@closed="closedReset"
|
||||
>
|
||||
<el-dialog v-model="dialogVisible" :title="titleOptions.title" width="80%" top="4vh" @closed="closedReset">
|
||||
<div class="scroll" ref="scrollRef">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="formRules"
|
||||
label-width="160px"
|
||||
class="dialog-form"
|
||||
>
|
||||
<el-form ref="formRef" :model="form" :rules="formRules" label-width="160px" class="dialog-form">
|
||||
<el-form-item :label="titleOptions.name" prop="title">
|
||||
<el-input
|
||||
v-model="form.title"
|
||||
:maxlength="20"
|
||||
placeholder="请输入优惠券名称"
|
||||
style="width: 300px"
|
||||
/>
|
||||
<el-input v-model="form.title" :maxlength="20" placeholder="请输入优惠券名称" style="width: 300px" />
|
||||
</el-form-item>
|
||||
<div v-if="form.couponType == 1">
|
||||
<el-form-item label="使用门槛" prop="fullAmount">
|
||||
<div class="center">
|
||||
<el-input
|
||||
v-model="form.fullAmount"
|
||||
placeholder="请输入使用门槛"
|
||||
style="width: 240px"
|
||||
input-style="text-align: center;"
|
||||
:maxlength="8"
|
||||
@input="(e) => (form.fullAmount = filterNumberInput(e))"
|
||||
>
|
||||
<el-input v-model="form.fullAmount" placeholder="请输入使用门槛" style="width: 240px"
|
||||
input-style="text-align: center;" :maxlength="8"
|
||||
@input="(e) => (form.fullAmount = filterNumberInput(e))">
|
||||
<template #prepend>满</template>
|
||||
<template #append>元</template>
|
||||
</el-input>
|
||||
<el-input
|
||||
v-model="form.discountAmount"
|
||||
placeholder="请输入满减金额"
|
||||
style="width: 240px"
|
||||
input-style="text-align: center;"
|
||||
:maxlength="8"
|
||||
@input="(e) => (form.discountAmount = filterNumberInput(e))"
|
||||
>
|
||||
<el-input v-model="form.discountAmount" placeholder="请输入满减金额" style="width: 240px"
|
||||
input-style="text-align: center;" :maxlength="8"
|
||||
@input="(e) => (form.discountAmount = filterNumberInput(e))">
|
||||
<template #prepend>减</template>
|
||||
<template #append>元</template>
|
||||
</el-input>
|
||||
@@ -53,14 +26,9 @@
|
||||
<div v-if="form.couponType == 2">
|
||||
<el-form-item label="使用门槛" prop="fullAmount2">
|
||||
<div class="center">
|
||||
<el-input
|
||||
v-model="form.fullAmount"
|
||||
placeholder="请输入使用门槛"
|
||||
style="width: 200px"
|
||||
input-style="text-align: center;"
|
||||
:maxlength="8"
|
||||
@input="(e) => (form.fullAmount = filterNumberInput(e))"
|
||||
>
|
||||
<el-input v-model="form.fullAmount" placeholder="请输入使用门槛" style="width: 200px"
|
||||
input-style="text-align: center;" :maxlength="8"
|
||||
@input="(e) => (form.fullAmount = filterNumberInput(e))">
|
||||
<template #prepend>满</template>
|
||||
<template #append>可用</template>
|
||||
</el-input>
|
||||
@@ -73,17 +41,9 @@
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item prop="goodsType" v-if="goodsType == 2">
|
||||
<el-cascader
|
||||
v-model="goodsTypeCascaderValue"
|
||||
:options="goodsList"
|
||||
:props="cascaderProps"
|
||||
:show-all-levels="false"
|
||||
:max-collapse-tags="3"
|
||||
collapse-tags
|
||||
clearable
|
||||
style="width: 300px"
|
||||
@change="selectFoodsConfirm"
|
||||
></el-cascader>
|
||||
<el-cascader v-model="goodsTypeCascaderValue" :options="goodsList" :props="cascaderProps"
|
||||
:show-all-levels="false" :max-collapse-tags="3" collapse-tags clearable style="width: 300px"
|
||||
@change="selectFoodsConfirm"></el-cascader>
|
||||
</el-form-item>
|
||||
<el-form-item label="使用规则">
|
||||
<el-radio-group v-model="form.useRule">
|
||||
@@ -92,33 +52,20 @@
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="可抵扣商品件数" prop="discountNum">
|
||||
<el-input
|
||||
v-model="form.discountNum"
|
||||
placeholder="请输入可抵扣商品件数"
|
||||
style="width: 200px"
|
||||
@input="discountNumInput"
|
||||
/>
|
||||
<el-input v-model="form.discountNum" placeholder="请输入可抵扣商品件数" style="width: 200px"
|
||||
@input="discountNumInput" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div v-if="form.couponType == 3">
|
||||
<el-form-item label="折扣" prop="discountRate">
|
||||
<el-input
|
||||
v-model="form.discountRate"
|
||||
placeholder="输入折扣(%)"
|
||||
style="width: 200px"
|
||||
@input="discountRateInput"
|
||||
/>
|
||||
<el-input v-model="form.discountRate" placeholder="输入折扣(%)" style="width: 200px"
|
||||
@input="discountRateInput" />
|
||||
</el-form-item>
|
||||
<el-form-item label="使用门槛" prop="fullAmount2">
|
||||
<div class="center">
|
||||
<el-input
|
||||
v-model="form.fullAmount"
|
||||
placeholder="请输入使用门槛"
|
||||
style="width: 300px"
|
||||
input-style="text-align: center;"
|
||||
:maxlength="8"
|
||||
@input="(e) => (form.fullAmount = filterNumberInput(e))"
|
||||
>
|
||||
<el-input v-model="form.fullAmount" placeholder="请输入使用门槛" style="width: 300px"
|
||||
input-style="text-align: center;" :maxlength="8"
|
||||
@input="(e) => (form.fullAmount = filterNumberInput(e))">
|
||||
<template #prepend>满</template>
|
||||
<template #append>可用</template>
|
||||
</el-input>
|
||||
@@ -126,13 +73,8 @@
|
||||
</el-form-item>
|
||||
<el-form-item label="可抵扣最大金额" prop="maxDiscountAmount">
|
||||
<div class="center">
|
||||
<el-input
|
||||
v-model="form.maxDiscountAmount"
|
||||
placeholder="请输入金额"
|
||||
style="width: 200px"
|
||||
:maxlength="8"
|
||||
@input="(e) => (form.maxDiscountAmount = filterNumberInput(e))"
|
||||
>
|
||||
<el-input v-model="form.maxDiscountAmount" placeholder="请输入金额" style="width: 200px" :maxlength="8"
|
||||
@input="(e) => (form.maxDiscountAmount = filterNumberInput(e))">
|
||||
<template #append>可用</template>
|
||||
</el-input>
|
||||
</div>
|
||||
@@ -146,17 +88,9 @@
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item prop="goodsType" v-if="goodsType == 2">
|
||||
<el-cascader
|
||||
v-model="goodsTypeCascaderValue"
|
||||
:options="goodsList"
|
||||
:props="cascaderProps"
|
||||
:show-all-levels="false"
|
||||
:max-collapse-tags="3"
|
||||
collapse-tags
|
||||
clearable
|
||||
style="width: 300px"
|
||||
@change="selectFoodsConfirm"
|
||||
></el-cascader>
|
||||
<el-cascader v-model="goodsTypeCascaderValue" :options="goodsList" :props="cascaderProps"
|
||||
:show-all-levels="false" :max-collapse-tags="3" collapse-tags clearable style="width: 300px"
|
||||
@change="selectFoodsConfirm"></el-cascader>
|
||||
</el-form-item>
|
||||
<el-form-item label="使用规则">
|
||||
<el-radio-group v-model="form.useRule">
|
||||
@@ -167,11 +101,7 @@
|
||||
</div>
|
||||
<div v-if="form.couponType == 6"></div>
|
||||
<div class="title">指定设置</div>
|
||||
<el-form-item
|
||||
label="选择门店"
|
||||
prop="useShopType"
|
||||
v-if="shopInfo.isHeadShop && shopInfo.shopType != 'only'"
|
||||
>
|
||||
<el-form-item label="选择门店" prop="useShopType" v-if="shopInfo.isHeadShop && shopInfo.shopType != 'only'">
|
||||
<el-radio-group v-model="form.useShopType">
|
||||
<el-radio label="仅本店可用" value="only"></el-radio>
|
||||
<el-radio label="全部门店" value="all"></el-radio>
|
||||
@@ -179,20 +109,8 @@
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="选择门店" v-if="form.useShopType == 'custom'">
|
||||
<el-select
|
||||
v-model="shops"
|
||||
multiple
|
||||
clearable
|
||||
placeholder="请选择门店"
|
||||
@change="shopsChange"
|
||||
style="width: 300px"
|
||||
>
|
||||
<el-option
|
||||
:label="item.shopName"
|
||||
:value="item.id"
|
||||
v-for="item in branchList"
|
||||
:key="item.id"
|
||||
></el-option>
|
||||
<el-select v-model="shops" multiple clearable placeholder="请选择门店" @change="shopsChange" style="width: 300px">
|
||||
<el-option :label="item.shopName" :value="item.id" v-for="item in branchList" :key="item.id"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<div v-if="form.couponType != 2 && form.couponType != 4 && form.couponType != 6">
|
||||
@@ -203,25 +121,17 @@
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="goodsType == 2">
|
||||
<el-cascader
|
||||
v-model="goodsTypeCascaderValue"
|
||||
:options="goodsList"
|
||||
:props="cascaderProps"
|
||||
:show-all-levels="false"
|
||||
:max-collapse-tags="3"
|
||||
collapse-tags
|
||||
clearable
|
||||
style="width: 300px"
|
||||
@change="selectFoodsConfirm"
|
||||
></el-cascader>
|
||||
<el-cascader v-model="goodsTypeCascaderValue" :options="goodsList" :props="cascaderProps"
|
||||
:show-all-levels="false" :max-collapse-tags="3" collapse-tags clearable style="width: 300px"
|
||||
@change="selectFoodsConfirm"></el-cascader>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<el-form-item label="可使用类型" prop="useType">
|
||||
<el-checkbox-group v-model="form.useType">
|
||||
<el-checkbox value="dine" label="堂食" />
|
||||
<el-checkbox value="pickup" label="自取" />
|
||||
<el-checkbox value="deliv" label="配送" />
|
||||
<el-checkbox value="express" label="快递" />
|
||||
<el-checkbox value="dine-in" label="堂食" />
|
||||
<el-checkbox value="take-out" label="外带" />
|
||||
<el-checkbox value="take-away" label="外卖" />
|
||||
<el-checkbox value="post" label="配送" />
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
<div class="title">时效设置</div>
|
||||
@@ -232,52 +142,24 @@
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="有效期" prop="validDays">
|
||||
<el-input
|
||||
v-model="form.validDays"
|
||||
placeholder="请输入有效期"
|
||||
style="width: 200px"
|
||||
:maxlength="5"
|
||||
v-if="form.validType == 'fixed'"
|
||||
input-style="text-align: center;"
|
||||
@input="validDaysInput"
|
||||
>
|
||||
<el-input v-model="form.validDays" placeholder="请输入有效期" style="width: 200px" :maxlength="5"
|
||||
v-if="form.validType == 'fixed'" input-style="text-align: center;" @input="validDaysInput">
|
||||
<template #append>天</template>
|
||||
</el-input>
|
||||
<div style="width: 200px">
|
||||
<el-date-picker
|
||||
v-model="validityScope"
|
||||
type="daterange"
|
||||
range-separator="至"
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="结束时间"
|
||||
v-if="form.validType == 'custom'"
|
||||
@change="validityScopeChange"
|
||||
/>
|
||||
<el-date-picker v-model="validityScope" type="daterange" range-separator="至" start-placeholder="开始时间"
|
||||
end-placeholder="结束时间" v-if="form.validType == 'custom'" @change="validityScopeChange" />
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
label="隔天生效"
|
||||
prop="daysToTakeEffect"
|
||||
v-if="form.validType == 'fixed'"
|
||||
>
|
||||
<el-form-item label="隔天生效" prop="daysToTakeEffect" v-if="form.validType == 'fixed'">
|
||||
<div class="center">
|
||||
<el-input
|
||||
v-model="form.daysToTakeEffect"
|
||||
placeholder="请输入隔天生效日期"
|
||||
style="width: 300px"
|
||||
input-style="text-align: center;"
|
||||
:maxlength="5"
|
||||
@input="daysToTakeEffectInput"
|
||||
>
|
||||
<el-input v-model="form.daysToTakeEffect" placeholder="请输入隔天生效日期" style="width: 300px"
|
||||
input-style="text-align: center;" :maxlength="5" @input="daysToTakeEffectInput">
|
||||
<template #prepend>隔</template>
|
||||
<template #append>天生效</template>
|
||||
</el-input>
|
||||
<el-tooltip
|
||||
class="box-item"
|
||||
effect="dark"
|
||||
:content="`领取后${form.daysToTakeEffect}天后的0点0分生效`"
|
||||
placement="top-start"
|
||||
>
|
||||
<el-tooltip class="box-item" effect="dark" :content="`领取后${form.daysToTakeEffect}天后的0点0分生效`"
|
||||
placement="top-start">
|
||||
<el-icon size="18">
|
||||
<QuestionFilled />
|
||||
</el-icon>
|
||||
@@ -303,14 +185,8 @@
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.useTimeType == 'custom'" prop="useTimeType">
|
||||
<div style="width: 200px">
|
||||
<el-time-picker
|
||||
v-model="useTimeScope"
|
||||
is-range
|
||||
range-separator="至"
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="结束时间"
|
||||
@change="useTimeScopeChange"
|
||||
/>
|
||||
<el-time-picker v-model="useTimeScope" is-range range-separator="至" start-placeholder="开始时间"
|
||||
end-placeholder="结束时间" @change="useTimeScopeChange" />
|
||||
</div>
|
||||
</el-form-item>
|
||||
<div class="title">发放设置</div>
|
||||
@@ -334,14 +210,8 @@
|
||||
<span>关闭则为无限制</span>
|
||||
</div>
|
||||
<div v-if="infiniteGiveNum" style="margin-top: 10px">
|
||||
<el-input
|
||||
v-model="form.giveNum"
|
||||
placeholder="请输入总发放数量"
|
||||
style="width: 200px"
|
||||
input-style="text-align: center;"
|
||||
:maxlength="5"
|
||||
@input="giveNumInput"
|
||||
>
|
||||
<el-input v-model="form.giveNum" placeholder="请输入总发放数量" style="width: 200px"
|
||||
input-style="text-align: center;" :maxlength="5" @input="giveNumInput">
|
||||
<template #append>张</template>
|
||||
</el-input>
|
||||
</div>
|
||||
@@ -356,24 +226,15 @@
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="每人限领量" prop="getLimit">
|
||||
<el-input
|
||||
v-model="form.getLimit"
|
||||
placeholder="请输入每人限领量"
|
||||
style="width: 200px"
|
||||
input-style="text-align: center;"
|
||||
:maxlength="5"
|
||||
@input="getLimitInput"
|
||||
>
|
||||
<el-input v-model="form.getLimit" placeholder="请输入每人限领量" style="width: 200px"
|
||||
input-style="text-align: center;" :maxlength="5" @input="getLimitInput">
|
||||
<template #append>张</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="每人每日使用限量">
|
||||
<div class="column">
|
||||
<div class="center">
|
||||
<el-radio-group
|
||||
v-model="infiniteUseLimit"
|
||||
@change="infiniteUseLimitChange"
|
||||
>
|
||||
<el-radio-group v-model="infiniteUseLimit" @change="infiniteUseLimitChange">
|
||||
<el-radio label="无限制" :value="true"></el-radio>
|
||||
<el-radio label="每日限用" :value="false"></el-radio>
|
||||
</el-radio-group>
|
||||
@@ -381,14 +242,8 @@
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="限用数量" v-if="!infiniteUseLimit" prop="useLimit">
|
||||
<el-input
|
||||
v-model="form.useLimit"
|
||||
placeholder="需小于或等于每人限领量"
|
||||
style="width: 255px"
|
||||
input-style="text-align: center;"
|
||||
:maxlength="5"
|
||||
@input="useLimitInput"
|
||||
>
|
||||
<el-input v-model="form.useLimit" placeholder="需小于或等于每人限领量" style="width: 255px"
|
||||
input-style="text-align: center;" :maxlength="5" @input="useLimitInput">
|
||||
<template #append>张/每人1天</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
@@ -396,11 +251,7 @@
|
||||
<el-form-item label="与限时折扣同享">
|
||||
<div class="column">
|
||||
<div class="center">
|
||||
<el-switch
|
||||
v-model="form.discountShare"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
/>
|
||||
<el-switch v-model="form.discountShare" :active-value="1" :inactive-value="0" />
|
||||
<span>开启后,计算门槛时将会计入已折扣的商品</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -408,11 +259,7 @@
|
||||
<el-form-item label="与会员价/会员折扣同享">
|
||||
<div class="column">
|
||||
<div class="center">
|
||||
<el-switch
|
||||
v-model="form.vipPriceShare"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
/>
|
||||
<el-switch v-model="form.vipPriceShare" :active-value="1" :inactive-value="0" />
|
||||
<span>开启后,计算门槛时将会计入已享受会员价/会员折扣的商品</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -420,11 +267,7 @@
|
||||
<el-form-item label="与其他优惠券同享" v-if="form.couponType == 2">
|
||||
<div class="column">
|
||||
<div class="center">
|
||||
<el-switch
|
||||
v-model="form.otherCouponShare"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
/>
|
||||
<el-switch v-model="form.otherCouponShare" :active-value="1" :inactive-value="0" />
|
||||
<span>开启后,可与其他优惠券同时使用</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -433,13 +276,8 @@
|
||||
<el-form-item label-width="0">
|
||||
<div class="column">
|
||||
<div class="item">
|
||||
<el-input
|
||||
type="textarea"
|
||||
:rows="4"
|
||||
maxlength="250"
|
||||
v-model="form.ruleDetails"
|
||||
placeholder="填写内容"
|
||||
></el-input>
|
||||
<el-input type="textarea" :rows="4" maxlength="250" v-model="form.ruleDetails"
|
||||
placeholder="填写内容"></el-input>
|
||||
</div>
|
||||
<div class="item textarea-num">
|
||||
{{ form.ruleDetails.length }}/250字内,单文本
|
||||
@@ -449,10 +287,7 @@
|
||||
</el-form>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div
|
||||
class="dialog-footer"
|
||||
v-if="(shopInfo.isHeadShop && shopInfo.shopType != 'only') || !form.syncId"
|
||||
>
|
||||
<div class="dialog-footer" v-if="(shopInfo.isHeadShop && shopInfo.shopType != 'only') || !form.syncId">
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" :loading="loading" @click="submitHandle">确 定</el-button>
|
||||
</div>
|
||||
@@ -594,7 +429,7 @@ const form = ref({
|
||||
useShops: "", // id字符串拼接
|
||||
goodsType: 1, // 1=全部商品参与计算门槛 2=部分商品参与计算门槛
|
||||
foods: "", // 指定门槛商品 id拼接
|
||||
useType: ["dine"], // 可使用类型:dine堂食/pickup自取/deliv配送/express快递
|
||||
useType: ["dine-in"], // 堂食 dine-in 外带 take-out 外卖 take-away 配送 post
|
||||
validType: "fixed", // 有效期类型:fixed(固定时间),custom(自定义时间)
|
||||
validDays: "", // 有效期(天)
|
||||
validStartTime: "", // 有效期开始时间
|
||||
@@ -1040,22 +875,27 @@ onMounted(() => {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.item {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.scroll {
|
||||
height: 76vh;
|
||||
padding-bottom: 60px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.dialog-footer {
|
||||
position: relative;
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
width: 100%;
|
||||
@@ -1067,6 +907,7 @@ onMounted(() => {
|
||||
background: linear-gradient(to bottom, rgba(255, 255, 255, 0), rgba(255, 255, 255, 1));
|
||||
}
|
||||
}
|
||||
|
||||
.textarea-num {
|
||||
color: #999;
|
||||
display: flex;
|
||||
|
||||
@@ -1,28 +1,41 @@
|
||||
<!-- 选择门店组件 -->
|
||||
<template>
|
||||
<el-select v-model="shops" multiple clearable placeholder="请选择门店" @change="shopsChange" style="width: 300px">
|
||||
<el-select :model-value="modelValue" :multiple="multiple" clearable placeholder="请选择门店"
|
||||
@change="$emit('update:modelValue', $event)" style="width: 300px">
|
||||
<el-option :label="item.shopName" :value="item.id" v-for="item in branchList" :key="item.id"></el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { getBranchPage } from "@/api/coupon/index.js";
|
||||
import { getBranchPage, getBranchList } from "@/api/coupon/index.js";
|
||||
|
||||
const shops = ref([])
|
||||
const emit = defineEmits(['success'])
|
||||
|
||||
// 选择分店
|
||||
function shopsChange(e) {
|
||||
emit('success', e.join(","))
|
||||
}
|
||||
const props = defineProps({
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
modelValue: {
|
||||
type: [Array, String],
|
||||
default: []
|
||||
}
|
||||
})
|
||||
|
||||
// 获取分店列表
|
||||
const branchList = ref([]);
|
||||
async function getBranchPageAjax() {
|
||||
try {
|
||||
const res = await getBranchPage();
|
||||
branchList.value = res.records;
|
||||
let res = null
|
||||
if (props.multiple) {
|
||||
res = await getBranchPage();
|
||||
branchList.value = res.records;
|
||||
} else {
|
||||
res = await getBranchList()
|
||||
res.map(item => {
|
||||
item.id = item.shopId
|
||||
})
|
||||
branchList.value = res;
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,502 @@
|
||||
<template>
|
||||
<el-dialog v-model="dialogVisible" :title="titleOptions.title" width="80%" top="4vh" @closed="closedReset">
|
||||
<div class="scroll" ref="scrollRef">
|
||||
<el-form ref="formRef" :model="form" :rules="formRules" label-width="160px" class="dialog-form">
|
||||
<el-form-item label="满减优惠" prop="thresholds">
|
||||
<div class="thresholds_list">
|
||||
<div class="item" v-for="(item, index) in form.thresholds" :key="index">
|
||||
<div class="ipt">
|
||||
<el-input v-model.trim="item.fullAmount" input-style="text-align: center;">
|
||||
<template #prepend>满</template>
|
||||
<template #append>元</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="ipt">
|
||||
<el-input v-model.trim="item.discountAmount" input-style="text-align: center;">
|
||||
<template #prepend>减</template>
|
||||
<template #append>元</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<el-button link type="danger" v-if="index > 0" @click="form.thresholds.splice(index, 1)">删除</el-button>
|
||||
</div>
|
||||
<div class="item">
|
||||
<el-button type="primary" icon="CirclePlus" @click="addThresholdsObjHandle">添加</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="活动日期" prop="validDays">
|
||||
<div style="width: 300px;">
|
||||
<el-date-picker v-model="validityScope" type="daterange" range-separator="至" start-placeholder="开始时间"
|
||||
end-placeholder="结束时间" @change="validityScopeChange" />
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="可用周期" prop="useDays">
|
||||
<el-checkbox-group v-model="form.useDays">
|
||||
<el-checkbox value="周一" label="周一" />
|
||||
<el-checkbox value="周二" label="周二" />
|
||||
<el-checkbox value="周三" label="周三" />
|
||||
<el-checkbox value="周四" label="周四" />
|
||||
<el-checkbox value="周五" label="周五" />
|
||||
<el-checkbox value="周六" label="周六" />
|
||||
<el-checkbox value="周七" label="周日" />
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="指定时间段">
|
||||
<el-radio-group v-model="form.useTimeType">
|
||||
<el-radio label="全时段可用" value="all"></el-radio>
|
||||
<el-radio label="指定时间段可用" value="custom"></el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.useTimeType == 'custom'" prop="useTimeType">
|
||||
<div style="width: 200px">
|
||||
<el-time-picker v-model="useTimeScope" is-range range-separator="至" start-placeholder="开始时间"
|
||||
end-placeholder="结束时间" @change="useTimeScopeChange" />
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="可使用类型" prop="useType">
|
||||
<el-checkbox-group v-model="form.useType">
|
||||
<el-checkbox value="dine-in" label="堂食" />
|
||||
<el-checkbox value="take-out" label="外带" />
|
||||
<el-checkbox value="take-away" label="外卖" />
|
||||
<el-checkbox value="post" label="配送" />
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="排序">
|
||||
<div class="column">
|
||||
<div>
|
||||
<el-input v-model="form.sort" placeholder="默认值:0" @input="e => form.sort = filterNumberInput(e, true)"
|
||||
style="width: 200px;"></el-input>
|
||||
</div>
|
||||
<div style="color: #FF2F2F;font-size: 12px;">数值越大,排序越靠前。重复时段下,按照排序值最高的活动减免</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="与优惠券同享">
|
||||
<div class="column">
|
||||
<div class="center">
|
||||
<el-switch disabled v-model="form.couponShare" :active-value="1" :inactive-value="0" />
|
||||
<span>不能和优惠券同时使用</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="与限时折扣同享">
|
||||
<div class="column">
|
||||
<div class="center">
|
||||
<el-switch v-model="form.discountShare" :active-value="1" :inactive-value="0" />
|
||||
<span>开启后,计算门槛时将会按照折扣价计算</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="与会员价/会员折扣同享">
|
||||
<div class="column">
|
||||
<div class="center">
|
||||
<el-switch v-model="form.vipPriceShare" :active-value="1" :inactive-value="0" />
|
||||
<span>开启后,计算门槛时将会按照会员价/会员折扣价计算</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="与积分抵扣同享">
|
||||
<div class="column">
|
||||
<div class="center">
|
||||
<el-switch v-model="form.pointsShare" :active-value="1" :inactive-value="0" />
|
||||
<span>开启后,可和积分抵扣同时使用</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="dialog-footer" v-if="(shopInfo.isHeadShop && shopInfo.shopType != 'only') || !form.syncId">
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" :loading="loading" @click="submitHandle">确 定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import _ from "lodash";
|
||||
import { dayjs } from "element-plus";
|
||||
import { ref, reactive, onMounted } from "vue";
|
||||
import { filterNumberInput } from "@/utils";
|
||||
import { discountActivity } from "@/api/coupon/index.js";
|
||||
|
||||
const shopInfo = ref("");
|
||||
const dialogVisible = ref(false);
|
||||
|
||||
const titleOptions = reactive({
|
||||
title: "添加优惠券"
|
||||
});
|
||||
|
||||
// 有效期时间
|
||||
const validityScope = ref([]);
|
||||
function validityScopeChange(e) {
|
||||
console.log("validityScopeChange===", e);
|
||||
if (e && e.length) {
|
||||
form.value.validStartTime = dayjs(e[0]).format("YYYY-MM-DD");
|
||||
form.value.validEndTime = dayjs(e[1]).format("YYYY-MM-DD");
|
||||
} else {
|
||||
form.value.validStartTime = "";
|
||||
form.value.validEndTime = "";
|
||||
}
|
||||
}
|
||||
// 可用时间
|
||||
const useTimeScope = ref([]);
|
||||
function useTimeScopeChange(e) {
|
||||
console.log("useTimeScopeChange===", e);
|
||||
if (e && e.length) {
|
||||
form.value.useStartTime = dayjs(e[0]).format("HH:mm");
|
||||
form.value.useEndTime = dayjs(e[1]).format("HH:mm");
|
||||
} else {
|
||||
form.value.useStartTime = "";
|
||||
form.value.useEndTime = "";
|
||||
}
|
||||
}
|
||||
|
||||
const formRef = ref(null);
|
||||
const addThresholdsObj = ref({
|
||||
fullAmount: '', // 满多少金额
|
||||
discountAmount: '' // 减多少金额
|
||||
})
|
||||
|
||||
const form = ref({
|
||||
id: '',
|
||||
shopId: '',
|
||||
validStartTime: '',
|
||||
validEndTime: '',
|
||||
useDays: ['周一', '周二', '周三', '周四', '周五', '周六', '周七'],
|
||||
useTimeType: 'all',
|
||||
useStartTime: '',
|
||||
useEndTime: '',
|
||||
useType: ['dine-in', 'take-out'],
|
||||
sort: '',
|
||||
couponShare: '',
|
||||
discountShare: 1,
|
||||
vipPriceShare: 1,
|
||||
pointsShare: 1,
|
||||
status: 1, // 状态:1未开始,2进行中,3已结束
|
||||
thresholds: [
|
||||
{ ...addThresholdsObj.value }
|
||||
]
|
||||
});
|
||||
|
||||
// 添加
|
||||
function addThresholdsObjHandle() {
|
||||
form.value.thresholds.push({ ...addThresholdsObj.value })
|
||||
}
|
||||
|
||||
// 初始化
|
||||
const resetForm = ref(null);
|
||||
function reset() {
|
||||
validityScope.value = [];
|
||||
useTimeScope.value = [];
|
||||
form.value = { ...resetForm.value };
|
||||
}
|
||||
|
||||
// 自定义校验固定有效期范围内可用
|
||||
const useTimeTypeValidate = (rule, value, callback) => {
|
||||
if (!form.value.useStartTime) {
|
||||
callback(new Error("请选择指定可用时间段"));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
// 验证满减规则数组的函数
|
||||
function validateDiscountRules(rules) {
|
||||
console.log(rules);
|
||||
|
||||
|
||||
// 检查是否为空数组
|
||||
if (!rules || rules.length === 0) {
|
||||
return { valid: false, message: '请添加满减规则' };
|
||||
}
|
||||
|
||||
// 遍历每个规则进行验证
|
||||
for (let i = 0; i < rules.length; i++) {
|
||||
const rule = rules[i];
|
||||
const index = i + 1; // 显示给用户的序号(从1开始)
|
||||
|
||||
// 检查fullAmount是否填写
|
||||
if (!rule.fullAmount) {
|
||||
return {
|
||||
valid: false,
|
||||
message: `第${index}条规则:请填写满减金额`
|
||||
};
|
||||
}
|
||||
|
||||
// 检查discountAmount是否填写
|
||||
if (!rule.discountAmount) {
|
||||
return {
|
||||
valid: false,
|
||||
message: `第${index}条规则:请填写减免金额`
|
||||
};
|
||||
}
|
||||
|
||||
// 转换为数字进行比较
|
||||
const full = Number(rule.fullAmount);
|
||||
const discount = Number(rule.discountAmount);
|
||||
|
||||
// 检查是否为有效数字
|
||||
if (isNaN(full) || full <= 0) {
|
||||
return {
|
||||
valid: false,
|
||||
message: `第${index}条规则:满减金额必须是有效的正数`
|
||||
};
|
||||
}
|
||||
|
||||
if (isNaN(discount) || discount <= 0) {
|
||||
return {
|
||||
valid: false,
|
||||
message: `第${index}条规则:减免金额必须是有效的正数`
|
||||
};
|
||||
}
|
||||
|
||||
// 检查减免金额是否大于满减金额
|
||||
if (discount > full) {
|
||||
return {
|
||||
valid: false,
|
||||
message: `第${index}条规则:减免金额不能大于满减金额`
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 所有规则都通过验证
|
||||
return { valid: true };
|
||||
}
|
||||
|
||||
const formRules = reactive({
|
||||
thresholds: [{
|
||||
required: true,
|
||||
validator: (rule, value, callback) => {
|
||||
let result = validateDiscountRules(form.value.thresholds)
|
||||
if (!result.valid) {
|
||||
callback(result.message)
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
},
|
||||
trigger: 'change'
|
||||
}],
|
||||
validDays: [
|
||||
{
|
||||
required: true,
|
||||
validator: (rule, value, callback) => {
|
||||
if (validityScope.value.length == 0) {
|
||||
callback(new Error("请选择日期范围"));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
},
|
||||
trigger: "change",
|
||||
},
|
||||
],
|
||||
useDays: [{ required: true, message: "请选择可用周期", trigger: "change" }],
|
||||
useTimeType: [
|
||||
{
|
||||
validator: useTimeTypeValidate,
|
||||
trigger: "change",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// 开始提交
|
||||
const emits = defineEmits(["success"]);
|
||||
const loading = ref(false);
|
||||
function submitHandle() {
|
||||
formRef.value.validate(async (valid) => {
|
||||
try {
|
||||
if (valid) {
|
||||
loading.value = true;
|
||||
|
||||
let data = { ...form.value }
|
||||
data.useDays = data.useDays.join(',')
|
||||
data.useType = data.useType.join(',')
|
||||
|
||||
await discountActivity(data, data.id ? 'put' : 'post');
|
||||
emits("success");
|
||||
dialogVisible.value = false;
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
// 从本地获取商户信息
|
||||
function getLocalShopInfo() {
|
||||
shopInfo.value = JSON.parse(localStorage.getItem("userInfo"));
|
||||
}
|
||||
|
||||
// 显示弹窗
|
||||
const scrollRef = ref(null);
|
||||
function show(obj = null) {
|
||||
nextTick(() => {
|
||||
if (scrollRef.value) {
|
||||
setTimeout(() => {
|
||||
scrollRef.value.scrollTop = 0;
|
||||
}, 50);
|
||||
}
|
||||
});
|
||||
if (obj && obj.id) {
|
||||
titleOptions.title = `编辑满减活动`;
|
||||
|
||||
form.value = { ...obj };
|
||||
|
||||
|
||||
form.value.useDays = form.value.useDays.split(',');
|
||||
form.value.useType = form.value.useType.split(',');
|
||||
|
||||
if (form.value.useTimeType == "custom") {
|
||||
useTimeScope.value = [
|
||||
convertTimeToDate(`${form.value.useStartTime}:00`),
|
||||
convertTimeToDate(`${form.value.useEndTime}:00`),
|
||||
];
|
||||
|
||||
console.log(useTimeScope.value);
|
||||
}
|
||||
} else {
|
||||
reset();
|
||||
titleOptions.title = `添加满减活动`;
|
||||
}
|
||||
dialogVisible.value = true;
|
||||
}
|
||||
|
||||
// 关闭后重置表单验证
|
||||
function closedReset() {
|
||||
formRef.value.resetFields();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将时分秒字符串转换为完整日期格式
|
||||
* @param {string} timeStr - 时分秒字符串,格式需为 HH:mm:ss(如 '00:53:00')
|
||||
* @param {Object} options - 可选配置项
|
||||
* @param {string} [options.customDate] - 自定义日期,格式为 YYYY-MM-DD(默认使用当前日期)
|
||||
* @param {string} [options.format='YYYY-MM-DD HH:mm:ss'] - 输出的日期格式
|
||||
* @returns {string|null} 转换后的日期字符串,失败时返回 null
|
||||
*/
|
||||
function convertTimeToDate(timeStr, options = {}) {
|
||||
// 解构配置项,设置默认值
|
||||
const { customDate, format = "YYYY-MM-DD HH:mm:ss" } = options;
|
||||
|
||||
// 1. 校验时分秒格式(必须为 HH:mm:ss,允许数字1-2位)
|
||||
const timeRegex = /^\d{1,2}:\d{1,2}:\d{1,2}$/;
|
||||
if (!timeRegex.test(timeStr)) {
|
||||
console.error("时分秒格式错误,请使用 HH:mm:ss 格式(如 00:53:00)");
|
||||
return null;
|
||||
}
|
||||
|
||||
// 2. 确定日期部分(自定义日期或当前日期)
|
||||
let datePart;
|
||||
if (customDate) {
|
||||
// 校验自定义日期格式
|
||||
if (!dayjs(customDate, "YYYY-MM-DD", true).isValid()) {
|
||||
console.error("自定义日期格式错误,请使用 YYYY-MM-DD 格式(如 2024-05-20)");
|
||||
return null;
|
||||
}
|
||||
datePart = customDate;
|
||||
} else {
|
||||
// 使用当前日期(格式:YYYY-MM-DD)
|
||||
datePart = dayjs().format("YYYY-MM-DD");
|
||||
}
|
||||
|
||||
// 3. 组合日期和时分秒,生成完整日期对象
|
||||
const fullDateTime = `${datePart} ${timeStr}`;
|
||||
const dateObj = dayjs(fullDateTime);
|
||||
|
||||
// 4. 校验完整日期是否有效(如避免 2024-02-30 这种无效日期)
|
||||
if (!dateObj.isValid()) {
|
||||
console.error("生成的日期无效,请检查日期或时分秒是否合理");
|
||||
return null;
|
||||
}
|
||||
|
||||
// 5. 按指定格式返回日期字符串
|
||||
return dateObj.format(format);
|
||||
}
|
||||
|
||||
// input过滤
|
||||
const time = 500;
|
||||
const discountNumInput = _.debounce(function (value) {
|
||||
form.value.discountNum = filterNumberInput(value, true);
|
||||
if (form.value.discountNum == "") {
|
||||
form.value.discountNum = 1;
|
||||
}
|
||||
}, time);
|
||||
|
||||
defineExpose({
|
||||
show,
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
resetForm.value = { ...form.value };
|
||||
getLocalShopInfo();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.title {
|
||||
color: #000;
|
||||
padding: 14px;
|
||||
background-color: #f8f8f8;
|
||||
margin-bottom: 14px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.column {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.item {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.scroll {
|
||||
height: 76vh;
|
||||
padding-bottom: 60px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.dialog-footer {
|
||||
position: relative;
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: calc(100% + var(--el-dialog-padding-primary));
|
||||
z-index: 9;
|
||||
background: linear-gradient(to bottom, rgba(255, 255, 255, 0), rgba(255, 255, 255, 1));
|
||||
}
|
||||
}
|
||||
|
||||
.textarea-num {
|
||||
color: #999;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.thresholds_list {
|
||||
.item {
|
||||
display: flex;
|
||||
gap: 14px;
|
||||
|
||||
&:not(:first-child) {
|
||||
margin-top: 14px;
|
||||
}
|
||||
|
||||
.ipt {
|
||||
width: 200px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
254
src/views/marketing_center/discount_activity/index.vue
Normal file
254
src/views/marketing_center/discount_activity/index.vue
Normal file
@@ -0,0 +1,254 @@
|
||||
<template>
|
||||
<div class="gyq_container">
|
||||
<div class="gyq_content">
|
||||
<headerCard icon="mjhd" name="满减活动" intro="达到指定支付金额享受减价" showSwitch v-model:isOpen="form.isEnableDiscount" />
|
||||
<div class="row">
|
||||
<el-form :model="form" inline>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="CirclePlus" @click="addDialogRef.show()">添加</el-button>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-date-picker style="width: 300px" v-model="times" type="daterange" range-separator="至"
|
||||
start-placeholder="开始日期" end-placeholder="结束日期" value-format="YYYY-MM-DD"
|
||||
@change="selectTimeChange"></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="resetSearch">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetSearch">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<div class="row">
|
||||
<el-table :data="tableData.list" stripe border v-loading="tableData.loading">
|
||||
<el-table-column label="ID" prop="id" width="80"></el-table-column>
|
||||
<el-table-column label="活动日期" prop="orderNo">
|
||||
<template #default="scope">
|
||||
<div v-if="scope.row.useTimeType == 'all'">全时段</div>
|
||||
<div v-else>
|
||||
{{ scope.row.validStartTime }} - {{ scope.row.validEndTime }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="生效周期" prop="useDays"></el-table-column>
|
||||
<el-table-column label="生效时段" prop="validStartTime">
|
||||
<template #default="scope">
|
||||
<div v-if="scope.row.useTimeType == 'all'">全时段</div>
|
||||
<div v-else>
|
||||
{{ scope.row.useStartTime }} - {{ scope.row.useEndTime }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="活动内容" prop="thresholds">
|
||||
<template #default="scope">
|
||||
<div class="column">
|
||||
<div class="item" v-for="(item, index) in scope.row.thresholds" :key="index">
|
||||
满{{ item.fullAmount }}减{{ item.discountAmount }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="排序" prop="sort"></el-table-column>
|
||||
<el-table-column label="状态" prop="status">
|
||||
<template #default="scope">
|
||||
<el-tag disable-transitions type="primary" v-if="scope.row.status == 1">未开始</el-tag>
|
||||
<el-tag disable-transitions type="success" v-if="scope.row.status == 2">进行中</el-tag>
|
||||
<el-tag disable-transitions type="info" v-if="scope.row.status == 3">已结束</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" prop="orderNo" width="120" fixed="right">
|
||||
<template #default="scope">
|
||||
<div class="center">
|
||||
<el-button link type="primary" @click="addDialogRef.show(scope.row)">编辑</el-button>
|
||||
<el-popconfirm title="确认要删除吗?" @confirm="deleteHandle(scope.row)">
|
||||
<template #reference>
|
||||
<el-button type="danger" link>删除</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<div class="row" style="margin-top: 14px;">
|
||||
<el-pagination v-model:current-page="tableData.page" v-model:page-size="tableData.size"
|
||||
:page-sizes="[10, 20, 50, 100, 500]" background layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="tableData.total" @size-change="handleSizeChange" @current-change="handleCurrentChange" />
|
||||
</div>
|
||||
</div>
|
||||
<addDialog ref="addDialogRef" @success="searchHandle" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch } from 'vue'
|
||||
import addDialog from './components/addDialog.vue'
|
||||
import { discountActivityPage, discountActivityDelete, shopInfoPut, shopInfoGet } from '@/api/coupon/index'
|
||||
|
||||
const addDialogRef = ref(null)
|
||||
|
||||
const form = ref({
|
||||
isEnableDiscount: false
|
||||
})
|
||||
|
||||
watch(() => form.value.isEnableDiscount, (newVal, oldVal) => {
|
||||
console.log('值改变了', form.value);
|
||||
if (form.value.id) {
|
||||
shopInfoPutAjax()
|
||||
}
|
||||
})
|
||||
|
||||
// 更新店铺信息
|
||||
async function shopInfoPutAjax() {
|
||||
try {
|
||||
await shopInfoPut(form.value)
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
const times = ref([])
|
||||
const queryForm = ref({
|
||||
startTime: '',
|
||||
endTime: ''
|
||||
})
|
||||
|
||||
const tableData = reactive({
|
||||
loading: false,
|
||||
page: 1,
|
||||
size: 10,
|
||||
list: [],
|
||||
total: 0
|
||||
})
|
||||
|
||||
// 搜索
|
||||
function searchHandle() {
|
||||
tableData.page = 1;
|
||||
getTableData()
|
||||
}
|
||||
|
||||
// 重置搜索
|
||||
function resetSearch() {
|
||||
queryForm.value.startTime = ''
|
||||
queryForm.value.endTime = ''
|
||||
times.value = []
|
||||
searchHandle()
|
||||
}
|
||||
|
||||
// 选择日期
|
||||
function selectTimeChange(e) {
|
||||
console.log(e);
|
||||
queryForm.value.startTime = dayjs(e[0]).format('YYYY-MM-DD 00:00:00')
|
||||
queryForm.value.endTime = dayjs(e[1]).format('YYYY-MM-DD 23:59:59')
|
||||
}
|
||||
|
||||
// 分页大小发生变化
|
||||
function handleSizeChange(e) {
|
||||
tableData.size = e;
|
||||
getTableData();
|
||||
}
|
||||
|
||||
// 分页发生变化
|
||||
function handleCurrentChange(e) {
|
||||
tableData.page = e;
|
||||
getTableData();
|
||||
}
|
||||
|
||||
// 删除
|
||||
async function deleteHandle(row) {
|
||||
try {
|
||||
tableData.loading = true;
|
||||
await discountActivityDelete(row.id);
|
||||
ElNotification({
|
||||
title: '注意',
|
||||
message: '已删除',
|
||||
type: 'success'
|
||||
})
|
||||
getTableData();
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
|
||||
// 获取表格数据
|
||||
async function getTableData() {
|
||||
try {
|
||||
tableData.loading = true
|
||||
const res = await discountActivityPage({
|
||||
// ...queryForm.value,
|
||||
page: tableData.page,
|
||||
size: tableData.size
|
||||
})
|
||||
tableData.list = res.records
|
||||
tableData.total = +res.totalRow
|
||||
|
||||
console.log(tableData);
|
||||
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
tableData.loading = false
|
||||
}
|
||||
|
||||
// 获取店铺信息
|
||||
async function shopInfoGetAjax() {
|
||||
try {
|
||||
const res = await shopInfoGet()
|
||||
form.value = res
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getTableData()
|
||||
shopInfoGetAjax()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.gyq_container {
|
||||
padding: 14px;
|
||||
|
||||
.gyq_content {
|
||||
padding: 14px;
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.column {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
|
||||
.item {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.tips {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.footer {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.textarea-num {
|
||||
color: #999;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.row {
|
||||
padding-top: 14px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,11 +1,7 @@
|
||||
<template>
|
||||
<div class="app_main">
|
||||
<div class="card">
|
||||
<headerCard
|
||||
icon="xfzq"
|
||||
name="满减券"
|
||||
intro="用户满足指定金额后,可使用优惠券立减相应金额,如:设置满100-50券,符合要求的订单满100元后,立减50元。"
|
||||
/>
|
||||
<headerCard icon="xfzq" name="满减券" intro="用户满足指定金额后,可使用优惠券立减相应金额,如:设置满100-50券,符合要求的订单满100元后,立减50元。" />
|
||||
<div class="tab_wrap">
|
||||
<div class="row">
|
||||
<el-button type="primary" @click="CouponDialogRef.show(couponType)">
|
||||
@@ -42,10 +38,7 @@
|
||||
<div class="center">
|
||||
<el-text>{{ scope.row.giftNum }}</el-text>
|
||||
<el-text>|</el-text>
|
||||
<el-link
|
||||
type="primary"
|
||||
@click="GetDetailDialogRef.show(scope.row)"
|
||||
>
|
||||
<el-link type="primary" @click="GetDetailDialogRef.show(scope.row)">
|
||||
详情
|
||||
</el-link>
|
||||
</div>
|
||||
@@ -67,47 +60,25 @@
|
||||
</el-table-column>
|
||||
<el-table-column prop="status" label="启用状态" width="100">
|
||||
<template #default="scope">
|
||||
<el-switch
|
||||
v-model="scope.row.status"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
:disabled="!!scope.row.syncId"
|
||||
@change="statusChange($event, scope.row)"
|
||||
/>
|
||||
<el-switch v-model="scope.row.status" :active-value="1" :inactive-value="0"
|
||||
:disabled="!!scope.row.syncId" @change="statusChange($event, scope.row)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="创建时间" width="180" />
|
||||
<el-table-column
|
||||
prop="actions"
|
||||
label="操作"
|
||||
align="center"
|
||||
width="140"
|
||||
fixed="right"
|
||||
>
|
||||
<el-table-column prop="actions" label="操作" align="center" width="140" fixed="right">
|
||||
<template #default="scope">
|
||||
<template v-if="!scope.row.syncId">
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
@click="CouponDialogRef.show(couponType, scope.row)"
|
||||
>
|
||||
<el-button type="primary" link @click="CouponDialogRef.show(couponType, scope.row)">
|
||||
编辑
|
||||
</el-button>
|
||||
<el-popconfirm
|
||||
title="确认要删除吗?"
|
||||
@confirm="deleteHandle(scope.row)"
|
||||
>
|
||||
<el-popconfirm title="确认要删除吗?" @confirm="deleteHandle(scope.row)">
|
||||
<template #reference>
|
||||
<el-button type="danger" link>删除</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
@click="CouponDialogRef.show(couponType, scope.row)"
|
||||
>
|
||||
<el-button type="primary" link @click="CouponDialogRef.show(couponType, scope.row)">
|
||||
详情
|
||||
</el-button>
|
||||
<el-button type="danger" disabled link>删除</el-button>
|
||||
@@ -117,16 +88,9 @@
|
||||
</el-table>
|
||||
</div>
|
||||
<div class="row">
|
||||
<el-pagination
|
||||
v-model:current-page="tableData.page"
|
||||
v-model:page-size="tableData.pageSize"
|
||||
:page-sizes="[100, 200, 300, 400]"
|
||||
background
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="tableData.total"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
<el-pagination v-model:current-page="tableData.page" v-model:page-size="tableData.pageSize"
|
||||
:page-sizes="[100, 200, 300, 400]" background layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="tableData.total" @size-change="handleSizeChange" @current-change="handleCurrentChange" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -241,6 +205,7 @@ onMounted(() => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
289
src/views/marketing_center/drainage/index.vue
Normal file
289
src/views/marketing_center/drainage/index.vue
Normal file
@@ -0,0 +1,289 @@
|
||||
<!-- 私域引流 -->
|
||||
<template>
|
||||
<div class="gyq_container">
|
||||
<div class="gyq_content">
|
||||
<headerCard icon="syyl" name="私域引流" intro="可设置用户下单成功后的群二维码" showSwitch v-model:isOpen="form.isEnable" />
|
||||
<div class="form_content">
|
||||
<div class="left">
|
||||
<div class="preview">
|
||||
<div class="info">
|
||||
<div class="top">
|
||||
<div class="title">{{ form.title || '请输入模块标题' }}</div>
|
||||
<div class="content">{{ form.content || '请输入模块内容' }}</div>
|
||||
</div>
|
||||
<div class="btm">{{ form.note || '请输入模块提示语' }}</div>
|
||||
</div>
|
||||
<div class="img_wrap">
|
||||
<el-image :src="form.qrCode" style="width: 100%;height: 100%;"></el-image>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form">
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-position="right" label-width="120px">
|
||||
<el-form-item label="可使用类型" prop="useType">
|
||||
<el-checkbox-group v-model="form.useType">
|
||||
<el-checkbox value="dine-in" label="堂食" />
|
||||
<el-checkbox value="take-out" label="外带" />
|
||||
<el-checkbox value="take-away" label="外卖" />
|
||||
<el-checkbox value="post" label="配送" />
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="群二维码" prop="qrCode">
|
||||
<single-image-upload style="width: 120px; height: 120px" v-model="form.qrCode"></single-image-upload>
|
||||
</el-form-item>
|
||||
<el-form-item label="模块标题" prop="title">
|
||||
<el-input placeholder="请输入模块标题" :maxlength="20" v-model.trim="form.title"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="模块内容">
|
||||
<div class="textarea">
|
||||
<el-input type="textarea" :rows="4" :maxlength="50" placeholder="请输入内容"
|
||||
v-model.trim="form.content"></el-input>
|
||||
<span class="num">{{ form.content.length }}/50</span>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="模块提示语">
|
||||
<el-input placeholder="请输模块提示语" :maxlength="20" v-model.trim="form.note"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" :loading="loading" @click="submitHandle">保存</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { ElNotification } from 'element-plus'
|
||||
import { drainageConfigGet, drainageConfigPost } from '@/api/coupon/index'
|
||||
|
||||
const defaultNote = ref('长按识别,微信内扫一扫加好友')
|
||||
|
||||
const form = ref({
|
||||
id: '',
|
||||
useType: ['dine-in'],
|
||||
qrCode: '',
|
||||
title: '',
|
||||
content: '',
|
||||
note: '长按识别,微信内扫一扫加好友',
|
||||
isEnable: '',
|
||||
mainShopId: '',
|
||||
shopId: ''
|
||||
})
|
||||
|
||||
const rules = reactive({
|
||||
useType: [
|
||||
{
|
||||
required: true,
|
||||
validator: (rule, value, callback) => {
|
||||
if (form.value.useType.length <= 0) {
|
||||
callback(new Error('请选择可使用类型'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
},
|
||||
triiger: 'change'
|
||||
}
|
||||
],
|
||||
qrCode: [
|
||||
{
|
||||
required: true,
|
||||
message: '请上传群二维码',
|
||||
triiger: 'change'
|
||||
}
|
||||
],
|
||||
title: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入模块标题',
|
||||
triiger: 'change'
|
||||
}
|
||||
],
|
||||
content: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入内容',
|
||||
triiger: 'change'
|
||||
}
|
||||
],
|
||||
note: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输模块提示语',
|
||||
triiger: 'change'
|
||||
}
|
||||
],
|
||||
})
|
||||
|
||||
// 开始提交
|
||||
const formRef = ref(null)
|
||||
const emits = defineEmits(["success"]);
|
||||
const loading = ref(false);
|
||||
function submitHandle() {
|
||||
formRef.value.validate(async (valid) => {
|
||||
try {
|
||||
if (valid) {
|
||||
loading.value = true;
|
||||
|
||||
let data = { ...form.value }
|
||||
|
||||
await drainageConfigPost(data);
|
||||
ElNotification({
|
||||
title: '注意',
|
||||
message: '保存成功',
|
||||
type: 'success'
|
||||
})
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
// 获取配置信息
|
||||
async function drainageConfigGetAjax() {
|
||||
try {
|
||||
const res = await drainageConfigGet()
|
||||
if (res.useType == null) {
|
||||
res.useType = []
|
||||
}
|
||||
form.value = res
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
drainageConfigGetAjax()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.gyq_container {
|
||||
padding: 14px;
|
||||
|
||||
.gyq_content {
|
||||
padding: 14px;
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.column {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
|
||||
.item {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.tips {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.footer {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.textarea-num {
|
||||
color: #999;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.row {
|
||||
padding-top: 14px;
|
||||
}
|
||||
|
||||
.form_content {
|
||||
padding: 14px 0 0;
|
||||
display: flex;
|
||||
|
||||
.left {
|
||||
width: 374px;
|
||||
height: 720px;
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
position: relative;
|
||||
background: url('https://cashier-oss.oss-cn-beijing.aliyuncs.com/upload/3/ac537d7f47464ec5a8e7577db1fd4f3c.png') no-repeat center center / 100% 100%;
|
||||
|
||||
.preview {
|
||||
width: 350px;
|
||||
height: 130px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background-color: #fff;
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 14px;
|
||||
|
||||
.info {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
padding: 5px 0;
|
||||
|
||||
.top {
|
||||
flex: 1;
|
||||
|
||||
.title {
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.content {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
.btm {
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
.img_wrap {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
|
||||
.img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.textarea {
|
||||
width: 300px;
|
||||
position: relative;
|
||||
|
||||
.num {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
bottom: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
167
src/views/marketing_center/king_dine/index.vue
Normal file
167
src/views/marketing_center/king_dine/index.vue
Normal file
@@ -0,0 +1,167 @@
|
||||
<!-- 霸王餐 -->
|
||||
<template>
|
||||
<div class="gyq_container">
|
||||
<div class="gyq_content">
|
||||
<HeaderCard name="霸王餐" intro="设置充值消费的N倍,当前订单立即免单" icon="xffx" showSwitch v-model:isOpen="form.enable">
|
||||
</HeaderCard>
|
||||
<div class="row">
|
||||
<el-tabs v-model="tabsValue">
|
||||
<el-tab-pane label="基础设置" :name="1">
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-position="right" label-width="120px">
|
||||
<el-form-item label="充值设置" prop="rechargeTimes">
|
||||
<div class="center">
|
||||
<span>用户消费结账时,成功充值</span>
|
||||
<el-input v-model="form.rechargeTimes" placeholder="请输入内容" style="width: 100px;"
|
||||
input-style="text-align: center;"></el-input>
|
||||
<span>倍的金额,本单即可享受免单</span>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="充值门槛" prop="rechargeThreshold">
|
||||
<div class="center">
|
||||
<span>订单支付金额需满</span>
|
||||
<el-input v-model="form.rechargeThreshold" placeholder="请输入内容" style="width: 100px;"
|
||||
input-style="text-align: center;"></el-input>
|
||||
<span>元才能使用</span>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="可用门店">
|
||||
<el-radio-group v-model="form.useShopType">
|
||||
<el-radio label="全部门店" value="all"></el-radio>
|
||||
<el-radio label="指定门店可用" value="part"></el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="选择门店" prop="shopIdList" v-if="form.useShopType == 'part'">
|
||||
<selectBranchs v-model="form.shopIdList" />
|
||||
</el-form-item>
|
||||
<!-- 可使用类型 -->
|
||||
<el-form-item label="可使用类型" prop="useType">
|
||||
<el-checkbox-group v-model="form.useType">
|
||||
<el-checkbox label="店内" value="dine-in"></el-checkbox>
|
||||
<el-checkbox label="自取" value="take-out"></el-checkbox>
|
||||
<el-checkbox label="快递" value="post"></el-checkbox>
|
||||
<el-checkbox label="外卖" value="takeaway"></el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="可与优惠券同享">
|
||||
<el-switch v-model="form.withCoupon"></el-switch>
|
||||
</el-form-item>
|
||||
<el-form-item label="可与积分同享">
|
||||
<el-switch v-model="form.withPoints"></el-switch>
|
||||
</el-form-item>
|
||||
<el-form-item style="margin-top: 50px;" v-if="shopInfo.isHeadShop == 1 && shopInfo.shopType != 'only'">
|
||||
<el-button type="primary" @click="submitHandle">保存</el-button>
|
||||
<el-button @click="router.back()">取消</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
<!-- <el-tab-pane label="充值记录" :name="2">
|
||||
<record />
|
||||
</el-tab-pane> -->
|
||||
</el-tabs>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import HeaderCard from "../components/headerCard.vue";
|
||||
import record from "./record.vue";
|
||||
import { freeDingGet, freeDingPut } from "@/api/coupon";
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
const shopInfo = ref(JSON.parse(localStorage.getItem('userInfo')))
|
||||
const router = useRouter();
|
||||
const tabsValue = ref(1)
|
||||
|
||||
const form = ref({
|
||||
id: '',
|
||||
enable: false, //是否启用
|
||||
rechargeTimes: '', //充值倍数
|
||||
rechargeThreshold: '', //充值门槛
|
||||
useShopType: 'all', //门店类型 all全部 part部分
|
||||
shopIdList: [], // 门店列表
|
||||
useType: ['dine-in'], // 可使用类型 dine-in店内 takeaway外卖 takeout自取 post快递
|
||||
withCoupon: false, //是否可与优惠券同享
|
||||
withPoints: true, //是否可与积分同享
|
||||
})
|
||||
|
||||
const rules = ref({
|
||||
rechargeTimes: [
|
||||
{ required: true, message: '请输入充值倍数', trigger: 'blur' },
|
||||
{ pattern: /^[1-9]\d*$/, message: '请输入正整数', trigger: 'blur' }
|
||||
],
|
||||
rechargeThreshold: [
|
||||
{ required: true, message: '请输入充值门槛', trigger: 'blur' },
|
||||
{ pattern: /^(0|[1-9]\d*)(\.\d{1,2})?$/, message: '请输入正确的金额', trigger: 'blur' }
|
||||
],
|
||||
shopIdList: [
|
||||
{ required: true, message: '请选择门店', trigger: 'change' },
|
||||
],
|
||||
useType: [
|
||||
{ required: true, message: '请选择可使用类型', trigger: 'change' }
|
||||
]
|
||||
})
|
||||
|
||||
// 提交保存
|
||||
const formRef = ref(null)
|
||||
function submitHandle() {
|
||||
formRef.value.validate(async (valid) => {
|
||||
try {
|
||||
if (valid) {
|
||||
const data = { ...form.value }
|
||||
data.enable = form.value.enable ? true : false
|
||||
await freeDingPut(form.value);
|
||||
ElNotification({
|
||||
title: "注意",
|
||||
message: "保存成功",
|
||||
type: "success",
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 获取当前店铺霸王餐配置信息列表
|
||||
async function freeDingGetAjax() {
|
||||
try {
|
||||
const res = await freeDingGet();
|
||||
form.value = { ...res }
|
||||
form.value.enable = res.enable ? 1 : 0
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
freeDingGetAjax()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.gyq_container {
|
||||
padding: 14px;
|
||||
|
||||
.gyq_content {
|
||||
padding: 14px;
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.row {
|
||||
padding-top: 14px;
|
||||
}
|
||||
|
||||
.center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
|
||||
span {
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
6
src/views/marketing_center/king_dine/record.vue
Normal file
6
src/views/marketing_center/king_dine/record.vue
Normal file
@@ -0,0 +1,6 @@
|
||||
<template>
|
||||
<div>充值记录</div>
|
||||
</template>
|
||||
<script setup>
|
||||
|
||||
</script>
|
||||
@@ -42,10 +42,10 @@ const menus = ref([
|
||||
{
|
||||
name: "霸王餐",
|
||||
icon: "bwc",
|
||||
pathName: "bwc",
|
||||
pathName: "king_dine",
|
||||
intro: "设置充值消费的N倍,当前订单立即免单",
|
||||
},
|
||||
{ name: "邀请列表", icon: "yqlb", pathName: "invite", intro: "邀请好友领券" },
|
||||
// { name: "邀请列表", icon: "yqlb", pathName: "invite", intro: "邀请好友领券" },
|
||||
{
|
||||
name: "积分锁客",
|
||||
icon: "jfsk",
|
||||
@@ -67,10 +67,10 @@ const menus = ref([
|
||||
{
|
||||
name: "私域引流",
|
||||
icon: "syyl",
|
||||
pathName: "",
|
||||
pathName: "drainage",
|
||||
intro: "可设置用户下单成功后的群二维码",
|
||||
},
|
||||
{ name: "满减活动", icon: "mjhd", pathName: "", intro: "达到指定支付金额享受减价" },
|
||||
{ name: "满减活动", icon: "mjhd", pathName: "discount_activity", intro: "达到指定支付金额享受减价" },
|
||||
{ name: "生日有礼", icon: "sryl", pathName: "", intro: "用户生日管理设置" },
|
||||
{
|
||||
name: "点餐智能推荐",
|
||||
@@ -155,7 +155,7 @@ const menus = ref([
|
||||
label: "推送功能",
|
||||
list: [
|
||||
{ name: "推送活动消息", icon: "tshdxx", pathName: "", intro: "给用户推送服务通知" },
|
||||
{ name: "短信推送", icon: "dxts", pathName: "", intro: "给用户推送服务通知" },
|
||||
{ name: "短信推送", icon: "dxts", pathName: "note_push", intro: "给用户推送服务通知" },
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
766
src/views/marketing_center/note_push/components/addTask.vue
Normal file
766
src/views/marketing_center/note_push/components/addTask.vue
Normal file
@@ -0,0 +1,766 @@
|
||||
<template>
|
||||
<el-dialog v-model="dialogVisible" :title="form.id ? '编辑推送任务' : '添加推送任务'" width="1200px" top="4vh">
|
||||
<div class="scroll" ref="scrollRef">
|
||||
<el-form ref="formRef" :model="form" :rules="formRules" label-width="160px">
|
||||
<div class="title">短信内容</div>
|
||||
<el-form-item label-width="0">
|
||||
<div class="shop_user_wrap">
|
||||
<div class="item">
|
||||
<el-form-item label="选择模板" prop="pushEventId">
|
||||
<el-select v-model="form.pushEventId" style="width: 300px;" @change="selectTempChange">
|
||||
<el-option :label="item.title" :value="item.id" v-for="item in tempList" :key="item.id"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-for="(item, index) in selectTempList" :key="index" :label="item.key"
|
||||
style="margin-top: 14px;" prop="selectTempList">
|
||||
<el-input v-if="item.type === 'input'" v-model="item.value" :placeholder="`请输入${item.key}`"
|
||||
style="width:300px"></el-input>
|
||||
<el-time-picker v-else-if="item.type === 'time'" v-model="item.value" placeholder="请选择时间" format="HH:mm"
|
||||
value-format="HH:mm" :picker-options="{ selectableRange: '00:00:00-23:59:59' }"
|
||||
style="width: 300px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="赠送优惠券" style="margin-top: 14px;" prop="coupon">
|
||||
<div class="center" v-for="(item, index) in selectCoupons" :key="item.id">
|
||||
<el-select v-model="item.id" @change="selectCouponChnge($event, index)">
|
||||
<el-option :label="val.title" :value="val.id" v-for="val in couponList" :key="val.id"></el-option>
|
||||
</el-select>
|
||||
<el-input v-model="item.num" input-style="text-align:center;">
|
||||
<template #append>数量</template>
|
||||
</el-input>
|
||||
<div class="del" @click="selectCoupons.splice(index, 1)">
|
||||
<el-icon size="18" color="#FF2F2F">
|
||||
<Delete />
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="center">
|
||||
<el-button link type="primary" icon="CirclePlus" @click="addCoupon">新增券</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="temp_preview">
|
||||
<div class="temp_preview_title">模板预览</div>
|
||||
<div class="temp_preview_content" ref="tempPrewiewContentRef" v-if="selectTemp" v-html="previewHtml">
|
||||
</div>
|
||||
<div class="temp_preview_content" style="margin-top: 14px;" v-else>请选择模板</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<div class="title">选择目标用户</div>
|
||||
<el-form-item label-width="0">
|
||||
<div class="shop_user_wrap">
|
||||
<div class="item">
|
||||
<el-form-item label="发送对象">
|
||||
<el-radio-group v-model="form.userType" @change="getPushEventUserAjax">
|
||||
<el-radio label="全部绑定手机号用户" :value="1"></el-radio>
|
||||
<el-radio label="自定义用户" :value="2"></el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<div v-if="form.userType == 2">
|
||||
<el-form-item label="性别">
|
||||
<el-checkbox v-model="form.smsPushEventUser.sexMan" label="男" :true-value="1" :false-value="0"
|
||||
@change="getPushEventUserAjax"></el-checkbox>
|
||||
<el-checkbox v-model="form.smsPushEventUser.sexWoman" label="女" :true-value="1" :false-value="0"
|
||||
@change="getPushEventUserAjax"></el-checkbox>
|
||||
<el-checkbox v-model="form.smsPushEventUser.sexUnknown" label="未知" :true-value="1" :false-value="0"
|
||||
@change="getPushEventUserAjax"></el-checkbox>
|
||||
</el-form-item>
|
||||
<el-form-item label="下单">
|
||||
<el-checkbox v-model="form.smsPushEventUser.noOrder" label="从未下单" :true-value="1" :false-value="0"
|
||||
@change="getPushEventUserAjax"></el-checkbox>
|
||||
<el-checkbox v-model="form.smsPushEventUser.oneOrder" label="下过1单" :true-value="1" :false-value="0"
|
||||
@change="getPushEventUserAjax"></el-checkbox>
|
||||
<el-checkbox v-model="form.smsPushEventUser.fiveOrder" label="下过5单及以上" :true-value="1"
|
||||
:false-value="0" @change="getPushEventUserAjax"></el-checkbox>
|
||||
</el-form-item>
|
||||
<el-form-item label="下单时间">
|
||||
<el-checkbox v-model="form.smsPushEventUser.orderTimeToday" label="今天" :true-value="1"
|
||||
:false-value="0" @change="getPushEventUserAjax"></el-checkbox>
|
||||
<el-checkbox v-model="form.smsPushEventUser.orderTimeYesterday" label="昨天" :true-value="1"
|
||||
:false-value="0" @change="getPushEventUserAjax"></el-checkbox>
|
||||
<el-checkbox v-model="form.smsPushEventUser.orderTimeTwoWeeks" label="2周内" :true-value="1"
|
||||
:false-value="0" @change="getPushEventUserAjax"></el-checkbox>
|
||||
<el-checkbox v-model="form.smsPushEventUser.orderTimeMoreThanTwoWeeks" label="2周前" :true-value="1"
|
||||
:false-value="0" @change="getPushEventUserAjax"></el-checkbox>
|
||||
</el-form-item>
|
||||
<el-form-item label="会员">
|
||||
<el-radio-group v-model="form.smsPushEventUser.isVip" @change="getPushEventUserAjax">
|
||||
<el-radio label="会员" :value="1"></el-radio>
|
||||
<el-radio label="非会员" :value="0"></el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="充值">
|
||||
<el-radio-group v-model="form.smsPushEventUser.isRecharge" @change="getPushEventUserAjax">
|
||||
<el-radio label="从未充值过" :value="0"></el-radio>
|
||||
<el-radio label="充值过" :value="1"></el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item2">
|
||||
<div class="user_wrap" v-loading="userTableData.loading">
|
||||
<div class="user_header">
|
||||
<span>预计发送</span>
|
||||
<span>{{ form.estimateNum }}人</span>
|
||||
</div>
|
||||
<div class="list">
|
||||
<div class="item" v-for="item in userTableData.list" :key="item.id">
|
||||
<div class="avatar">
|
||||
<el-avatar :size="50" :src="item.headImg" />
|
||||
</div>
|
||||
<div class="info">
|
||||
<div class="name">{{ item.nickName }}</div>
|
||||
<div class="info_wrap">
|
||||
<span>余额:{{ item.amount }}</span>
|
||||
<span>积分:{{ item.accountPoints }}</span>
|
||||
<span>手机号:{{ item.phone }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex;justify-content: center;padding-bottom: 14px;">
|
||||
<el-pagination v-model:current-page="userTableData.page" v-model:page-size="userTableData.pageSize"
|
||||
:page-sizes="[100, 200, 300, 400]" background layout="prev, pager, next"
|
||||
:total="userTableData.total" @size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<div class="title">发送设置</div>
|
||||
<el-form-item label="发送时间">
|
||||
<el-radio-group v-model="form.sendType">
|
||||
<el-radio label="立即发送" :value="1"></el-radio>
|
||||
<el-radio label="定时发送" :value="2"></el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="选择时间" v-if="form.sendType == 2" prop="sendTime">
|
||||
<el-date-picker v-model="form.sendTime" type="datetime" placeholder="请选择发送时间" format="YYYY-MM-DD HH:mm:sss"
|
||||
value-format="YYYY-MM-DD HH:mm:ss" style="width: 220px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="预计发送人数">
|
||||
{{ form.estimateNum }}人
|
||||
</el-form-item>
|
||||
<el-form-item label="短信单价">
|
||||
{{ notePirce }}元/条
|
||||
</el-form-item>
|
||||
<el-form-item label="预计费用">
|
||||
¥{{ predictPrice }}
|
||||
</el-form-item>
|
||||
<el-form-item label="账户余额">
|
||||
¥{{ multiplyAndFormat(shopBalance.money || 0) }}
|
||||
<span v-if="predictPrice > shopBalance.money"
|
||||
style="color:#FF2F2F;margin-left: 14px;">余额不足,请联系管理员充值后再发送</span>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" :disabled="form.estimateNum <= 0 || predictPrice > shopBalance.money"
|
||||
:loading="loading" @click="submitHandle">确
|
||||
定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import _ from 'lodash'
|
||||
import { multiplyAndFormat } from '@/utils'
|
||||
import { ref, reactive, onMounted, computed, nextTick } from 'vue'
|
||||
import { getPushEventUser, smsTemplateGet, couponPage, pushEventPost, smsMoneyGet, smsMoneyGetFee } from '@/api/coupon'
|
||||
|
||||
const dialogVisible = ref(false)
|
||||
const smsPushEventUserObj = ref({
|
||||
sexMan: 1,
|
||||
sexWoman: 1,
|
||||
sexUnknown: 1,
|
||||
isRecharge: 0,
|
||||
noOrder: 1,
|
||||
oneOrder: 0,
|
||||
fiveOrder: 0,
|
||||
orderTimeToday: 0,
|
||||
orderTimeYesterday: 0,
|
||||
orderTimeTwoWeeks: 0,
|
||||
orderTimeMoreThanTwoWeeks: 0,
|
||||
isVip: 1,
|
||||
isRecharge: 1,
|
||||
vipLevel: 0,
|
||||
vipLevelId: 0
|
||||
})
|
||||
|
||||
const form = ref({
|
||||
id: '',
|
||||
shopId: localStorage.getItem('shopId') || '',
|
||||
pushType: 1,
|
||||
userType: 1,
|
||||
pushEventId: '',
|
||||
estimateNum: 0, // 预计人数
|
||||
content: '',
|
||||
json: '',
|
||||
coupon: '',
|
||||
sendType: 1,
|
||||
sendTime: '',
|
||||
smsPushEventUser: { ...smsPushEventUserObj.value }
|
||||
})
|
||||
|
||||
const resetForm = ref({})
|
||||
|
||||
const formRules = ref({
|
||||
pushEventId: [{ required: true, message: '请选择模板', trigger: 'change' }],
|
||||
// 校验 selectTempList
|
||||
selectTempList: [
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
// value 实际不会自动传递,需要手动取 selectTempList.value
|
||||
const fields = selectTempList.value;
|
||||
if (!fields || fields.length === 0) {
|
||||
callback();
|
||||
} else {
|
||||
for (let i = 0; i < fields.length; i++) {
|
||||
if (fields[i].value === '' || fields[i].value == null) {
|
||||
callback(new Error(`请填写${fields[i].key}`));
|
||||
return;
|
||||
}
|
||||
}
|
||||
callback();
|
||||
}
|
||||
},
|
||||
trigger: 'change'
|
||||
}
|
||||
],
|
||||
coupon: [
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
// value 是 selectCoupons.value 的 JSON 字符串
|
||||
let coupons = selectCoupons.value;
|
||||
if (coupons.length === 0) {
|
||||
// 没有添加优惠券,不校验
|
||||
callback();
|
||||
} else {
|
||||
// 校验每个优惠券
|
||||
for (let i = 0; i < coupons.length; i++) {
|
||||
if (!coupons[i].id) {
|
||||
callback(new Error('请选择优惠券'));
|
||||
return;
|
||||
}
|
||||
if (!coupons[i].num || !Number.isInteger(Number(coupons[i].num)) || Number(coupons[i].num) <= 0) {
|
||||
callback(new Error('请输入大于零的优惠券数量'));
|
||||
return;
|
||||
}
|
||||
}
|
||||
callback();
|
||||
}
|
||||
},
|
||||
trigger: 'change'
|
||||
}
|
||||
],
|
||||
sendTime: [
|
||||
{
|
||||
required: (form) => form.sendType == 2,
|
||||
message: '请选择发送时间',
|
||||
trigger: 'change'
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
// 开始提交
|
||||
const formRef = ref(null)
|
||||
const emits = defineEmits(["success"]);
|
||||
const loading = ref(false);
|
||||
const tempPrewiewContentRef = ref(null);
|
||||
|
||||
function getJsonFromFields(fields, mapping) {
|
||||
const result = {};
|
||||
// 先处理可编辑字段
|
||||
fields.forEach(f => {
|
||||
result[f.key] = f.value;
|
||||
});
|
||||
// 再处理 disabled 且有 default 的字段
|
||||
mapping.forEach(m => {
|
||||
if (m.disabled && m.default != null) {
|
||||
result[m.key] = m.default;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
function submitHandle() {
|
||||
formRef.value.validate(async (valid) => {
|
||||
try {
|
||||
if (valid) {
|
||||
loading.value = true;
|
||||
let data = { ...form.value }
|
||||
data.coupon = JSON.stringify(selectCoupons.value);
|
||||
data.content = tempPrewiewContentRef.value ? tempPrewiewContentRef.value.innerText : '';
|
||||
|
||||
// 转换 json
|
||||
data.json = JSON.stringify(getJsonFromFields(selectTempList.value, mapping));
|
||||
|
||||
await pushEventPost(data, data.id ? 'put' : 'post');
|
||||
emits("success");
|
||||
dialogVisible.value = false;
|
||||
|
||||
ElNotification({
|
||||
title: '注意',
|
||||
message: '保存成功',
|
||||
type: 'success'
|
||||
})
|
||||
|
||||
form.value = { ...resetForm.value }
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
// 获取用户列表
|
||||
const notePirce = ref(0); // 短信单价
|
||||
|
||||
// 预计费用
|
||||
const predictPrice = computed(() => {
|
||||
return multiplyAndFormat(form.value.estimateNum, notePirce.value);
|
||||
})
|
||||
|
||||
const userTableData = reactive({
|
||||
loading: false,
|
||||
list: [],
|
||||
total: 0,
|
||||
page: 1,
|
||||
pageSize: 5
|
||||
})
|
||||
// 获取推送用户
|
||||
async function getPushEventUserAjax() {
|
||||
try {
|
||||
userTableData.loading = true
|
||||
const res = await getPushEventUser({
|
||||
...form.value.smsPushEventUser,
|
||||
isAll: form.value.userType,
|
||||
shopId: form.value.shopId,
|
||||
page: userTableData.page,
|
||||
size: userTableData.pageSize
|
||||
})
|
||||
userTableData.list = res.records
|
||||
userTableData.total = res.totalRow
|
||||
form.value.estimateNum = res.totalRow
|
||||
|
||||
console.log(form.value);
|
||||
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
userTableData.loading = false
|
||||
}
|
||||
|
||||
// 分页大小发生变化
|
||||
function handleSizeChange(e) {
|
||||
userTableData.pageSize = e;
|
||||
getPushEventUserAjax();
|
||||
}
|
||||
|
||||
// 分页发生变化
|
||||
function handleCurrentChange(e) {
|
||||
userTableData.page = e;
|
||||
getPushEventUserAjax();
|
||||
}
|
||||
|
||||
// 获取商户短信余额
|
||||
const shopBalance = ref('')
|
||||
async function smsMoneyGetAjax() {
|
||||
try {
|
||||
const res = await smsMoneyGet()
|
||||
shopBalance.value = res
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
// 获取短信单价
|
||||
async function smsMoneyGetFeeAjax() {
|
||||
try {
|
||||
const res = await smsMoneyGetFee()
|
||||
console.log('获取短信单价', res);
|
||||
notePirce.value = +res.paramValue
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
// 显示弹窗
|
||||
const scrollRef = ref(null);
|
||||
async function show(obj, temp = null) {
|
||||
dialogVisible.value = true
|
||||
nextTick(() => {
|
||||
if (scrollRef.value) {
|
||||
setTimeout(() => {
|
||||
scrollRef.value.scrollTop = 0;
|
||||
}, 50);
|
||||
}
|
||||
});
|
||||
|
||||
smsMoneyGetAjax()
|
||||
smsMoneyGetFeeAjax()
|
||||
|
||||
if (obj && obj.id) {
|
||||
form.value = { ...obj }
|
||||
form.value.userType = +obj.userType
|
||||
if (obj.userType == 1) {
|
||||
form.value.smsPushEventUser = { ...smsPushEventUserObj.value }
|
||||
}
|
||||
|
||||
if (obj.pushEventId && tempList.value.length > 0) {
|
||||
// 选择模板
|
||||
selectTemp.value = tempList.value.find(item => item.id === obj.pushEventId)
|
||||
// 提取可编辑字段
|
||||
selectTempList.value = parseTemplate(selectTemp.value?.content)
|
||||
} else {
|
||||
selectTempList.value = []
|
||||
}
|
||||
|
||||
// 解析优惠券
|
||||
if (form.value.coupon) {
|
||||
try {
|
||||
const coupons = JSON.parse(form.value.coupon)
|
||||
if (Array.isArray(coupons)) {
|
||||
selectCoupons.value = coupons
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
} else {
|
||||
selectCoupons.value = []
|
||||
}
|
||||
// 选择模板
|
||||
if (form.value.pushEventId) {
|
||||
selectTempChange(form.value.pushEventId)
|
||||
// 回填字段值(只回填可编辑字段)
|
||||
if (form.value.json) {
|
||||
try {
|
||||
const jsonObj = JSON.parse(form.value.json)
|
||||
selectTempList.value = selectTempList.value.map(field => {
|
||||
if (jsonObj.hasOwnProperty(field.key)) {
|
||||
return { ...field, value: jsonObj[field.key] }
|
||||
}
|
||||
return field
|
||||
})
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
selectTempList.value = []
|
||||
}
|
||||
} else {
|
||||
form.value = { ...resetForm.value }
|
||||
selectTempList.value = []
|
||||
selectCoupons.value = []
|
||||
// 选中模板id
|
||||
if (temp && temp.id) {
|
||||
form.value.pushEventId = temp.id
|
||||
await getTempListAjax()
|
||||
selectTempChange(temp.id)
|
||||
} else {
|
||||
form.value.pushEventId = ''
|
||||
selectTemp.value = null
|
||||
selectTempList.value = []
|
||||
}
|
||||
|
||||
form.value.estimateNum = userTableData.total
|
||||
}
|
||||
}
|
||||
// 从本地获取商户信息
|
||||
const shopInfo = ref(JSON.parse(localStorage.getItem("userInfo")));
|
||||
|
||||
// 获取模板列表
|
||||
const tempList = ref([])
|
||||
const selectTemp = ref(null);
|
||||
const selectTempList = ref([])
|
||||
|
||||
// 映射表(你给的样例)
|
||||
const mapping = [
|
||||
{ key: '用户昵称', inputType: 'input', disabled: true, default: '某某某' },
|
||||
{ key: '店铺名称', inputType: 'input', disabled: true, default: shopInfo.value ? shopInfo.value.shopName : '' },
|
||||
{ key: '活动名称', inputType: 'input' },
|
||||
{ key: '活动时间', inputType: 'time' },
|
||||
{ key: '数量', inputType: 'input' },
|
||||
{ key: '金额', inputType: 'input' },
|
||||
{ key: '时间', inputType: 'time' }
|
||||
]
|
||||
|
||||
// parseTemplate: 提取占位符并映射到目标数组
|
||||
function parseTemplate(templateStr, mappingList = mapping) {
|
||||
if (!templateStr) return []
|
||||
|
||||
const regex = /\$\{\s*([^}]+?)\s*\}/g
|
||||
const seen = new Set()
|
||||
const result = []
|
||||
let match
|
||||
|
||||
while ((match = regex.exec(templateStr)) !== null) {
|
||||
const key = match[1].trim()
|
||||
if (!key || seen.has(key)) continue
|
||||
seen.add(key)
|
||||
|
||||
const map = mappingList.find(item => item.key === key)
|
||||
// 如果映射项被标记为 disabled,则不加入可编辑列表(但会用于预览默认值)
|
||||
const type = map ? (map.inputType || map.type || 'input') : 'input'
|
||||
if (map && map.disabled) {
|
||||
continue
|
||||
}
|
||||
|
||||
result.push({ key, value: '', type })
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// 选择模板
|
||||
function selectTempChange(e) {
|
||||
selectTemp.value = tempList.value.find(item => item.id == e)
|
||||
form.value.content = selectTemp.value.content;
|
||||
selectTempList.value = parseTemplate(selectTemp.value.content)
|
||||
|
||||
console.log('选择模板===', selectTempList.value);
|
||||
}
|
||||
|
||||
// 获取短信模板列表
|
||||
async function getTempListAjax() {
|
||||
try {
|
||||
const res = await smsTemplateGet()
|
||||
tempList.value = res.filter(item => item.status === 2)
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
// 选择优惠券开始
|
||||
const couponList = ref([])
|
||||
const selectCoupons = ref([]);
|
||||
const couponObj = ref({ id: '', num: 1, title: '' });
|
||||
|
||||
// 选择优惠券添加标题
|
||||
function selectCouponChnge(e, index) {
|
||||
const coupon = couponList.value.find(item => item.id === e)
|
||||
if (coupon) {
|
||||
selectCoupons.value[index].title = coupon.title
|
||||
}
|
||||
}
|
||||
|
||||
// 新增优惠券
|
||||
function addCoupon() {
|
||||
selectCoupons.value.push(_.cloneDeep(couponObj.value));
|
||||
}
|
||||
|
||||
// 获取优惠券列表
|
||||
async function couponPageAjax() {
|
||||
try {
|
||||
const res = await couponPage({
|
||||
shopId: form.value.shopId,
|
||||
page: 1,
|
||||
size: 500
|
||||
})
|
||||
couponList.value = res.records
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
// 选择优惠券结束
|
||||
|
||||
// HTML escape
|
||||
function escapeHtml(str) {
|
||||
return String(str)
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''')
|
||||
}
|
||||
|
||||
// 渲染预览 HTML
|
||||
function renderPreviewHtml(template, fields = []) {
|
||||
if (!template) return ''
|
||||
const map = {}
|
||||
fields.forEach(f => { map[f.key] = f.value != null && f.value !== '' ? escapeHtml(String(f.value)) : null })
|
||||
// 把 mapping 中 disabled 且有 default 的加入 map 作为默认值
|
||||
mapping.forEach(m => {
|
||||
if (m.disabled && m.default != null) {
|
||||
if (!map.hasOwnProperty(m.key) || map[m.key] === null) {
|
||||
map[m.key] = escapeHtml(String(m.default))
|
||||
}
|
||||
}
|
||||
})
|
||||
const replaced = template.replace(/\$\{\s*([^}]+?)\s*\}/g, (m, p1) => {
|
||||
const key = p1.trim();
|
||||
if (map.hasOwnProperty(key)) {
|
||||
return map[key] != null ? map[key] : '[未填写]'
|
||||
}
|
||||
return '[未填写]'
|
||||
})
|
||||
// 保留换行为 <br/>
|
||||
return (replaced || '').replace(/\n/g, '<br/>')
|
||||
}
|
||||
|
||||
// 预览 HTML
|
||||
const previewHtml = computed(() => {
|
||||
if (!selectTemp.value) return ''
|
||||
// 合并 title + content
|
||||
const tpl = `${selectTemp.value.title || ''}${selectTemp.value.content || ''}`
|
||||
return renderPreviewHtml(tpl, selectTempList.value)
|
||||
})
|
||||
|
||||
defineExpose({
|
||||
show
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
resetForm.value = { ...form.value }
|
||||
getPushEventUserAjax()
|
||||
getTempListAjax()
|
||||
couponPageAjax()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.title {
|
||||
color: #000;
|
||||
padding: 14px;
|
||||
background-color: #f8f8f8;
|
||||
margin-bottom: 14px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.scroll {
|
||||
height: 76vh;
|
||||
padding-bottom: 60px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
|
||||
&:not(:first-child) {
|
||||
margin-top: 14px;
|
||||
}
|
||||
|
||||
.del {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.shop_user_wrap {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
gap: 14px;
|
||||
|
||||
.item {
|
||||
flex: 1;
|
||||
|
||||
.temp_preview {
|
||||
border: 1px solid #D9D9D9;
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
background-color: #F8F8F8;
|
||||
padding: 14px;
|
||||
|
||||
.temp_preview_title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
.temp_preview_content {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
line-height: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.item2 {
|
||||
width: 400px;
|
||||
margin-right: 50px;
|
||||
|
||||
.user_wrap {
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
|
||||
.user_header {
|
||||
padding: 20px 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background-color: #F8F8F8;
|
||||
|
||||
span {
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.list {
|
||||
--count: 5;
|
||||
--itemHeight: 70px;
|
||||
height: calc(var(--count) * var(--itemHeight) + 30px);
|
||||
|
||||
.item {
|
||||
height: var(--itemHeight);
|
||||
border-bottom: 1px solid #ddd;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 14px;
|
||||
|
||||
.avatar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.info {
|
||||
flex: 1;
|
||||
margin-left: 10px;
|
||||
|
||||
.name {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.info_wrap {
|
||||
margin-top: 4px;
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
display: flex;
|
||||
|
||||
span {
|
||||
&:nth-child(1) {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
&:nth-child(3) {
|
||||
flex: 1.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
202
src/views/marketing_center/note_push/components/addTemplate.vue
Normal file
202
src/views/marketing_center/note_push/components/addTemplate.vue
Normal file
@@ -0,0 +1,202 @@
|
||||
<template>
|
||||
<el-dialog v-model="dialogVisible" title="申请短信模板" width="800px">
|
||||
<!-- <div class="title">填写模板信息并提交审核,审核通过后可用于短信推送</div> -->
|
||||
<div class="gyq_flex">
|
||||
<div class="item">
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-position="left" label-width="120px">
|
||||
<el-form-item label="模板名称" prop="title">
|
||||
<el-input placeholder="请输入内容" v-model="form.title" :maxlength="30" style="width: 300px;"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="模版内容" prop="title">
|
||||
<div style="width: 300px;">
|
||||
<editorDiv ref="editorDivRef" v-model="form.content" />
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="使用场景">
|
||||
<div class="ipt_wrap">
|
||||
<el-input type="textarea" :rows="8" :maxlength="500"
|
||||
placeholder="为了提高您短信模板审核的通过率,请您详细描述您的短信模板的使用场景(例如:说明该短信在何种情境下发送,以及短信接收对象等)如果短信中涉及到变量,也请详细说明变量的内容。此外,您也可以提供一份完整的、已填入变量内容的短停样例以供参考"
|
||||
v-model="form.sceneDetail" style="width: 300px;"></el-input>
|
||||
<span class="n">{{ form.sceneDetail.length }}/500</span>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="preview_wrap">
|
||||
<div class="preview_title">模板示例</div>
|
||||
<div class="preview">
|
||||
尊敬的{姓名},您的会员卡将于{到期日期}到期,请及时续费享受会员权益
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" :loading="loading" @click="submitHandle">确 定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import editorDiv from './editorDiv.vue'
|
||||
import { smsTemplate, smsTemplateResubmit } from '@/api/coupon/index'
|
||||
import { ElNotification } from 'element-plus'
|
||||
|
||||
const editorDivRef = ref(null)
|
||||
|
||||
const form = ref({
|
||||
id: '',
|
||||
shopId: '',
|
||||
title: '',
|
||||
content: '',
|
||||
sceneDetail: '',
|
||||
})
|
||||
|
||||
const resetForm = ref('')
|
||||
|
||||
const rules = {
|
||||
title: [
|
||||
{ required: true, message: '请输入模板名称', trigger: 'blur' },
|
||||
{ min: 1, max: 100, message: '长度在 1 到 100 个字符', trigger: 'blur' }
|
||||
],
|
||||
content: [
|
||||
{ required: true, message: '请输入模版内容', trigger: 'blur' },
|
||||
{ min: 1, max: 500, message: '长度在 1 到 500 个字符', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
|
||||
// 开始提交
|
||||
const formRef = ref(null)
|
||||
const emits = defineEmits(["success"]);
|
||||
const loading = ref(false);
|
||||
function submitHandle() {
|
||||
formRef.value.validate(async (valid) => {
|
||||
try {
|
||||
if (valid) {
|
||||
loading.value = true;
|
||||
let data = { ...form.value }
|
||||
data.content = `${data.content},拒收请回复R`
|
||||
if (data.id) {
|
||||
await smsTemplateResubmit(data);
|
||||
} else {
|
||||
await smsTemplate(data);
|
||||
}
|
||||
emits("success");
|
||||
dialogVisible.value = false;
|
||||
|
||||
ElNotification({
|
||||
title: '注意',
|
||||
message: '保存成功',
|
||||
type: 'success'
|
||||
})
|
||||
|
||||
form.value = { ...resetForm.value }
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
// 从本地获取商户信息
|
||||
const shopInfo = ref("");
|
||||
function getLocalShopInfo() {
|
||||
shopInfo.value = JSON.parse(localStorage.getItem("userInfo"));
|
||||
}
|
||||
|
||||
const dialogVisible = ref(false)
|
||||
function show(obj = null) {
|
||||
if (obj && obj.id) {
|
||||
form.value = { ...obj }
|
||||
} else {
|
||||
nextTick(() => {
|
||||
editorDivRef.value?.reset()
|
||||
})
|
||||
form.value = { ...resetForm.value }
|
||||
}
|
||||
dialogVisible.value = true;
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
show,
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
resetForm.value = { ...form.value }
|
||||
getLocalShopInfo()
|
||||
form.value.shopId = shopInfo.value.shopId
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.editor {
|
||||
min-height: 40px;
|
||||
border: 1px solid #ccc;
|
||||
padding: 8px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.title {
|
||||
color: #000;
|
||||
padding: 14px;
|
||||
background-color: #f8f8f8;
|
||||
margin-bottom: 14px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.gyq_flex {
|
||||
display: flex;
|
||||
gap: 14px;
|
||||
|
||||
.item {
|
||||
flex: 1;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.column {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.column_item {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
.ipt_wrap {
|
||||
position: relative;
|
||||
|
||||
.n {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
bottom: 0;
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.preview_wrap {
|
||||
border-radius: 4px;
|
||||
border: 1px solid #D9D9D9;
|
||||
background-color: #F8F8F8;
|
||||
padding: 14px;
|
||||
|
||||
.preview_title {
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.preview {
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
424
src/views/marketing_center/note_push/components/editorDiv.vue
Normal file
424
src/views/marketing_center/note_push/components/editorDiv.vue
Normal file
@@ -0,0 +1,424 @@
|
||||
<template>
|
||||
<div class="wrap">
|
||||
<div class="editor_div" ref="editorRef" contenteditable></div>
|
||||
<div class="tag_list">
|
||||
<el-tag v-for="tag in dynamicTags" :key="tag" :closable="tag.type == 'custom'" disable-transitions
|
||||
@close="handleClose(tag)" @mousedown.prevent="saveRange" @click="selectTag(tag)" style="cursor: pointer;"
|
||||
size="large">
|
||||
{{ tag.label }}
|
||||
</el-tag>
|
||||
<div v-if="inputVisible" style="width: 92px;"><el-input ref="InputRef" v-model="inputValue" placeholder="请输入"
|
||||
@keyup.enter="handleInputConfirm" @blur="handleInputConfirm" /></div>
|
||||
<el-button v-else class="button-new-tag" @click="showInput">
|
||||
+标签
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="tips">变量限制:不支持QQ号、微信号、网址信息</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, nextTick, onBeforeUnmount, defineProps, defineEmits } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
const editorRef = ref(null)
|
||||
let lastRange = null // 记录光标位置
|
||||
let lastInsertTime = 0 // 防止快速连续插入
|
||||
let needsInput = false // 插入后必须输入内容才能再次插入
|
||||
const InputRef = ref(null)
|
||||
const inputValue = ref('')
|
||||
const inputVisible = ref(false)
|
||||
const dynamicTags = ref([
|
||||
{
|
||||
label: '用户昵称',
|
||||
value: '${用户昵称}',
|
||||
type: 'fix'
|
||||
},
|
||||
{
|
||||
label: '店铺名称',
|
||||
value: '${店铺名称}',
|
||||
type: 'fix'
|
||||
},
|
||||
{
|
||||
label: '活动名称',
|
||||
value: '${活动名称}',
|
||||
type: 'fix'
|
||||
},
|
||||
{
|
||||
label: '活动时间',
|
||||
value: '${活动时间}',
|
||||
type: 'fix'
|
||||
},
|
||||
{
|
||||
label: '数量',
|
||||
value: '${数量}',
|
||||
type: 'fix'
|
||||
},
|
||||
{
|
||||
label: '金额',
|
||||
value: '${金额}',
|
||||
type: 'fix'
|
||||
},
|
||||
{
|
||||
label: '时间',
|
||||
value: '${时间}',
|
||||
type: 'fix'
|
||||
}
|
||||
])
|
||||
function handleClose(tag) {
|
||||
const index = dynamicTags.value.indexOf(tag);
|
||||
dynamicTags.value.splice(index, 1);
|
||||
}
|
||||
function showInput() {
|
||||
inputVisible.value = true;
|
||||
nextTick(() => {
|
||||
InputRef.value.$el.querySelector('input').focus();
|
||||
});
|
||||
}
|
||||
function handleInputConfirm() {
|
||||
if (inputValue.value && dynamicTags.value.indexOf(inputValue.value) === -1
|
||||
) {
|
||||
dynamicTags.value.push({ label: inputValue.value, value: '${' + inputValue.value + '}', type: 'custom' });
|
||||
}
|
||||
inputVisible.value = false;
|
||||
inputValue.value = '';
|
||||
}
|
||||
|
||||
// 记录 selection range(提升到模块作用域,便于模板调用)
|
||||
function saveRange() {
|
||||
const sel = window.getSelection();
|
||||
if (sel && sel.rangeCount > 0) {
|
||||
const range = sel.getRangeAt(0);
|
||||
// 只在光标在 editorRef 内部时才记录
|
||||
if (editorRef.value && editorRef.value.contains(range.startContainer)) {
|
||||
lastRange = range.cloneRange();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 选择标签
|
||||
function selectTag(tag) {
|
||||
// 防抖:300ms 内再次触发插入判定为重复,忽略
|
||||
const now = Date.now();
|
||||
if (now - lastInsertTime < 300) {
|
||||
return;
|
||||
}
|
||||
// 如果上次插入后还没输入任何可见字符,则禁止再次插入
|
||||
if (needsInput) {
|
||||
ElMessage.warning('请输入内容');
|
||||
return;
|
||||
}
|
||||
// 新需求:同一种标签只能存在一个,如果已存在则提示并阻止插入
|
||||
if (editorRef.value) {
|
||||
const existing = Array.from(editorRef.value.querySelectorAll('.tag-chip'))
|
||||
.some(el => el.dataset && el.dataset.value === tag.value);
|
||||
if (existing) {
|
||||
ElMessage.warning('已存在该标签');
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (editorRef.value) {
|
||||
// 如果光标前紧邻一个变量标签且两者之间没有其他可见字符,则禁止连续插入
|
||||
function isCaretAfterTag(range) {
|
||||
if (!range) return false;
|
||||
const sc = range.startContainer;
|
||||
const offset = range.startOffset;
|
||||
// 辅助:去除零宽空格并判断文本是否为空
|
||||
const textEmpty = (txt) => !txt || txt.replace(/\u200B/g, '').trim().length === 0;
|
||||
|
||||
// 如果在文本节点内
|
||||
if (sc.nodeType === Node.TEXT_NODE) {
|
||||
const before = sc.textContent.slice(0, offset);
|
||||
if (!textEmpty(before)) return false;
|
||||
// 向前查找非空白节点
|
||||
let p = sc.previousSibling;
|
||||
while (p) {
|
||||
if (p.nodeType === Node.TEXT_NODE) {
|
||||
if (!textEmpty(p.textContent)) return false;
|
||||
p = p.previousSibling; continue;
|
||||
}
|
||||
if (p.nodeType === Node.ELEMENT_NODE) {
|
||||
return !!(p.dataset && p.dataset.value);
|
||||
}
|
||||
p = p.previousSibling;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 如果在元素节点内
|
||||
if (sc.nodeType === Node.ELEMENT_NODE) {
|
||||
if (offset === 0) {
|
||||
// 在元素开始位置,检查元素的前一个兄弟节点
|
||||
let p = sc.previousSibling;
|
||||
while (p) {
|
||||
if (p.nodeType === Node.TEXT_NODE) {
|
||||
if (!textEmpty(p.textContent)) return false;
|
||||
p = p.previousSibling; continue;
|
||||
}
|
||||
if (p.nodeType === Node.ELEMENT_NODE) {
|
||||
return !!(p.dataset && p.dataset.value);
|
||||
}
|
||||
p = p.previousSibling;
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
const nodeBefore = sc.childNodes[offset - 1];
|
||||
if (!nodeBefore) return false;
|
||||
if (nodeBefore.nodeType === Node.ELEMENT_NODE) {
|
||||
return !!(nodeBefore.dataset && nodeBefore.dataset.value);
|
||||
}
|
||||
if (nodeBefore.nodeType === Node.TEXT_NODE) {
|
||||
if (!textEmpty(nodeBefore.textContent)) return false;
|
||||
// 再向前找
|
||||
let p = nodeBefore.previousSibling;
|
||||
while (p) {
|
||||
if (p.nodeType === Node.TEXT_NODE) {
|
||||
if (!textEmpty(p.textContent)) return false;
|
||||
p = p.previousSibling; continue;
|
||||
}
|
||||
if (p.nodeType === Node.ELEMENT_NODE) {
|
||||
return !!(p.dataset && p.dataset.value);
|
||||
}
|
||||
p = p.previousSibling;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查当前保存的 lastRange 或当前 selection
|
||||
const checkRange = lastRange || (window.getSelection() && window.getSelection().rangeCount ? window.getSelection().getRangeAt(0) : null);
|
||||
// 进一步:如果光标前最近的可见节点就是相同变量标签,禁止插入(防止首次双插入)
|
||||
function getNodeBeforeCaret(range) {
|
||||
if (!range) return null;
|
||||
const sc = range.startContainer;
|
||||
const offset = range.startOffset;
|
||||
// 如果在文本节点内,从文本节点切片前部判断
|
||||
if (sc.nodeType === Node.TEXT_NODE) {
|
||||
// 若文本前有非空白字符则返回 null
|
||||
const beforeText = sc.textContent.slice(0, offset).replace(/\u200B/g, '');
|
||||
if (beforeText.trim().length > 0) return null;
|
||||
// 检查前一个兄弟
|
||||
let p = sc.previousSibling;
|
||||
while (p) {
|
||||
if (p.nodeType === Node.TEXT_NODE) {
|
||||
if (p.textContent.replace(/\u200B/g, '').trim().length > 0) return null;
|
||||
p = p.previousSibling; continue;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
return sc.parentNode && sc.parentNode.previousSibling ? sc.parentNode.previousSibling : null;
|
||||
}
|
||||
// 如果在元素节点
|
||||
if (sc.nodeType === Node.ELEMENT_NODE) {
|
||||
if (offset === 0) {
|
||||
return sc.previousSibling;
|
||||
}
|
||||
return sc.childNodes[offset - 1] || null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
const nodeBefore = getNodeBeforeCaret(checkRange);
|
||||
if (nodeBefore && nodeBefore.nodeType === Node.ELEMENT_NODE) {
|
||||
const el = nodeBefore;
|
||||
if (el.dataset && el.dataset.value && el.dataset.value === tag.value) {
|
||||
ElMessage.warning('请输入内容');
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (checkRange && isCaretAfterTag(checkRange)) {
|
||||
ElMessage.warning('请输入内容');
|
||||
return;
|
||||
}
|
||||
// 先恢复光标位置(不要先 focus,focus 可能改变 selection)
|
||||
// 创建一个span标签用于插入
|
||||
const span = document.createElement('span');
|
||||
span.textContent = tag.label;
|
||||
span.contentEditable = "false";
|
||||
span.style.display = "inline-block";
|
||||
span.style.background = "#f0f0f0";
|
||||
span.style.borderRadius = "4px";
|
||||
span.style.padding = "0 4px";
|
||||
span.style.fontSize = "12px";
|
||||
span.style.margin = "0 2px";
|
||||
span.style.cursor = "pointer";
|
||||
span.style.border = "1px solid #dcdcdc";
|
||||
span.style.userSelect = "none";
|
||||
// 关键:保存变量值到 dataset
|
||||
span.dataset.value = tag.value;
|
||||
span.dataset.type = tag.type || 'custom';
|
||||
span.className = 'tag-chip';
|
||||
// 添加删除按钮
|
||||
const closeBtn = document.createElement('span');
|
||||
closeBtn.textContent = ' ×';
|
||||
closeBtn.style.color = "#999";
|
||||
closeBtn.style.cursor = "pointer";
|
||||
closeBtn.className = 'tag-close';
|
||||
closeBtn.onclick = function (e) {
|
||||
e.stopPropagation();
|
||||
span.remove();
|
||||
emitValue();
|
||||
};
|
||||
span.appendChild(closeBtn);
|
||||
|
||||
// 恢复光标位置
|
||||
let sel = window.getSelection();
|
||||
let inserted = false;
|
||||
if (lastRange) {
|
||||
sel.removeAllRanges();
|
||||
sel.addRange(lastRange);
|
||||
// 插入到光标处
|
||||
if (sel && sel.rangeCount > 0) {
|
||||
const range = sel.getRangeAt(0);
|
||||
range.collapse(false);
|
||||
range.insertNode(span);
|
||||
// 光标移到span后
|
||||
range.setStartAfter(span);
|
||||
range.setEndAfter(span);
|
||||
sel.removeAllRanges();
|
||||
sel.addRange(range);
|
||||
// 更新 lastRange
|
||||
lastRange = range.cloneRange();
|
||||
inserted = true;
|
||||
}
|
||||
}
|
||||
// 插入完成后确保 editor 聚焦(在插入之后调用 focus 更稳妥)
|
||||
if (editorRef.value) {
|
||||
try { editorRef.value.focus(); } catch (e) { }
|
||||
}
|
||||
// 如果没有光标,则插入到内容尾部
|
||||
if (!inserted) {
|
||||
editorRef.value.appendChild(span);
|
||||
}
|
||||
// 插入后 emit 内容
|
||||
emitValue();
|
||||
lastInsertTime = Date.now();
|
||||
// 设置需要输入标志,直到用户输入可见字符
|
||||
needsInput = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 获取序列化内容(如:尊共的${username})
|
||||
function getSerializedContent() {
|
||||
if (!editorRef.value) return '';
|
||||
function walk(node) {
|
||||
let out = '';
|
||||
node.childNodes.forEach(n => {
|
||||
if (n.nodeType === Node.TEXT_NODE) {
|
||||
out += n.textContent;
|
||||
} else if (n.nodeType === Node.ELEMENT_NODE) {
|
||||
// 如果是我们插入的 tag 节点并且有 data-value,则用 data-value
|
||||
const el = n;
|
||||
if (el.dataset && el.dataset.value) {
|
||||
out += el.dataset.value;
|
||||
} else {
|
||||
out += walk(el);
|
||||
}
|
||||
}
|
||||
});
|
||||
return out;
|
||||
}
|
||||
return walk(editorRef.value);
|
||||
}
|
||||
|
||||
// emit 内容给父组件
|
||||
function emitValue() {
|
||||
emit('update:modelValue', getSerializedContent());
|
||||
}
|
||||
|
||||
// 重置编辑器内容和内部状态
|
||||
function reset() {
|
||||
if (editorRef.value) {
|
||||
// 清空内容
|
||||
editorRef.value.innerHTML = '';
|
||||
}
|
||||
// 重置状态
|
||||
lastRange = null;
|
||||
lastInsertTime = 0;
|
||||
needsInput = false;
|
||||
inputValue.value = '';
|
||||
inputVisible.value = false;
|
||||
// 通知父组件内容清空
|
||||
emit('update:modelValue', '');
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
const events = ['input', 'keyup', 'mouseup', 'blur', 'focus'];
|
||||
events.forEach((ev) => editorRef.value && editorRef.value.addEventListener(ev, saveRange));
|
||||
// input 事件实时 emit 内容
|
||||
editorRef.value && editorRef.value.addEventListener('input', emitValue);
|
||||
// 当用户输入可见字符时,允许再次插入
|
||||
const inputHandler = (e) => {
|
||||
const text = editorRef.value ? editorRef.value.innerText.replace(/\u200B/g, '').trim() : '';
|
||||
if (text.length > 0) {
|
||||
needsInput = false;
|
||||
}
|
||||
}
|
||||
editorRef.value && editorRef.value.addEventListener('input', inputHandler);
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
events.forEach((ev) => editorRef.value && editorRef.value.removeEventListener(ev, saveRange));
|
||||
editorRef.value && editorRef.value.removeEventListener('input', emitValue);
|
||||
editorRef.value && editorRef.value.removeEventListener('input', inputHandler);
|
||||
});
|
||||
})
|
||||
|
||||
// 暴露方法给父组件
|
||||
defineExpose({
|
||||
reset
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.wrap {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.tag_list {
|
||||
padding: 10px 0;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.tips {
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.editor_div {
|
||||
flex: 1;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
min-height: 100px;
|
||||
padding: 10px 10px;
|
||||
transition: all .1s ease-in-out;
|
||||
|
||||
&:focus {
|
||||
border-color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
/* 核心:placeholder样式 */
|
||||
&:empty:not(:focus):before {
|
||||
content: '请输入内容';
|
||||
/* placeholder文本 */
|
||||
color: rgb(168, 168, 168);
|
||||
/* 占位符颜色 */
|
||||
pointer-events: none;
|
||||
font-size: 14px;
|
||||
/* 点击时穿透到容器 */
|
||||
}
|
||||
}
|
||||
</style>
|
||||
47
src/views/marketing_center/note_push/index.vue
Normal file
47
src/views/marketing_center/note_push/index.vue
Normal file
@@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<div class="gyq_container">
|
||||
<div class="gyq_content">
|
||||
<headerCard icon="dxts" name="短信推送" intro="给用户推送服务通知" />
|
||||
<div class="row">
|
||||
<el-tabs v-model="activeKey">
|
||||
<el-tab-pane label="群发短信" :name="1">
|
||||
<noteList ref="noteListRef" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="用量记录" :name="2">
|
||||
<record />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="模版管理" :name="3">
|
||||
<templateManage @useTemplate="useTemp" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import noteList from './noteList.vue'
|
||||
import record from './record.vue'
|
||||
import templateManage from './templateManage.vue'
|
||||
|
||||
const activeKey = ref(1)
|
||||
|
||||
const noteListRef = ref(null)
|
||||
function useTemp(item) {
|
||||
activeKey.value = 1
|
||||
noteListRef.value?.useTemplate(item)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.gyq_container {
|
||||
padding: 14px;
|
||||
|
||||
.gyq_content {
|
||||
padding: 14px;
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
115
src/views/marketing_center/note_push/noteList.vue
Normal file
115
src/views/marketing_center/note_push/noteList.vue
Normal file
@@ -0,0 +1,115 @@
|
||||
<!-- 群发短信 -->
|
||||
<template>
|
||||
<div>
|
||||
<div class="row">
|
||||
<el-button type="primary" @click="addTaskRef?.show()">添加任务</el-button>
|
||||
</div>
|
||||
<div class="row">
|
||||
<el-table :data="tableData.list" stripe border v-loading="tableData.loading">
|
||||
<el-table-column label="ID" prop="id"></el-table-column>
|
||||
<el-table-column label="短信内容" prop="content" width="300"></el-table-column>
|
||||
<el-table-column label="优惠券信息" prop="coupon"></el-table-column>
|
||||
<el-table-column label="发送对象" prop="userType"></el-table-column>
|
||||
<el-table-column label="发送人数「预计」" prop="estimateNum" width="150"></el-table-column>
|
||||
<el-table-column label="发送时间" prop="sendTime"></el-table-column>
|
||||
<el-table-column label="状态" prop="status"></el-table-column>
|
||||
<el-table-column label="创建时间" prop="createTime"></el-table-column>
|
||||
<el-table-column label="操作" fixed="right" width="120">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" v-if="scope.row.status != 2"
|
||||
@click="addTaskRef.show(scope.row)">编辑</el-button>
|
||||
<el-popconfirm title="确认要删除吗?" @confirm="deleteHandle(scope.row)">
|
||||
<template #reference>
|
||||
<el-button type="danger" link>删除</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<div class="row">
|
||||
<el-pagination v-model:current-page="tableData.page" v-model:page-size="tableData.size"
|
||||
:page-sizes="[100, 200, 300, 400]" background layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="tableData.total" @size-change="handleSizeChange" @current-change="handleCurrentChange" />
|
||||
</div>
|
||||
<addTask ref="addTaskRef" @success="getTableData" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive } from 'vue'
|
||||
import addTask from './components/addTask.vue';
|
||||
import { pushEventGet, pushEventDel } from '@/api/coupon'
|
||||
|
||||
const addTaskRef = ref(null)
|
||||
|
||||
const tableData = reactive({
|
||||
loading: false,
|
||||
page: 1,
|
||||
size: 10,
|
||||
list: []
|
||||
})
|
||||
|
||||
// 删除
|
||||
async function deleteHandle(row) {
|
||||
try {
|
||||
tableData.loading = true;
|
||||
await pushEventDel(row.id);
|
||||
ElNotification({
|
||||
title: '注意',
|
||||
message: '已删除',
|
||||
type: 'success'
|
||||
})
|
||||
getTableData();
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
|
||||
// 分页大小发生变化
|
||||
function handleSizeChange(e) {
|
||||
tableData.pageSize = e;
|
||||
getTableData();
|
||||
}
|
||||
|
||||
// 分页发生变化
|
||||
function handleCurrentChange(e) {
|
||||
tableData.page = e;
|
||||
getTableData();
|
||||
}
|
||||
|
||||
// 获取列表
|
||||
const getTableData = async () => {
|
||||
try {
|
||||
tableData.loading = true;
|
||||
const res = await pushEventGet({
|
||||
page: tableData.page,
|
||||
size: tableData.size
|
||||
});
|
||||
tableData.list = res.records;
|
||||
tableData.total = res.totalRow;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
tableData.loading = false;
|
||||
}
|
||||
|
||||
// 添加模板,并且默认选中模板
|
||||
function useTemplate(item) {
|
||||
addTaskRef.value?.show(null, item)
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
useTemplate
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
getTableData();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.row {
|
||||
padding-top: 14px;
|
||||
}
|
||||
</style>
|
||||
120
src/views/marketing_center/note_push/record.vue
Normal file
120
src/views/marketing_center/note_push/record.vue
Normal file
@@ -0,0 +1,120 @@
|
||||
<!-- 用量记录 -->
|
||||
<template>
|
||||
<div>
|
||||
<div class="data_show">
|
||||
<div class="data_list">
|
||||
<div class="item">
|
||||
<div class="title">
|
||||
返现总金额
|
||||
</div>
|
||||
<div class="num">
|
||||
{{ tableData.sendTotal }}条/¥{{ multiplyAndFormat(tableData.sendAmountTotal, 1) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table" style="width: 100%;">
|
||||
<el-table :data="tableData.list" stripe border v-loading="tableData.loading" height="500px">
|
||||
<el-table-column label="ID" prop="id"></el-table-column>
|
||||
<el-table-column label="记录类型" prop="type">
|
||||
<template #default="scope">
|
||||
<div class="column">
|
||||
<div v-if="scope.row.type === 1">增加</div>
|
||||
<div v-else-if="scope.row.type === 2">减少</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="变动原因" prop="reason"></el-table-column>
|
||||
<el-table-column label="变动前余额" prop="balance"></el-table-column>
|
||||
<el-table-column label="变动金额" prop="expense"></el-table-column>
|
||||
<el-table-column label="创建时间" prop="createTime"></el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<div class="row" style="margin-top: 14px;">
|
||||
<el-pagination v-model:current-page="tableData.page" v-model:page-size="tableData.size"
|
||||
:page-sizes="[100, 200, 300, 400]" background layout="total, sizes, prev, pager, next, jumper"
|
||||
:page-count="tableData.total" @size-change="handleSizeChange" @current-change="handleCurrentChange" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted } from 'vue'
|
||||
import { smsMoneyDetail } from '@/api/coupon/index.js'
|
||||
import { multiplyAndFormat } from '@/utils'
|
||||
|
||||
const tableData = reactive({
|
||||
sendAmountTotal: 0,
|
||||
sendTotal: 0,
|
||||
loading: false,
|
||||
page: 1,
|
||||
size: 10,
|
||||
list: []
|
||||
})
|
||||
|
||||
// 分页大小发生变化
|
||||
function handleSizeChange(e) {
|
||||
tableData.pageSize = e;
|
||||
getTableData();
|
||||
}
|
||||
|
||||
// 分页发生变化
|
||||
function handleCurrentChange(e) {
|
||||
tableData.page = e;
|
||||
getTableData();
|
||||
}
|
||||
|
||||
// 获取店铺短信余额明细
|
||||
async function getTableData(params) {
|
||||
try {
|
||||
tableData.loading = true
|
||||
const res = await smsMoneyDetail({
|
||||
page: 1,
|
||||
size: 10
|
||||
})
|
||||
tableData.totalAmount = res.totalAmount
|
||||
tableData.list = res.records
|
||||
tableData.total = +res.totalRow
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
tableData.loading = false
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getTableData()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.data_show {
|
||||
padding: 0 0 14px;
|
||||
|
||||
.data_list {
|
||||
display: flex;
|
||||
|
||||
.item {
|
||||
width: 300px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
padding: 14px;
|
||||
|
||||
.title {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.num {
|
||||
font-size: 24px;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
line-height: 16px;
|
||||
}
|
||||
</style>
|
||||
155
src/views/marketing_center/note_push/templateManage.vue
Normal file
155
src/views/marketing_center/note_push/templateManage.vue
Normal file
@@ -0,0 +1,155 @@
|
||||
<!-- 模板管理 -->
|
||||
<template>
|
||||
<div>
|
||||
<div class="row">
|
||||
<el-button type="primary" @click="addTemplateRef.show()">申请新模板</el-button>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="title">可用模板</div>
|
||||
<div class="card_wrap" :class="[`card_wrap${useData.length}`]">
|
||||
<div class="card" v-for="item in useData" :key="item.id">
|
||||
<div class="card_title">
|
||||
<span>{{ item.title }}</span>
|
||||
<el-button type="primary" @click="toUse(item)">去使用</el-button>
|
||||
</div>
|
||||
<div class="card_content">
|
||||
{{ item.content }}
|
||||
</div>
|
||||
</div>
|
||||
<el-empty description="没有数据" v-if="useData.length == 0" />
|
||||
</div>
|
||||
<div class="title">我申请的模板</div>
|
||||
<div class="card_wrap" :class="[`card_wrap${applyData.length}`]">
|
||||
<div class="card" v-for="item in applyData" :key="item.id">
|
||||
<div class="card_title">
|
||||
<span>{{ item.title }}</span>
|
||||
<el-button :type="statusOptions?.find(val => val.value == item.status).type" plain
|
||||
@click="toApplayHandle(item)">
|
||||
{{statusOptions?.find(val => val.value == item.status).label}}
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="card_content">
|
||||
{{ item.content }}
|
||||
</div>
|
||||
</div>
|
||||
<el-empty description="没有数据" v-if="applyData.length == 0" />
|
||||
</div>
|
||||
</div>
|
||||
<addTemplate ref="addTemplateRef" @success="smsTemplateGetAjax" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import addTemplate from './components/addTemplate.vue';
|
||||
import { smsTemplateGet } from '@/api/coupon/index'
|
||||
|
||||
const addTemplateRef = ref(null)
|
||||
|
||||
const statusOptions = ref([
|
||||
{
|
||||
label: '待审核',
|
||||
value: 0,
|
||||
type: 'info'
|
||||
},
|
||||
{
|
||||
label: '审核中',
|
||||
value: 1,
|
||||
type: 'warning'
|
||||
},
|
||||
{
|
||||
label: '已通过',
|
||||
value: 2,
|
||||
type: 'success'
|
||||
},
|
||||
{
|
||||
label: '未通过',
|
||||
value: -1,
|
||||
type: 'danger'
|
||||
},
|
||||
{
|
||||
label: '待审核',
|
||||
value: -2,
|
||||
type: 'info'
|
||||
}
|
||||
])
|
||||
|
||||
function toApplayHandle(item) {
|
||||
if (item.status === -1) {
|
||||
// 已通过 去使用
|
||||
addTemplateRef.value.show(item)
|
||||
}
|
||||
}
|
||||
|
||||
const useData = ref([]) // 可用模板
|
||||
const applyData = ref([]) // 申请模板
|
||||
|
||||
// 获取模板列表
|
||||
async function smsTemplateGetAjax() {
|
||||
try {
|
||||
const res = await smsTemplateGet()
|
||||
useData.value = res.filter(item => item.status === 2)
|
||||
applyData.value = res.filter(item => item.status !== 2)
|
||||
} catch (error) {
|
||||
console.log('error', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 跳转去使用模板
|
||||
const emit = defineEmits(['useTemplate'])
|
||||
function toUse(item) {
|
||||
emit('useTemplate', item)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
smsTemplateGetAjax()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.row {
|
||||
padding-top: 14px;
|
||||
}
|
||||
|
||||
.title {
|
||||
color: #333;
|
||||
padding-bottom: 14px;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.card_wrap {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
grid-template-rows: auto;
|
||||
grid-column-gap: 14px;
|
||||
grid-row-gap: 14px;
|
||||
margin-bottom: 24px;
|
||||
|
||||
&.card_wrap0 {
|
||||
grid-template-columns: repeat(1, 1fr);
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: 24px 14px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #ddd;
|
||||
|
||||
.card_title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
span {
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.card_content {
|
||||
color: #666;
|
||||
padding-top: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,23 +1,11 @@
|
||||
<template>
|
||||
<div class="gyq_container">
|
||||
<div class="gyq_content">
|
||||
<HeaderCard
|
||||
name="智慧充值"
|
||||
intro="允许客户充值并使用余额支付"
|
||||
icon="zhcz"
|
||||
showSwitch
|
||||
v-model:isOpen="form.isEnable"
|
||||
></HeaderCard>
|
||||
<HeaderCard name="智慧充值" intro="允许客户充值并使用余额支付" icon="zhcz" showSwitch v-model:isOpen="form.isEnable"></HeaderCard>
|
||||
<div style="padding-top: 14px">
|
||||
<el-tabs v-model="tabsValue">
|
||||
<el-tab-pane label="基础设置" :name="1">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-width="120"
|
||||
label-position="right"
|
||||
>
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="120" label-position="right">
|
||||
<el-form-item label="充值面额" required>
|
||||
<el-button type="primary" @click="AddDialogRef.open()">
|
||||
添加面额
|
||||
@@ -26,18 +14,9 @@
|
||||
<el-form-item prop="rechargeDetailList">
|
||||
<el-table :data="form.rechargeDetailList" border stripe>
|
||||
<el-table-column label="ID" prop="id"></el-table-column>
|
||||
<el-table-column
|
||||
label="充值金额(元)"
|
||||
prop="amount"
|
||||
></el-table-column>
|
||||
<el-table-column
|
||||
label="赠送金额"
|
||||
prop="rewardAmount"
|
||||
></el-table-column>
|
||||
<el-table-column
|
||||
label="赠送积分"
|
||||
prop="rewardPoints"
|
||||
></el-table-column>
|
||||
<el-table-column label="充值金额(元)" prop="amount"></el-table-column>
|
||||
<el-table-column label="赠送金额" prop="rewardAmount"></el-table-column>
|
||||
<el-table-column label="赠送积分" prop="rewardPoints"></el-table-column>
|
||||
<el-table-column label="赠送优惠券" prop="couponInfoList">
|
||||
<template #default="scope">
|
||||
<div class="column">
|
||||
@@ -53,60 +32,34 @@
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="120">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
@click="AddDialogRef.open(scope.row, scope.$index)"
|
||||
>
|
||||
<el-button type="primary" link @click="AddDialogRef.open(scope.row, scope.$index)">
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
link
|
||||
@click="
|
||||
form.rechargeDetailList.splice(scope.$index, 1)
|
||||
"
|
||||
>
|
||||
<el-button type="danger" link @click="
|
||||
form.rechargeDetailList.splice(scope.$index, 1)
|
||||
">
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
label="选择门店"
|
||||
prop="useType"
|
||||
v-if="shopInfo.isHeadShop"
|
||||
>
|
||||
<el-form-item label="选择门店" prop="useType" v-if="shopInfo.isHeadShop">
|
||||
<el-radio-group v-model="form.useType">
|
||||
<el-radio label="全部门店" value="all"></el-radio>
|
||||
<el-radio label="指定门店可用" value="part"></el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="选择门店" v-if="form.useType == 'part'">
|
||||
<el-select
|
||||
v-model="form.shopIdList"
|
||||
multiple
|
||||
clearable
|
||||
placeholder="请选择门店"
|
||||
style="width: 300px"
|
||||
>
|
||||
<el-option
|
||||
:label="item.shopName"
|
||||
:value="item.id"
|
||||
v-for="item in branchList"
|
||||
:key="item.id"
|
||||
></el-option>
|
||||
<el-select v-model="form.shopIdList" multiple clearable placeholder="请选择门店" style="width: 300px">
|
||||
<el-option :label="item.shopName" :value="item.id" v-for="item in branchList"
|
||||
:key="item.id"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="自定义金额">
|
||||
<div class="column">
|
||||
<div class="center">
|
||||
<el-switch
|
||||
v-model="form.isCustom"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
/>
|
||||
<el-switch v-model="form.isCustom" :active-value="1" :inactive-value="0" />
|
||||
<span class="tips">自定义金额不参与赠送优惠</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -114,11 +67,7 @@
|
||||
<el-form-item label="充值并下单">
|
||||
<div class="column">
|
||||
<div class="center">
|
||||
<el-switch
|
||||
v-model="form.isOrder"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
/>
|
||||
<el-switch v-model="form.isOrder" :active-value="1" :inactive-value="0" />
|
||||
<span class="tips">开启后,订单支付页面显示充值选项</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -126,13 +75,8 @@
|
||||
<el-form-item label="充值说明" prop="remark">
|
||||
<div class="column">
|
||||
<div class="item">
|
||||
<el-input
|
||||
type="textarea"
|
||||
:rows="4"
|
||||
:maxlength="250"
|
||||
v-model="form.remark"
|
||||
placeholder="填写内容"
|
||||
></el-input>
|
||||
<el-input type="textarea" :rows="4" :maxlength="250" v-model="form.remark"
|
||||
placeholder="填写内容"></el-input>
|
||||
</div>
|
||||
<div class="item textarea-num">
|
||||
{{ form.remark.length }}/250字内,单文本
|
||||
@@ -141,12 +85,7 @@
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div class="footer">
|
||||
<el-button
|
||||
type="primary"
|
||||
size="large"
|
||||
@click="submitHandle"
|
||||
v-if="shopInfo.isHeadShop"
|
||||
>
|
||||
<el-button type="primary" size="large" @click="submitHandle" v-if="shopInfo.isHeadShop">
|
||||
保存
|
||||
</el-button>
|
||||
<el-button size="large" @click="back">取消</el-button>
|
||||
|
||||
110
src/views/note_push_manage/components/addBlance.vue
Normal file
110
src/views/note_push_manage/components/addBlance.vue
Normal file
@@ -0,0 +1,110 @@
|
||||
<!-- 增加或者减少余额弹窗 -->
|
||||
<template>
|
||||
<el-dialog :title="`余额${form.type == 1 ? '增加' : '扣减'}`" width="400px" v-model="visible" @closed="onClose">
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="80px" label-position="left">
|
||||
<el-form-item label="店铺名称">
|
||||
<el-input v-model="row.shopName" readonly style="width: 300px;"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item :label="`${form.type == 1 ? '充值' : '扣减'}金额`" prop="expense">
|
||||
<el-input v-model="form.expense" placeholder="请输入金额" style="width: 300px;"
|
||||
@input="e => form.expense = filterNumberInput(e)">
|
||||
<template #append>元</template>
|
||||
</el-input>
|
||||
<div>当前余额:<span style="color: red;">{{ multiplyAndFormat(row.money) }}</span></div>
|
||||
</el-form-item>
|
||||
<el-form-item label="扣减原因" v-if="form.type == 2" prop="reason">
|
||||
<el-input type="textarea" :rows="5" v-model="form.reason" placeholder="请输入扣减原因,必填 "></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="submitHandle" :loading="loading">确 定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { smsMoneyChange } from '@/api/coupon'
|
||||
import { ElNotification } from 'element-plus'
|
||||
import { multiplyAndFormat, filterNumberInput } from '@/utils'
|
||||
|
||||
const visible = ref(false)
|
||||
|
||||
const loading = ref(false)
|
||||
const formRef = ref(null)
|
||||
const row = ref({
|
||||
shopName: '',
|
||||
money: 0
|
||||
})
|
||||
const form = ref({
|
||||
expense: '',
|
||||
type: 1,
|
||||
reason: ''
|
||||
})
|
||||
|
||||
const rules = ref({
|
||||
expense: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入充值金额',
|
||||
triiger: 'blur'
|
||||
}
|
||||
],
|
||||
reason: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入扣减原因',
|
||||
triiger: 'blur'
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
function onClose() {
|
||||
form.value = {
|
||||
expense: '',
|
||||
type: 1,
|
||||
reason: ''
|
||||
}
|
||||
formRef.value.resetFields()
|
||||
}
|
||||
|
||||
const emit = defineEmits(['success'])
|
||||
function submitHandle() {
|
||||
formRef.value.validate(async (valid) => {
|
||||
try {
|
||||
if (valid) {
|
||||
loading.value = true
|
||||
await smsMoneyChange({
|
||||
shopId: row.value.shopId,
|
||||
...form.value
|
||||
})
|
||||
emit('success')
|
||||
visible.value = false
|
||||
|
||||
ElNotification({
|
||||
title: '注意',
|
||||
message: `${form.value.type == 1 ? '增加' : '扣减'}成功`,
|
||||
type: 'success'
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
// 显示
|
||||
function show(obj = {}, type = 1) {
|
||||
visible.value = true
|
||||
row.value = { ...obj }
|
||||
form.value.type = type
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
show
|
||||
})
|
||||
</script>
|
||||
139
src/views/note_push_manage/components/record.vue
Normal file
139
src/views/note_push_manage/components/record.vue
Normal file
@@ -0,0 +1,139 @@
|
||||
<!-- 充值记录 -->
|
||||
<template>
|
||||
<el-dialog :title="`记录(${tableRow.shopName})`" width="1200px" v-model="visible">
|
||||
<el-form label-width="0" inline>
|
||||
<el-form-item>
|
||||
<el-date-picker v-model="dateRange" type="datetimerange" range-separator="至" start-placeholder="开始日期时间"
|
||||
end-placeholder="结束日期时间" format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss"
|
||||
@change="timeChange" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-select v-model="queryForm.type" placeholder="类型" style="width: 200px;" @change="getTableData">
|
||||
<el-option :label="item.label" :value="item.value" v-for="item in statusList" :key="item.value"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div class="row">
|
||||
<el-table :data="tableData.list" stripe border v-loading="tableData.loading" height="500px">
|
||||
<el-table-column label="变动金额" prop="expense">
|
||||
<template #default="scope">
|
||||
{{ multiplyAndFormat(scope.row.money || 0) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="变动后余额" prop="balance">
|
||||
<template #default="scope">
|
||||
{{ multiplyAndFormat(scope.row.balance || 0) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="变动时间" prop="createTime"></el-table-column>
|
||||
<el-table-column label="变动原因" prop="reason"></el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<div class="row">
|
||||
<el-pagination v-model:current-page="tableData.page" v-model:page-size="tableData.size"
|
||||
:page-sizes="[10, 20, 50, 100]" background layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="tableData.total" @size-change="handleSizeChange" @current-change="handleCurrentChange" />
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive } from 'vue'
|
||||
import { smsMoneyDetailQuery } from '@/api/coupon'
|
||||
import { multiplyAndFormat } from '@/utils'
|
||||
|
||||
const visible = ref(false)
|
||||
|
||||
const statusList = ref([
|
||||
{
|
||||
value: '',
|
||||
label: '全部'
|
||||
},
|
||||
{
|
||||
value: 1,
|
||||
label: '增加'
|
||||
},
|
||||
{
|
||||
value: 2,
|
||||
label: '减少'
|
||||
},
|
||||
])
|
||||
const dateRange = ref([])
|
||||
const resetQueryForm = ref({
|
||||
startTime: '',
|
||||
endTime: '',
|
||||
type: '',
|
||||
})
|
||||
const queryForm = ref({ ...resetQueryForm.value })
|
||||
|
||||
const tableData = reactive({
|
||||
loading: false,
|
||||
list: [],
|
||||
total: 0,
|
||||
page: 1,
|
||||
size: 10
|
||||
})
|
||||
|
||||
// 选择时间
|
||||
function timeChange(e) {
|
||||
if (e == null) {
|
||||
queryForm.value.startTime = ''
|
||||
queryForm.value.endTime = ''
|
||||
} else {
|
||||
queryForm.value.startTime = e[0]
|
||||
queryForm.value.endTime = e[1]
|
||||
}
|
||||
tableData.page = 1
|
||||
getTableData()
|
||||
}
|
||||
|
||||
// 分页大小发生变化
|
||||
function handleSizeChange(e) {
|
||||
tableData.pageSize = e;
|
||||
getTableData();
|
||||
}
|
||||
|
||||
// 分页发生变化
|
||||
function handleCurrentChange(e) {
|
||||
tableData.page = e;
|
||||
getTableData();
|
||||
}
|
||||
|
||||
async function getTableData() {
|
||||
try {
|
||||
tableData.loading = true
|
||||
const res = await smsMoneyDetailQuery({
|
||||
shopId: tableRow.value.shopId,
|
||||
page: tableData.page,
|
||||
size: tableData.size,
|
||||
...queryForm.value
|
||||
})
|
||||
tableData.list = res.records
|
||||
tableData.total = +res.totalRow
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
setTimeout(() => {
|
||||
tableData.loading = false
|
||||
}, 500);
|
||||
}
|
||||
|
||||
const tableRow = ref('')
|
||||
function show(row) {
|
||||
dateRange.value = []
|
||||
queryForm.value = { ...resetQueryForm.value }
|
||||
tableRow.value = { ...row }
|
||||
visible.value = true
|
||||
getTableData()
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
show
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.row {
|
||||
padding-top: 14px;
|
||||
}
|
||||
</style>
|
||||
274
src/views/note_push_manage/note_template/index.vue
Normal file
274
src/views/note_push_manage/note_template/index.vue
Normal file
@@ -0,0 +1,274 @@
|
||||
<!-- 店铺配置管理 -->
|
||||
<template>
|
||||
<div class="gyq_container">
|
||||
<div class="gyq_content">
|
||||
<div class="row">
|
||||
<div class="center">
|
||||
<el-select v-model="status" placeholder="状态" style="width: 200px;">
|
||||
<el-option label="全部" value=""></el-option>
|
||||
<el-option label="启用中" value="1"></el-option>
|
||||
<el-option label="已禁用" value="0"></el-option>
|
||||
</el-select>
|
||||
<el-button type="primary" @click="searchHandle">搜索</el-button>
|
||||
<el-button @click="resetSearch">重置</el-button>
|
||||
<el-button type="primary" plain @click="recordRef.show()">商家申请记录</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt14">
|
||||
<el-button type="primary" @click="visible = true">添加模板</el-button>
|
||||
</div>
|
||||
<div class="row mt14">
|
||||
<el-table :data="tableData.list" stripe border v-loading="tableData.loading">
|
||||
<el-table-column prop="id" label="ID"></el-table-column>
|
||||
<el-table-column prop="title" label="模版名称" />
|
||||
<el-table-column prop="content" label="短信模版内容" width="500" />
|
||||
<el-table-column prop="shopUse" label="启用状态">
|
||||
<template #default="scope">
|
||||
<el-switch v-model="scope.row.shopUse" :active-value="1" :inactive-value="0"
|
||||
@change="statusChange($event, scope.row)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="150">
|
||||
<template #default="scope">
|
||||
<el-button type="primary" link @click="editorHandle(scope.row)">编辑</el-button>
|
||||
<el-popconfirm title="确认要删除吗?" @confirm="deleteHandle(scope.row)">
|
||||
<template #reference>
|
||||
<el-button type="danger" link>删除</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<div class="row mt14">
|
||||
<el-pagination v-model:current-page="tableData.page" v-model:page-size="tableData.size"
|
||||
:page-sizes="[10, 20, 50, 100]" background layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="tableData.total" @size-change="handleSizeChange" @current-change="handleCurrentChange" />
|
||||
</div>
|
||||
</div>
|
||||
<el-dialog :title="`${selectRow.id ? '编辑' : '添加'}模板`" width="500px" v-model="visible" @closed="onClose">
|
||||
<el-form :model="addForm" label-width="80px" :rules="addFormRules" ref="addFormRef">
|
||||
<el-form-item label="模板名称" prop="title">
|
||||
<el-input v-model="addForm.title" :maxlength="20" placeholder="请输入模板名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="模板内容" prop="content">
|
||||
<div class="ipt">
|
||||
<el-input type="textarea" :maxlength="maxLength" :rows="4" v-model="addForm.content"
|
||||
placeholder="请输入模板内容" />
|
||||
<span>{{ addForm.content.length }}/{{ maxLength }}</span>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="handleCancel">取 消</el-button>
|
||||
<el-button type="primary" @click="handleOk" :loading="confirmLoading">确 定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<record ref="recordRef" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import record from './record.vue'
|
||||
import { smsTemplatePage, smsTemplate, smsTemplateResubmit, shopUseDelStatus } from '@/api/coupon/index.js'
|
||||
|
||||
const recordRef = ref(null)
|
||||
|
||||
const status = ref('')
|
||||
|
||||
// 搜索
|
||||
function searchHandle() {
|
||||
tableData.page = 1
|
||||
getTableData()
|
||||
}
|
||||
|
||||
// 重置搜索
|
||||
function resetSearch() {
|
||||
status.value = ''
|
||||
tableData.page = 1
|
||||
getTableData()
|
||||
}
|
||||
|
||||
const tableData = reactive({
|
||||
loading: false,
|
||||
list: [],
|
||||
total: 0,
|
||||
page: 1,
|
||||
size: 10
|
||||
})
|
||||
|
||||
// 添加模板 statr
|
||||
const selectRow = ref({ id: '' })
|
||||
const visible = ref(false)
|
||||
const confirmLoading = ref(false)
|
||||
const maxLength = ref(500)
|
||||
const addForm = ref({
|
||||
title: '',
|
||||
content: ''
|
||||
})
|
||||
|
||||
const addFormRules = {
|
||||
title: [
|
||||
{ required: true, message: '请输入模板名称', trigger: 'blur' },
|
||||
{ min: 1, max: 20, message: '长度在 1 到 20 个字符', trigger: 'blur' }
|
||||
],
|
||||
content: [
|
||||
{ required: true, message: '请输入模板内容', trigger: 'blur' },
|
||||
{ min: 1, max: maxLength.value, message: `长度在 1 到 ${maxLength.value} 个字符`, trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
|
||||
const addFormRef = ref(null)
|
||||
|
||||
function onClose() {
|
||||
selectRow.value = { id: '' }
|
||||
visible.value = false
|
||||
addForm.value = {
|
||||
title: '',
|
||||
content: ''
|
||||
}
|
||||
if (addFormRef.value) {
|
||||
addFormRef.value.clearValidate()
|
||||
}
|
||||
}
|
||||
|
||||
// 取消提交
|
||||
function handleCancel() {
|
||||
onClose()
|
||||
}
|
||||
|
||||
// 显示编辑
|
||||
function editorHandle(row) {
|
||||
selectRow.value = { ...row }
|
||||
addForm.value = { ...row }
|
||||
visible.value = true
|
||||
}
|
||||
|
||||
// 开始提交
|
||||
function handleOk() {
|
||||
if (!addFormRef.value) return
|
||||
addFormRef.value.validate(async (valid) => {
|
||||
try {
|
||||
if (valid) {
|
||||
confirmLoading.value = true
|
||||
if (selectRow.value.id) {
|
||||
await smsTemplateResubmit(addForm.value)
|
||||
} else {
|
||||
await smsTemplate({
|
||||
...addForm.value,
|
||||
shopId: localStorage.getItem('shopId'),
|
||||
})
|
||||
}
|
||||
onClose()
|
||||
getTableData()
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
confirmLoading.value = false
|
||||
})
|
||||
}
|
||||
// 添加模板 end
|
||||
|
||||
// 状态改变
|
||||
async function statusChange(e, row) {
|
||||
try {
|
||||
if (tableData.loading) return
|
||||
await shopUseDelStatus({
|
||||
id: row.id,
|
||||
shopUse: row.shopUse
|
||||
})
|
||||
getTableData()
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
// 删除
|
||||
async function deleteHandle(row) {
|
||||
try {
|
||||
await shopUseDelStatus({
|
||||
id: row.id,
|
||||
isDel: 1,
|
||||
});
|
||||
getTableData();
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
|
||||
// 分页大小发生变化
|
||||
function handleSizeChange(e) {
|
||||
tableData.pageSize = e;
|
||||
getTableData();
|
||||
}
|
||||
|
||||
// 分页发生变化
|
||||
function handleCurrentChange(e) {
|
||||
tableData.page = e;
|
||||
getTableData();
|
||||
}
|
||||
|
||||
async function getTableData() {
|
||||
try {
|
||||
tableData.loading = true
|
||||
const res = await smsTemplatePage({
|
||||
page: tableData.page,
|
||||
size: tableData.size,
|
||||
shopUse: status.value,
|
||||
shopId: localStorage.getItem('shopId')
|
||||
})
|
||||
tableData.list = res.records
|
||||
tableData.total = +res.totalRow
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
setTimeout(() => {
|
||||
tableData.loading = false
|
||||
}, 500);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getTableData()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.gyq_container {
|
||||
padding: 14px;
|
||||
|
||||
.gyq_content {
|
||||
padding: 14px;
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.row {
|
||||
&.mt14 {
|
||||
margin-top: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.ipt {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
|
||||
span {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
bottom: 0;
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
153
src/views/note_push_manage/note_template/record.vue
Normal file
153
src/views/note_push_manage/note_template/record.vue
Normal file
@@ -0,0 +1,153 @@
|
||||
<!-- 商家申请记录 -->
|
||||
<template>
|
||||
<el-dialog title="商家申请记录" width="1200" top="10vh" v-model="visible" @close="onClose">
|
||||
<div class="row">
|
||||
<el-form inline label-width="0">
|
||||
<el-form-item>
|
||||
<el-date-picker v-model="dateRange" type="datetimerange" range-separator="至" start-placeholder="开始日期时间"
|
||||
end-placeholder="结束日期时间" format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss"
|
||||
@change="timeChange" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-select v-model="queryForm.status" placeholder="状态" style="width: 200px;" @change="getTableData">
|
||||
<el-option label="全部" value=""></el-option>
|
||||
<el-option :label="item.label" :value="item.value" v-for="item in statusList" :key="item.value"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<div class="row mt14">
|
||||
<el-table :data="tableData.list" stripe border v-loading="tableData.loading" height="500px">
|
||||
<el-table-column label="申请商家" prop="shopName"></el-table-column>
|
||||
<el-table-column label="模版名称" prop="title"></el-table-column>
|
||||
<el-table-column label="模版内容" prop="content"></el-table-column>
|
||||
<el-table-column label="使用场景" prop="sceneDetail"></el-table-column>
|
||||
<el-table-column label="提交时间" prop="createTime"></el-table-column>
|
||||
<el-table-column label="审核状态" prop="status" width="100">
|
||||
<template #default="scope">
|
||||
{{statusList.find(item => item.value == scope.row.status).label}}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<div class="row mt14">
|
||||
<el-pagination v-model:current-page="tableData.page" v-model:page-size="tableData.size"
|
||||
:page-sizes="[10, 20, 50, 100]" background layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="tableData.total" @size-change="handleSizeChange" @current-change="handleCurrentChange" />
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive } from 'vue'
|
||||
import { smsTemplatePage } from '@/api/coupon/index.js'
|
||||
|
||||
const visible = ref(false)
|
||||
|
||||
// 0 待申请 1 审核中 2 成功 -1失败 -2 重新申请中
|
||||
const statusList = reactive([
|
||||
{
|
||||
value: 0,
|
||||
label: '待申请'
|
||||
},
|
||||
{
|
||||
value: 1,
|
||||
label: '审核中'
|
||||
},
|
||||
{
|
||||
value: 2,
|
||||
label: '成功'
|
||||
},
|
||||
{
|
||||
value: -1,
|
||||
label: '1失败'
|
||||
},
|
||||
{
|
||||
value: -2,
|
||||
label: '重新申请中'
|
||||
}
|
||||
])
|
||||
|
||||
const dateRange = ref([])
|
||||
const queryForm = ref({
|
||||
startTime: '',
|
||||
endTime: '',
|
||||
status: ''
|
||||
})
|
||||
|
||||
function onClose() {
|
||||
visible.value = false
|
||||
}
|
||||
|
||||
function show() {
|
||||
visible.value = true
|
||||
getTableData()
|
||||
}
|
||||
|
||||
// 选择时间
|
||||
function timeChange(e) {
|
||||
if (e == null) {
|
||||
queryForm.value.startTime = ''
|
||||
queryForm.value.endTime = ''
|
||||
} else {
|
||||
queryForm.value.startTime = e[0]
|
||||
queryForm.value.endTime = e[1]
|
||||
}
|
||||
|
||||
console.log(queryForm.value);
|
||||
|
||||
|
||||
tableData.page = 1
|
||||
getTableData()
|
||||
}
|
||||
|
||||
// 分页大小发生变化
|
||||
function handleSizeChange(e) {
|
||||
tableData.size = e;
|
||||
getTableData();
|
||||
}
|
||||
|
||||
// 分页发生变化
|
||||
function handleCurrentChange(e) {
|
||||
tableData.page = e;
|
||||
getTableData();
|
||||
}
|
||||
|
||||
const tableData = reactive({
|
||||
loading: false,
|
||||
page: 1,
|
||||
size: 10,
|
||||
list: []
|
||||
})
|
||||
|
||||
// 获取数据
|
||||
async function getTableData() {
|
||||
try {
|
||||
tableData.loading = true
|
||||
const res = await smsTemplatePage({
|
||||
page: tableData.page,
|
||||
size: tableData.size,
|
||||
...queryForm.value
|
||||
})
|
||||
tableData.list = res.records
|
||||
tableData.total = +res.totalRow
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
setTimeout(() => {
|
||||
tableData.loading = false
|
||||
}, 500);
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
show
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.row {
|
||||
&.mt14 {
|
||||
margin-top: 14px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
196
src/views/note_push_manage/shop_manage/index.vue
Normal file
196
src/views/note_push_manage/shop_manage/index.vue
Normal file
@@ -0,0 +1,196 @@
|
||||
<!-- 店铺配置管理 -->
|
||||
<template>
|
||||
<div class="gyq_container">
|
||||
<div class="gyq_content">
|
||||
<div class="row">
|
||||
<div class="center">
|
||||
<el-input v-model="searchValue" style="width: 300px;" placeholder="请输入内容" clearable @clear="getTableData">
|
||||
<template #prepend>名称</template>
|
||||
</el-input>
|
||||
<el-button type="primary" @click="searchHandle">搜索</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt14">
|
||||
<el-table :data="tableData.list" stripe border v-loading="tableData.loading">
|
||||
<el-table-column prop="name" label="店铺名称" width="300">
|
||||
<template #default="scope">
|
||||
<div class="shop_info">
|
||||
<el-avatar :src="scope.row.coverImg" shape="square" :size="50"></el-avatar>
|
||||
<div class="info">
|
||||
<div class="name">
|
||||
{{ scope.row.shopName }}
|
||||
</div>
|
||||
<div class="tag">
|
||||
<el-tag effect="dark" type="success" disable-transitions
|
||||
v-if="scope.row.profiles == 'release'">正式</el-tag>
|
||||
<el-tag effect="dark" type="warning" disable-transitions
|
||||
v-if="scope.row.profiles == 'trial'">试用版</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="type" label="店铺类型">
|
||||
<template #default="scope">
|
||||
<div class="column">
|
||||
<div>{{shopTypeList.find(item => item.value == scope.row.shopType).label}}</div>
|
||||
<div v-if="scope.row.shopType == 'join'">{{ scope.row.mainShopName }}</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="money" label="可用余额">
|
||||
<template #default="scope">
|
||||
{{ multiplyAndFormat(scope.row.money || 0) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="amountTotal" label="累计用量">
|
||||
<template #default="scope">
|
||||
{{ multiplyAndFormat(scope.row.amountTotal || 0) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="monthSendTotal" label="本月用量">
|
||||
<template #default="scope">
|
||||
{{ multiplyAndFormat(scope.row.monthSendTotal || 0) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="200" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" @click="addBlanceRef.show(scope.row, 1)">充值</el-button>
|
||||
<el-button link type="primary" @click="addBlanceRef.show(scope.row, 2)">扣减</el-button>
|
||||
<el-button link type="primary" @click="recordRef.show(scope.row)">查看记录</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<div class="row mt14">
|
||||
<el-pagination v-model:current-page="tableData.page" v-model:page-size="tableData.size"
|
||||
:page-sizes="[10, 20, 50, 100]" background layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="tableData.total" @size-change="handleSizeChange" @current-change="handleCurrentChange" />
|
||||
</div>
|
||||
</div>
|
||||
<addBlance ref="addBlanceRef" @success="getTableData" />
|
||||
<record ref="recordRef" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { adminSmsMoneyPage } from '@/api/coupon'
|
||||
import addBlance from '../components/addBlance.vue'
|
||||
import record from '../components/record.vue'
|
||||
import { multiplyAndFormat } from '@/utils'
|
||||
|
||||
const addBlanceRef = ref(null)
|
||||
const recordRef = ref(null)
|
||||
|
||||
const searchValue = ref('')
|
||||
|
||||
function searchHandle() {
|
||||
tableData.page = 1
|
||||
getTableData()
|
||||
}
|
||||
|
||||
const shopTypeList = ref([
|
||||
{
|
||||
value: 'only',
|
||||
label: '单店'
|
||||
},
|
||||
{
|
||||
value: 'chain',
|
||||
label: '连锁店'
|
||||
},
|
||||
{
|
||||
value: 'join',
|
||||
label: '加盟店'
|
||||
},
|
||||
])
|
||||
|
||||
const tableData = reactive({
|
||||
loading: false,
|
||||
list: [],
|
||||
total: 0,
|
||||
page: 1,
|
||||
size: 10
|
||||
})
|
||||
|
||||
// 分页大小发生变化
|
||||
function handleSizeChange(e) {
|
||||
tableData.pageSize = e;
|
||||
getTableData();
|
||||
}
|
||||
|
||||
// 分页发生变化
|
||||
function handleCurrentChange(e) {
|
||||
tableData.page = e;
|
||||
getTableData();
|
||||
}
|
||||
|
||||
// 平台:短信店铺配置 列表
|
||||
async function getTableData() {
|
||||
try {
|
||||
tableData.loading = true
|
||||
const res = await adminSmsMoneyPage({
|
||||
page: tableData.page,
|
||||
size: tableData.size,
|
||||
name: searchValue.value
|
||||
})
|
||||
tableData.list = res.records
|
||||
tableData.total = +res.totalRow
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
setTimeout(() => {
|
||||
tableData.loading = false
|
||||
}, 500);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getTableData()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.gyq_container {
|
||||
padding: 14px;
|
||||
|
||||
.gyq_content {
|
||||
padding: 14px;
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.row {
|
||||
&.mt14 {
|
||||
margin-top: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.shop_info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.info {
|
||||
padding-left: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
|
||||
.name {
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
</style>
|
||||
@@ -2,12 +2,8 @@
|
||||
<div class="app-container">
|
||||
<!-- 列表 -->
|
||||
<!-- 搜索 -->
|
||||
<page-search
|
||||
ref="searchRef"
|
||||
:search-config="searchConfig"
|
||||
@query-click="searchQueryClick"
|
||||
@reset-click="handleResetClick"
|
||||
/>
|
||||
<page-search ref="searchRef" :search-config="searchConfig" @query-click="searchQueryClick"
|
||||
@reset-click="handleResetClick" />
|
||||
<div class="head-container">
|
||||
<div class="card">
|
||||
<!-- <div class="title">统计数据</div> -->
|
||||
@@ -32,17 +28,9 @@
|
||||
</div>
|
||||
</div>
|
||||
<!-- 列表 -->
|
||||
<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 ? "启用" : "禁用" }}
|
||||
@@ -55,21 +43,18 @@
|
||||
{{ scope.row[scope.prop] ? "是" : "否" }}
|
||||
</template>
|
||||
<template #gender="scope">
|
||||
<el-tag
|
||||
:type="
|
||||
scope.row[scope.prop] == null
|
||||
? 'info'
|
||||
: scope.row[scope.prop] == 1
|
||||
? 'success'
|
||||
: 'warning'
|
||||
"
|
||||
>
|
||||
<el-tag :type="scope.row[scope.prop] == null
|
||||
? 'info'
|
||||
: scope.row[scope.prop] == 1
|
||||
? 'success'
|
||||
: 'warning'
|
||||
">
|
||||
{{
|
||||
scope.row[scope.prop] === null
|
||||
? "未知"
|
||||
: scope.row[scope.prop] == 1
|
||||
? "男"
|
||||
: "女"
|
||||
? "男"
|
||||
: "女"
|
||||
}}
|
||||
</el-tag>
|
||||
</template>
|
||||
@@ -86,11 +71,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>
|
||||
|
||||
<template #coupon="scope">
|
||||
@@ -104,25 +85,13 @@
|
||||
</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>
|
||||
<!-- 用户余额修改 -->
|
||||
<page-modal
|
||||
ref="editMoneyModalRef"
|
||||
:modal-config="editMoneyModalConfig"
|
||||
@formDataChange="formDataChange"
|
||||
@submit-click="handleSubmitClick"
|
||||
></page-modal>
|
||||
<page-modal ref="editMoneyModalRef" :modal-config="editMoneyModalConfig" @formDataChange="formDataChange"
|
||||
@submit-click="handleSubmitClick"></page-modal>
|
||||
|
||||
<!-- 用户优惠券详情 -->
|
||||
<UserCouponDialog ref="userCouponDialogRef"></UserCouponDialog>
|
||||
@@ -142,6 +111,7 @@ import editMoneyModalConfig, { addOptions, reduceOptions } from "./config/edit-m
|
||||
import searchConfig from "./config/search";
|
||||
import { returnOptionsLabel } from "./config/config";
|
||||
import shopUserApi from "@/api/account/shopUser";
|
||||
import { useRoute } from 'vue-router'
|
||||
const editMoneyModalRef = ref(null);
|
||||
const userCouponDialogRef = ref(null);
|
||||
const GiveCouponRef = ref(null);
|
||||
@@ -165,6 +135,9 @@ const {
|
||||
} = usePage();
|
||||
|
||||
function searchQueryClick(e) {
|
||||
console.log(e);
|
||||
|
||||
|
||||
handleQueryClick(e);
|
||||
// 获取统计数据
|
||||
getSummary();
|
||||
@@ -224,7 +197,7 @@ function handleToolbarClick(name) {
|
||||
}
|
||||
|
||||
// 赠送券
|
||||
function toGiveCoupon() {}
|
||||
function toGiveCoupon() { }
|
||||
// 其他操作列
|
||||
async function handleOperatClick(data) {
|
||||
const row = data.row;
|
||||
@@ -250,7 +223,14 @@ async function handleOperatClick(data) {
|
||||
}
|
||||
}
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
onMounted(() => {
|
||||
if (route.query.phone) {
|
||||
searchQueryClick({
|
||||
key: route.query.phone
|
||||
})
|
||||
}
|
||||
getSummary(searchRef.value.getQueryParams());
|
||||
});
|
||||
</script>
|
||||
@@ -258,6 +238,7 @@ onMounted(() => {
|
||||
.align-center {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: #f5f5f5;
|
||||
padding: 0 14px;
|
||||
|
||||
Reference in New Issue
Block a user