campus-errand/admin/src/views/Config.vue
2026-03-17 18:51:49 +08:00

265 lines
9.5 KiB
Vue

<template>
<div>
<h3 style="margin: 0 0 16px;">配置管理</h3>
<el-tabs v-model="activeTab">
<!-- 佣金规则 -->
<el-tab-pane label="佣金规则" name="commission">
<div style="margin-bottom: 12px;">
<el-button type="primary" size="small" @click="addRule">添加区间</el-button>
</div>
<el-table :data="commissionRules" border size="small">
<el-table-column label="最低金额" width="150">
<template #default="{ row }">
<el-input-number v-model="row.minAmount" :min="0" :precision="2" size="small" style="width: 120px;" />
</template>
</el-table-column>
<el-table-column label="最高金额" width="150">
<template #default="{ row }">
<el-input-number v-model="row.maxAmount" :min="0" :precision="2" size="small" style="width: 120px;" />
</template>
</el-table-column>
<el-table-column label="抽成类型" width="150">
<template #default="{ row }">
<el-select v-model="row.rateType" size="small">
<el-option label="百分比" :value="0" />
<el-option label="固定金额" :value="1" />
</el-select>
</template>
</el-table-column>
<el-table-column label="抽成值" width="150">
<template #default="{ row }">
<el-input-number v-model="row.rate" :min="0" :precision="4" :step="0.01" size="small" style="width: 120px;" />
</template>
</el-table-column>
<el-table-column label="操作" width="80">
<template #default="{ $index }">
<el-button size="small" type="danger" @click="commissionRules.splice($index, 1)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-button type="primary" style="margin-top: 12px;" :loading="saving" @click="saveCommissionRules">保存佣金规则</el-button>
</el-tab-pane>
<!-- 客服二维码 -->
<el-tab-pane label="客服二维码" name="qrcode">
<el-form label-width="100px" style="max-width: 500px;">
<el-form-item label="二维码图片">
<el-upload action="/api/upload/image" :headers="uploadHeaders" :show-file-list="false"
:on-success="(res) => configs.qrcode = res.url" accept="image/*">
<el-image v-if="configs.qrcode" :src="configs.qrcode" style="width: 150px; cursor: pointer; border-radius: 8px;" fit="contain" />
<el-button v-else size="small">上传图片</el-button>
</el-upload>
<div v-if="configs.qrcode" style="margin-top: 4px;">
<el-button size="small" text type="danger" @click="configs.qrcode = ''">移除</el-button>
</div>
</el-form-item>
<el-form-item>
<el-button type="primary" :loading="saving" @click="saveConfig('qrcode')">保存</el-button>
</el-form-item>
</el-form>
</el-tab-pane>
<!-- 用户协议 -->
<el-tab-pane label="用户协议" name="agreement">
<el-form label-width="100px" style="max-width: 700px;">
<el-form-item label="协议内容">
<el-input v-model="configs.agreement" type="textarea" :rows="12" placeholder="请输入用户协议内容" />
</el-form-item>
<el-form-item>
<el-button type="primary" :loading="saving" @click="saveConfig('agreement')">保存</el-button>
</el-form-item>
</el-form>
</el-tab-pane>
<!-- 隐私政策 -->
<el-tab-pane label="隐私政策" name="privacy">
<el-form label-width="100px" style="max-width: 700px;">
<el-form-item label="政策内容">
<el-input v-model="configs.privacy" type="textarea" :rows="12" placeholder="请输入隐私政策内容" />
</el-form-item>
<el-form-item>
<el-button type="primary" :loading="saving" @click="saveConfig('privacy')">保存</el-button>
</el-form-item>
</el-form>
</el-tab-pane>
<!-- 提现说明 -->
<el-tab-pane label="提现说明" name="withdrawal_guide">
<el-form label-width="100px" style="max-width: 700px;">
<el-form-item label="说明内容">
<el-input v-model="configs.withdrawal_guide" type="textarea" :rows="8" placeholder="请输入提现说明内容" />
</el-form-item>
<el-form-item>
<el-button type="primary" :loading="saving" @click="saveConfig('withdrawal_guide')">保存</el-button>
</el-form-item>
</el-form>
</el-tab-pane>
<!-- 冻结时间 -->
<el-tab-pane label="冻结时间" name="freeze_days">
<el-form label-width="120px" style="max-width: 500px;">
<el-form-item label="冻结天数">
<el-input-number v-model="freezeDays" :min="0" :max="30" />
<span style="margin-left: 8px; color: #909399;">天</span>
</el-form-item>
<el-form-item>
<el-button type="primary" :loading="saving" @click="saveFreezeDays">保存</el-button>
</el-form-item>
</el-form>
</el-tab-pane>
<!-- 页面顶图 -->
<el-tab-pane label="页面顶图" name="page_banners">
<el-form label-width="100px" style="max-width: 600px;">
<el-form-item v-for="item in pageBannerList" :key="item.key" :label="item.label">
<el-upload
action="/api/upload/image"
:headers="uploadHeaders"
:show-file-list="false"
:on-success="(res) => pageBanners[item.key] = res.url"
accept="image/*"
>
<el-image
v-if="pageBanners[item.key]"
:src="pageBanners[item.key]"
style="width: 300px; height: 100px; cursor: pointer; border-radius: 8px;"
fit="cover"
/>
<el-button v-else size="small" type="primary">选择图片</el-button>
</el-upload>
<div v-if="pageBanners[item.key]" style="margin-top: 4px;">
<el-button size="small" text type="danger" @click="pageBanners[item.key] = ''">移除</el-button>
</div>
</el-form-item>
<el-form-item>
<el-button type="primary" :loading="saving" @click="savePageBanners">保存全部顶图</el-button>
</el-form-item>
</el-form>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import request from '../utils/request'
const activeTab = ref('commission')
const saving = ref(false)
const uploadHeaders = { Authorization: `Bearer ${localStorage.getItem('admin_token')}` }
// 佣金规则
const commissionRules = ref([])
// 系统配置
const configs = reactive({ qrcode: '', agreement: '', privacy: '', withdrawal_guide: '' })
const freezeDays = ref(1)
// 页面顶图配置
const pageBannerList = [
{ key: 'page_banner_pickup', label: '代取' },
{ key: 'page_banner_delivery', label: '代送' },
{ key: 'page_banner_help', label: '万能帮' },
{ key: 'page_banner_purchase', label: '代购' },
{ key: 'page_banner_food', label: '美食街' },
{ key: 'page_banner_order-hall', label: '接单页' }
]
const pageBanners = reactive({
page_banner_pickup: '',
page_banner_delivery: '',
page_banner_help: '',
page_banner_purchase: '',
page_banner_food: '',
'page_banner_order-hall': ''
})
function addRule() {
commissionRules.value.push({ minAmount: 0, maxAmount: 0, rateType: 0, rate: 0 })
}
async function fetchCommissionRules() {
try {
commissionRules.value = await request.get('/admin/commission-rules')
} catch { /* 忽略 */ }
}
async function saveCommissionRules() {
saving.value = true
try {
await request.put('/admin/commission-rules', commissionRules.value)
ElMessage.success('佣金规则保存成功')
} finally {
saving.value = false
}
}
async function fetchConfig(key) {
try {
const res = await request.get(`/admin/config/${key}`)
configs[key] = res.value || ''
} catch { /* 忽略 */ }
}
async function saveConfig(key) {
saving.value = true
try {
await request.put(`/admin/config/${key}`, { value: configs[key] })
ElMessage.success('保存成功')
} finally {
saving.value = false
}
}
async function fetchFreezeDays() {
try {
const res = await request.get('/admin/config/freeze_days')
freezeDays.value = parseInt(res.value) || 1
} catch { /* 忽略 */ }
}
async function saveFreezeDays() {
saving.value = true
try {
await request.put('/admin/config/freeze_days', { value: String(freezeDays.value) })
ElMessage.success('冻结时间保存成功')
} finally {
saving.value = false
}
}
async function fetchPageBanners() {
for (const item of pageBannerList) {
try {
const res = await request.get(`/admin/config/${item.key}`)
pageBanners[item.key] = res.value || ''
} catch { /* 忽略 */ }
}
}
async function savePageBanners() {
saving.value = true
try {
for (const item of pageBannerList) {
await request.put(`/admin/config/${item.key}`, { value: pageBanners[item.key] })
}
ElMessage.success('页面顶图保存成功')
} finally {
saving.value = false
}
}
onMounted(async () => {
await fetchCommissionRules()
await Promise.all([
fetchConfig('qrcode'),
fetchConfig('agreement'),
fetchConfig('privacy'),
fetchConfig('withdrawal_guide'),
fetchFreezeDays(),
fetchPageBanners()
])
})
</script>