add: 增加新功能

This commit is contained in:
gyq
2025-10-17 11:28:41 +08:00
parent 0405a0fb99
commit 44482d9dc9
30 changed files with 4695 additions and 507 deletions

View 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>

View 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>

View 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>

View 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>

View 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>