Files
medical-mall/pages/mall/admin/system-settings.uvue

529 lines
13 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!-- 系统设置页面 - 基于CRMEB设计 -->
<template>
<view class="system-settings">
<!-- 设置选项卡 -->
<view class="settings-tabs">
<view class="tab-list">
<view
v-for="tab in settingTabs"
:key="tab.key"
class="tab-item"
:class="{ active: activeTab === tab.key }"
@click="switchTab(tab.key)"
>
<text class="tab-text">{{ tab.label }}</text>
</view>
</view>
</view>
<!-- 基础设置 -->
<view v-if="activeTab === 'basic'" class="setting-section">
<view class="section-title">基础设置</view>
<view class="setting-form">
<view class="form-item">
<text class="label">网站名称:</text>
<input v-model="settings.site_name" placeholder="请输入网站名称" class="input-field" />
</view>
<view class="form-item">
<text class="label">网站域名:</text>
<input v-model="settings.site_domain" placeholder="请输入网站域名" class="input-field" />
</view>
<view class="form-item">
<text class="label">网站Logo</text>
<view class="upload-area">
<image v-if="settings.site_logo" :src="settings.site_logo" class="logo-preview" />
<button class="upload-btn" @click="chooseLogo">选择图片</button>
</view>
</view>
<view class="form-item">
<text class="label">客服电话:</text>
<input v-model="settings.service_phone" placeholder="请输入客服电话" class="input-field" />
</view>
<view class="form-item">
<text class="label">网站描述:</text>
<textarea v-model="settings.site_description" placeholder="请输入网站描述" class="textarea-field" />
</view>
</view>
</view>
<!-- 支付设置 -->
<view v-if="activeTab === 'payment'" class="setting-section">
<view class="section-title">支付设置</view>
<view class="setting-form">
<view class="form-item">
<text class="label">微信支付:</text>
<view class="switch-item">
<switch :checked="settings.wechat_pay_enabled" @change="onWechatPayChange" />
<text class="switch-label">{{ settings.wechat_pay_enabled ? '已开启' : '已关闭' }}</text>
</view>
</view>
<view v-if="settings.wechat_pay_enabled" class="form-item">
<text class="label">微信商户号:</text>
<input v-model="settings.wechat_mch_id" placeholder="请输入微信商户号" class="input-field" />
</view>
<view v-if="settings.wechat_pay_enabled" class="form-item">
<text class="label">微信支付密钥:</text>
<input v-model="settings.wechat_pay_key" type="password" placeholder="请输入微信支付密钥" class="input-field" />
</view>
<view class="form-item">
<text class="label">支付宝支付:</text>
<view class="switch-item">
<switch :checked="settings.alipay_enabled" @change="onAlipayChange" />
<text class="switch-label">{{ settings.alipay_enabled ? '已开启' : '已关闭' }}</text>
</view>
</view>
<view v-if="settings.alipay_enabled" class="form-item">
<text class="label">支付宝应用ID</text>
<input v-model="settings.alipay_app_id" placeholder="请输入支付宝应用ID" class="input-field" />
</view>
<view class="form-item">
<text class="label">余额支付:</text>
<view class="switch-item">
<switch :checked="settings.balance_pay_enabled" @change="onBalancePayChange" />
<text class="switch-label">{{ settings.balance_pay_enabled ? '已开启' : '已关闭' }}</text>
</view>
</view>
</view>
</view>
<!-- 物流设置 -->
<view v-if="activeTab === 'shipping'" class="setting-section">
<view class="section-title">物流设置</view>
<view class="setting-form">
<view class="form-item">
<text class="label">默认物流公司:</text>
<picker mode="selector" :range="expressCompanies" range-key="name" @change="onDefaultExpressChange">
<view class="picker-text">{{ selectedExpress || '选择物流公司' }}</view>
</picker>
</view>
<view class="form-item">
<text class="label">运费计算方式:</text>
<picker mode="selector" :range="shippingMethods" range-key="label" @change="onShippingMethodChange">
<view class="picker-text">{{ selectedShippingMethod || '选择计算方式' }}</view>
</picker>
</view>
<view class="form-item">
<text class="label">包邮金额:</text>
<input v-model="settings.free_shipping_amount" type="number" placeholder="请输入包邮金额" class="input-field" />
</view>
</view>
</view>
<!-- 消息设置 -->
<view v-if="activeTab === 'notification'" class="setting-section">
<view class="section-title">消息设置</view>
<view class="setting-form">
<view class="form-item">
<text class="label">短信通知:</text>
<view class="switch-item">
<switch :checked="settings.sms_enabled" @change="onSmsChange" />
<text class="switch-label">{{ settings.sms_enabled ? '已开启' : '已关闭' }}</text>
</view>
</view>
<view v-if="settings.sms_enabled" class="form-item">
<text class="label">短信服务商:</text>
<picker mode="selector" :range="smsProviders" range-key="label" @change="onSmsProviderChange">
<view class="picker-text">{{ selectedSmsProvider || '选择服务商' }}</view>
</picker>
</view>
<view class="form-item">
<text class="label">邮件通知:</text>
<view class="switch-item">
<switch :checked="settings.email_enabled" @change="onEmailChange" />
<text class="switch-label">{{ settings.email_enabled ? '已开启' : '已关闭' }}</text>
</view>
</view>
<view v-if="settings.email_enabled" class="form-item">
<text class="label">SMTP服务器</text>
<input v-model="settings.smtp_host" placeholder="请输入SMTP服务器" class="input-field" />
</view>
</view>
</view>
<!-- 保存按钮 -->
<view class="save-section">
<button class="btn btn-primary" @click="saveSettings">保存设置</button>
<button class="btn btn-default" @click="resetSettings">重置</button>
</view>
</view>
</template>
<script setup lang="uts">
import { ref, onMounted } from 'vue'
import supa from '@/components/supadb/aksupainstance.uts'
// 响应式数据
const activeTab = ref('basic')
const settingTabs = ref([
{ key: 'basic', label: '基础设置' },
{ key: 'payment', label: '支付设置' },
{ key: 'shipping', label: '物流设置' },
{ key: 'notification', label: '消息设置' }
])
const settings = ref({
// 基础设置
site_name: '',
site_domain: '',
site_logo: '',
service_phone: '',
site_description: '',
// 支付设置
wechat_pay_enabled: false,
wechat_mch_id: '',
wechat_pay_key: '',
alipay_enabled: false,
alipay_app_id: '',
balance_pay_enabled: true,
// 物流设置
default_express: '',
shipping_method: '',
free_shipping_amount: '',
// 消息设置
sms_enabled: false,
sms_provider: '',
email_enabled: false,
smtp_host: ''
})
// 选项数据
const expressCompanies = ref([])
const shippingMethods = ref([
{ value: 'fixed', label: '固定运费' },
{ value: 'weight', label: '按重量计算' },
{ value: 'pieces', label: '按件数计算' }
])
const smsProviders = ref([
{ value: 'aliyun', label: '阿里云短信' },
{ value: 'tencent', label: '腾讯云短信' },
{ value: 'qiniu', label: '七牛云短信' }
])
const selectedExpress = ref('')
const selectedShippingMethod = ref('')
const selectedSmsProvider = ref('')
// 方法
const switchTab = (tabKey: string) => {
activeTab.value = tabKey
}
const onWechatPayChange = (e: any) => {
settings.value.wechat_pay_enabled = e.detail.value
}
const onAlipayChange = (e: any) => {
settings.value.alipay_enabled = e.detail.value
}
const onBalancePayChange = (e: any) => {
settings.value.balance_pay_enabled = e.detail.value
}
const onSmsChange = (e: any) => {
settings.value.sms_enabled = e.detail.value
}
const onEmailChange = (e: any) => {
settings.value.email_enabled = e.detail.value
}
const onDefaultExpressChange = (e: any) => {
selectedExpress.value = expressCompanies.value[e.detail.value].name
settings.value.default_express = expressCompanies.value[e.detail.value].code
}
const onShippingMethodChange = (e: any) => {
selectedShippingMethod.value = shippingMethods.value[e.detail.value].label
settings.value.shipping_method = shippingMethods.value[e.detail.value].value
}
const onSmsProviderChange = (e: any) => {
selectedSmsProvider.value = smsProviders.value[e.detail.value].label
settings.value.sms_provider = smsProviders.value[e.detail.value].value
}
const chooseLogo = () => {
uni.chooseImage({
count: 1,
sizeType: ['compressed'],
sourceType: ['album', 'camera'],
success: (res) => {
const tempFilePath = res.tempFilePaths[0]
// 这里应该上传图片到服务器
settings.value.site_logo = tempFilePath // 临时显示
uni.showToast({
title: '图片选择成功',
icon: 'success'
})
}
})
}
const saveSettings = async () => {
try {
await supa.from('system_settings').upsert(settings.value)
uni.showToast({
title: '保存成功',
icon: 'success'
})
} catch (error) {
console.error('保存设置失败:', error)
uni.showToast({
title: '保存失败',
icon: 'error'
})
}
}
const resetSettings = () => {
loadSettings()
}
// 数据加载方法
const loadSettings = async () => {
try {
const { data } = await supa.from('system_settings').select('*').single()
if (data) {
settings.value = { ...settings.value, ...data }
}
} catch (error) {
console.error('加载设置失败:', error)
}
}
const loadExpressCompanies = async () => {
try {
const { data } = await supa.from('express_companies').select('*')
expressCompanies.value = data || []
} catch (error) {
console.error('加载物流公司失败:', error)
}
}
// 页面初始化
onMounted(async () => {
await Promise.all([
loadSettings(),
loadExpressCompanies()
])
})
</script>
<style lang="scss">
.system-settings {
padding: 30rpx;
background-color: #f5f5f5;
min-height: 100vh;
}
// 设置选项卡样式
.settings-tabs {
background-color: #fff;
border-radius: 12rpx;
margin-bottom: 30rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
.tab-list {
display: flex;
padding: 0 30rpx;
}
.tab-item {
flex: 1;
padding: 30rpx 20rpx;
text-align: center;
cursor: pointer;
transition: all 0.2s;
&.active {
background-color: #007bff;
color: white;
}
.tab-text {
font-size: 28rpx;
color: #495057;
}
&.active .tab-text {
color: white;
}
}
}
// 设置表单样式
.setting-section {
background-color: #fff;
border-radius: 12rpx;
padding: 30rpx;
margin-bottom: 30rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
.section-title {
font-size: 32rpx;
font-weight: bold;
color: #212529;
margin-bottom: 30rpx;
}
.setting-form {
.form-item {
display: flex;
align-items: center;
margin-bottom: 30rpx;
.label {
font-size: 28rpx;
color: #666;
margin-right: 20rpx;
white-space: nowrap;
min-width: 150rpx;
}
.input-field {
flex: 1;
height: 60rpx;
border: 1rpx solid #ddd;
border-radius: 6rpx;
padding: 0 20rpx;
font-size: 26rpx;
}
.textarea-field {
flex: 1;
min-height: 120rpx;
border: 1rpx solid #ddd;
border-radius: 6rpx;
padding: 20rpx;
font-size: 26rpx;
line-height: 1.5;
}
.switch-item {
display: flex;
align-items: center;
gap: 20rpx;
.switch-label {
font-size: 26rpx;
color: #666;
}
}
.picker-text {
padding: 0 20rpx;
height: 60rpx;
line-height: 60rpx;
border: 1rpx solid #ddd;
border-radius: 6rpx;
font-size: 26rpx;
color: #333;
min-width: 200rpx;
}
.upload-area {
display: flex;
align-items: center;
gap: 20rpx;
.logo-preview {
width: 80rpx;
height: 80rpx;
border-radius: 8rpx;
object-fit: cover;
}
.upload-btn {
padding: 12rpx 24rpx;
border: 1rpx solid #ddd;
border-radius: 6rpx;
background-color: #f5f5f5;
font-size: 26rpx;
color: #333;
cursor: pointer;
}
}
}
}
}
// 保存按钮区域
.save-section {
background-color: #fff;
border-radius: 12rpx;
padding: 30rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
display: flex;
gap: 20rpx;
justify-content: center;
.btn {
padding: 15rpx 40rpx;
border-radius: 8rpx;
font-size: 28rpx;
border: none;
cursor: pointer;
&.btn-primary {
background-color: #007bff;
color: white;
}
&.btn-default {
background-color: #f5f5f5;
color: #333;
}
}
}
// 响应式设计
@media (max-width: 750rpx) {
.settings-tabs {
.tab-list {
flex-direction: column;
}
.tab-item {
padding: 20rpx;
}
}
.form-item {
flex-direction: column;
align-items: stretch;
.label {
margin-bottom: 10rpx;
min-width: auto;
}
.input-field,
.textarea-field,
.picker-text {
width: 100%;
}
.switch-item {
justify-content: space-between;
}
}
.save-section {
flex-direction: column;
align-items: stretch;
.btn {
width: 100%;
}
}
}
</style>