This commit is contained in:
zpc 2026-02-20 19:40:36 +08:00
parent d00dc98794
commit 04219e8436
8 changed files with 1101 additions and 37 deletions

View File

@ -27,6 +27,8 @@ public class ConfigController : ControllerBase
private static class ConfigKeys
{
public const string Upload = "uploads";
public const string Miniprogram = "miniprogram_setting";
public const string WeixinPay = "weixinpay_setting";
}
public ConfigController(IAdminConfigService configService, ILogger<ConfigController> logger)
@ -169,4 +171,117 @@ public class ConfigController : ControllerBase
return Task.FromResult(ApiResponse<bool>.Error(AdminErrorCodes.InternalError, $"连接测试失败: {ex.Message}"));
}
}
#region
/// <summary>
/// 获取小程序配置
/// </summary>
/// <returns>小程序配置</returns>
[HttpGet("miniprogram/get")]
public async Task<ApiResponse<MiniprogramSetting>> GetMiniprogramConfig()
{
try
{
var config = await _configService.GetConfigAsync<MiniprogramSetting>(ConfigKeys.Miniprogram);
return ApiResponse<MiniprogramSetting>.Success(config ?? new MiniprogramSetting());
}
catch (Exception ex)
{
_logger.LogError(ex, "获取小程序配置失败");
return ApiResponse<MiniprogramSetting>.Error(AdminErrorCodes.InternalError, "获取小程序配置失败");
}
}
/// <summary>
/// 更新小程序配置
/// </summary>
/// <param name="request">小程序配置</param>
/// <returns>更新结果</returns>
[HttpPost("miniprogram/update")]
[OperationLog("配置管理", "更新小程序配置")]
public async Task<ApiResponse<bool>> UpdateMiniprogramConfig([FromBody] MiniprogramSetting request)
{
// 验证至少有一个小程序配置
if (request.Miniprograms == null || request.Miniprograms.Count == 0)
{
return ApiResponse<bool>.Error(AdminErrorCodes.InvalidParameter, "至少需要配置一个小程序");
}
// 验证每个小程序的必填字段
foreach (var mp in request.Miniprograms)
{
if (string.IsNullOrWhiteSpace(mp.AppId))
return ApiResponse<bool>.Error(AdminErrorCodes.InvalidParameter, "小程序AppId不能为空");
if (string.IsNullOrWhiteSpace(mp.AppSecret))
return ApiResponse<bool>.Error(AdminErrorCodes.InvalidParameter, "小程序AppSecret不能为空");
}
try
{
var result = await _configService.UpdateConfigAsync(ConfigKeys.Miniprogram, request);
return ApiResponse<bool>.Success(result, "小程序配置更新成功");
}
catch (Exception ex)
{
_logger.LogError(ex, "更新小程序配置失败");
return ApiResponse<bool>.Error(AdminErrorCodes.InternalError, "更新小程序配置失败");
}
}
#endregion
#region
/// <summary>
/// 获取微信支付配置
/// </summary>
/// <returns>微信支付配置</returns>
[HttpGet("weixinpay/get")]
public async Task<ApiResponse<WeixinPaySetting>> GetWeixinPayConfig()
{
try
{
var config = await _configService.GetConfigAsync<WeixinPaySetting>(ConfigKeys.WeixinPay);
return ApiResponse<WeixinPaySetting>.Success(config ?? new WeixinPaySetting());
}
catch (Exception ex)
{
_logger.LogError(ex, "获取微信支付配置失败");
return ApiResponse<WeixinPaySetting>.Error(AdminErrorCodes.InternalError, "获取微信支付配置失败");
}
}
/// <summary>
/// 更新微信支付配置
/// </summary>
/// <param name="request">微信支付配置</param>
/// <returns>更新结果</returns>
[HttpPost("weixinpay/update")]
[OperationLog("配置管理", "更新微信支付配置")]
public async Task<ApiResponse<bool>> UpdateWeixinPayConfig([FromBody] WeixinPaySetting request)
{
// 验证商户配置
if (request.Merchants != null)
{
foreach (var merchant in request.Merchants)
{
if (string.IsNullOrWhiteSpace(merchant.MchId))
return ApiResponse<bool>.Error(AdminErrorCodes.InvalidParameter, "商户号不能为空");
}
}
try
{
var result = await _configService.UpdateConfigAsync(ConfigKeys.WeixinPay, request);
return ApiResponse<bool>.Success(result, "微信支付配置更新成功");
}
catch (Exception ex)
{
_logger.LogError(ex, "更新微信支付配置失败");
return ApiResponse<bool>.Error(AdminErrorCodes.InternalError, "更新微信支付配置失败");
}
}
#endregion
}

View File

@ -0,0 +1,57 @@
using System.Text.Json.Serialization;
namespace MiAssessment.Admin.Models.Config;
/// <summary>
/// 小程序配置
/// </summary>
public class MiniprogramSetting
{
/// <summary>
/// 小程序列表
/// </summary>
[JsonPropertyName("miniprograms")]
public List<MiniprogramConfig> Miniprograms { get; set; } = new();
}
/// <summary>
/// 小程序配置项
/// </summary>
public class MiniprogramConfig
{
/// <summary>
/// 小程序名称
/// </summary>
[JsonPropertyName("name")]
public string Name { get; set; } = string.Empty;
/// <summary>
/// AppId
/// </summary>
[JsonPropertyName("appid")]
public string AppId { get; set; } = string.Empty;
/// <summary>
/// AppSecret
/// </summary>
[JsonPropertyName("appsecret")]
public string? AppSecret { get; set; }
/// <summary>
/// 订单前缀必须2位
/// </summary>
[JsonPropertyName("order_prefix")]
public string? OrderPrefix { get; set; }
/// <summary>
/// 是否默认 0否 1是
/// </summary>
[JsonPropertyName("is_default")]
public int IsDefault { get; set; }
/// <summary>
/// 关联商户列表
/// </summary>
[JsonPropertyName("merchants")]
public List<string>? Merchants { get; set; }
}

View File

@ -0,0 +1,93 @@
using System.Text.Json.Serialization;
namespace MiAssessment.Admin.Models.Config;
/// <summary>
/// 微信支付配置
/// </summary>
public class WeixinPaySetting
{
/// <summary>
/// 商户列表
/// </summary>
[JsonPropertyName("merchants")]
public List<WeixinPayMerchant> Merchants { get; set; } = new();
}
/// <summary>
/// 微信支付商户配置
/// </summary>
public class WeixinPayMerchant
{
/// <summary>
/// 商户名称
/// </summary>
[JsonPropertyName("name")]
public string Name { get; set; } = string.Empty;
/// <summary>
/// 商户号
/// </summary>
[JsonPropertyName("mch_id")]
public string MchId { get; set; } = string.Empty;
/// <summary>
/// 订单前缀必须3位
/// </summary>
[JsonPropertyName("order_prefix")]
public string OrderPrefix { get; set; } = string.Empty;
/// <summary>
/// 支付版本: "V2" 或 "V3",默认 "V2"
/// </summary>
[JsonPropertyName("pay_version")]
public string PayVersion { get; set; } = "V3";
/// <summary>
/// APIv3 密钥32位字符串
/// </summary>
[JsonPropertyName("api_v3_key")]
public string? ApiV3Key { get; set; }
/// <summary>
/// 商户API证书序列号
/// </summary>
[JsonPropertyName("cert_serial_no")]
public string? CertSerialNo { get; set; }
/// <summary>
/// 商户私钥文件路径
/// </summary>
[JsonPropertyName("private_key_path")]
public string? PrivateKeyPath { get; set; }
/// <summary>
/// 微信支付公钥ID
/// </summary>
[JsonPropertyName("wechat_public_key_id")]
public string? WechatPublicKeyId { get; set; }
/// <summary>
/// 微信支付公钥文件路径
/// </summary>
[JsonPropertyName("wechat_public_key_path")]
public string? WechatPublicKeyPath { get; set; }
/// <summary>
/// API密钥V2版本使用
/// </summary>
[JsonPropertyName("api_key")]
public string? ApiKey { get; set; }
/// <summary>
/// 证书路径V2版本使用
/// </summary>
[JsonPropertyName("cert_path")]
public string? CertPath { get; set; }
/// <summary>
/// 是否启用
/// </summary>
[JsonPropertyName("is_enabled")]
public string? IsEnabled { get; set; } = "1";
}

View File

@ -55,3 +55,113 @@ export function testCosConnection(data: UploadSetting): Promise<ApiResponse<bool
data
})
}
// ==================== 小程序配置 ====================
/**
*
*/
export interface MiniprogramConfig {
/** 小程序名称 */
name: string
/** AppId */
appid: string
/** AppSecret */
appsecret?: string
/** 订单前缀2位 */
order_prefix?: string
/** 是否默认 0否 1是 */
is_default: number
/** 关联商户列表 */
merchants?: string[]
}
/**
*
*/
export interface MiniprogramSetting {
/** 小程序列表 */
miniprograms: MiniprogramConfig[]
}
/**
*
*/
export function getMiniprogramConfig(): Promise<ApiResponse<MiniprogramSetting>> {
return request<MiniprogramSetting>({
url: '/admin/config/miniprogram/get',
method: 'get'
})
}
/**
*
*/
export function updateMiniprogramConfig(data: MiniprogramSetting): Promise<ApiResponse<boolean>> {
return request<boolean>({
url: '/admin/config/miniprogram/update',
method: 'post',
data
})
}
// ==================== 微信支付配置 ====================
/**
*
*/
export interface WeixinPayMerchant {
/** 商户名称 */
name: string
/** 商户号 */
mch_id: string
/** 订单前缀3位 */
order_prefix: string
/** 支付版本 V2/V3 */
pay_version: string
/** APIv3密钥 */
api_v3_key?: string
/** 证书序列号 */
cert_serial_no?: string
/** 商户私钥文件路径 */
private_key_path?: string
/** 微信支付公钥ID */
wechat_public_key_id?: string
/** 微信支付公钥文件路径 */
wechat_public_key_path?: string
/** API密钥V2 */
api_key?: string
/** 证书路径V2 */
cert_path?: string
/** 是否启用 */
is_enabled?: string
}
/**
*
*/
export interface WeixinPaySetting {
/** 商户列表 */
merchants: WeixinPayMerchant[]
}
/**
*
*/
export function getWeixinPayConfig(): Promise<ApiResponse<WeixinPaySetting>> {
return request<WeixinPaySetting>({
url: '/admin/config/weixinpay/get',
method: 'get'
})
}
/**
*
*/
export function updateWeixinPayConfig(data: WeixinPaySetting): Promise<ApiResponse<boolean>> {
return request<boolean>({
url: '/admin/config/weixinpay/update',
method: 'post',
data
})
}

View File

@ -0,0 +1,34 @@
<template>
<div class="config-container">
<el-tabs v-model="activeTab" type="border-card">
<el-tab-pane label="小程序配置" name="miniprogram">
<MiniprogramConfig />
</el-tab-pane>
<el-tab-pane label="支付配置" name="payment">
<PaymentConfig />
</el-tab-pane>
<el-tab-pane label="上传配置" name="upload">
<UploadConfig />
</el-tab-pane>
</el-tabs>
</div>
</template>
<script setup lang="ts">
/**
* 系统配置管理 - Tab容器页面
*/
import { ref } from 'vue'
import UploadConfig from './upload.vue'
import MiniprogramConfig from './miniprogram.vue'
import PaymentConfig from './payment.vue'
// Tab
const activeTab = ref('miniprogram')
</script>
<style scoped>
.config-container {
padding: 20px;
}
</style>

View File

@ -0,0 +1,302 @@
<template>
<div class="miniprogram-config-container">
<!-- 页面标题 -->
<el-card class="page-header">
<div class="header-content">
<h2 class="page-title">小程序配置</h2>
<span class="page-description">配置微信小程序的AppId和AppSecret用于微信登录和授权</span>
</div>
</el-card>
<!-- 配置表单 -->
<el-card v-loading="state.loading" class="config-form-card">
<template #header>
<div class="card-header">
<span>小程序列表</span>
<el-button type="primary" :icon="Plus" @click="addMiniprogram">添加小程序</el-button>
</div>
</template>
<!-- 空状态 -->
<el-empty v-if="state.formData.miniprograms.length === 0" description="暂无小程序配置,请点击上方按钮添加" />
<!-- 小程序配置列表 -->
<div v-for="(mp, index) in state.formData.miniprograms" :key="index" class="miniprogram-item">
<div class="item-header">
<span class="item-title">
<el-tag v-if="mp.is_default === 1" type="success" size="small">默认</el-tag>
{{ mp.name || `小程序 ${index + 1}` }}
</span>
<div class="item-actions">
<el-button
v-if="mp.is_default !== 1"
type="primary"
link
size="small"
@click="setDefault(index)"
>设为默认</el-button>
<el-button
type="danger"
link
size="small"
:icon="Delete"
@click="removeMiniprogram(index)"
>删除</el-button>
</div>
</div>
<el-form label-width="140px" label-position="right">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="小程序名称" required>
<el-input v-model="mp.name" placeholder="请输入小程序名称" clearable />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="订单前缀">
<el-input v-model="mp.order_prefix" placeholder="2位字符" maxlength="2" clearable />
<div class="form-item-tip">用于区分不同小程序的订单必须2位</div>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="AppId" required>
<el-input v-model="mp.appid" placeholder="请输入小程序AppId" clearable />
<div class="form-item-tip">微信小程序的AppId在微信公众平台获取</div>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="AppSecret" required>
<el-input
v-model="mp.appsecret"
placeholder="请输入小程序AppSecret"
type="password"
show-password
clearable
/>
<div class="form-item-tip">微信小程序的AppSecret请妥善保管</div>
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
<!-- 操作按钮 -->
<div v-if="state.formData.miniprograms.length > 0" class="form-actions">
<el-button type="primary" :loading="state.saving" @click="handleSave">保存配置</el-button>
<el-button @click="handleReset">重置</el-button>
</div>
</el-card>
</div>
</template>
<script setup lang="ts">
/**
* 小程序配置页面
*/
import { reactive, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Plus, Delete } from '@element-plus/icons-vue'
import {
getMiniprogramConfig,
updateMiniprogramConfig,
type MiniprogramSetting,
type MiniprogramConfig
} from '@/api/system/config'
// State
const state = reactive({
loading: false,
saving: false,
formData: {
miniprograms: [] as MiniprogramConfig[]
} as MiniprogramSetting
})
/**
* 加载配置
*/
async function loadConfig() {
state.loading = true
try {
const res = await getMiniprogramConfig()
if (res.code === 0 && res.data) {
state.formData = res.data
// miniprograms
if (!state.formData.miniprograms) {
state.formData.miniprograms = []
}
}
} catch (error) {
console.error('加载小程序配置失败:', error)
ElMessage.error('加载配置失败')
} finally {
state.loading = false
}
}
/**
* 添加小程序
*/
function addMiniprogram() {
const isFirst = state.formData.miniprograms.length === 0
state.formData.miniprograms.push({
name: '',
appid: '',
appsecret: '',
order_prefix: '',
is_default: isFirst ? 1 : 0,
merchants: []
})
}
/**
* 删除小程序
*/
async function removeMiniprogram(index: number) {
const mp = state.formData.miniprograms[index]
await ElMessageBox.confirm(
`确定删除小程序「${mp.name || '未命名'}」吗?`,
'提示',
{ type: 'warning' }
)
const wasDefault = mp.is_default === 1
state.formData.miniprograms.splice(index, 1)
//
if (wasDefault && state.formData.miniprograms.length > 0) {
state.formData.miniprograms[0].is_default = 1
}
}
/**
* 设为默认
*/
function setDefault(index: number) {
state.formData.miniprograms.forEach((mp, i) => {
mp.is_default = i === index ? 1 : 0
})
}
/**
* 保存配置
*/
async function handleSave() {
//
for (const mp of state.formData.miniprograms) {
if (!mp.appid?.trim()) {
ElMessage.warning('请填写所有小程序的AppId')
return
}
if (!mp.appsecret?.trim()) {
ElMessage.warning('请填写所有小程序的AppSecret')
return
}
}
state.saving = true
try {
const res = await updateMiniprogramConfig(state.formData)
if (res.code === 0) {
ElMessage.success('保存成功')
} else {
ElMessage.error(res.message || '保存失败')
}
} catch (error) {
console.error('保存小程序配置失败:', error)
ElMessage.error('保存失败')
} finally {
state.saving = false
}
}
/**
* 重置配置
*/
function handleReset() {
loadConfig()
}
// Lifecycle
onMounted(() => {
loadConfig()
})
</script>
<style scoped>
.page-header {
margin-bottom: 20px;
}
.header-content {
display: flex;
align-items: baseline;
gap: 12px;
}
.page-title {
margin: 0;
font-size: 18px;
font-weight: 600;
}
.page-description {
color: #909399;
font-size: 14px;
}
.config-form-card {
margin-bottom: 20px;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.miniprogram-item {
border: 1px solid #ebeef5;
border-radius: 8px;
padding: 20px;
margin-bottom: 16px;
background: #fafafa;
}
.item-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
padding-bottom: 12px;
border-bottom: 1px solid #ebeef5;
}
.item-title {
font-size: 15px;
font-weight: 500;
display: flex;
align-items: center;
gap: 8px;
}
.item-actions {
display: flex;
gap: 8px;
}
.form-item-tip {
font-size: 12px;
color: #909399;
line-height: 1.4;
margin-top: 4px;
}
.form-actions {
display: flex;
justify-content: center;
gap: 12px;
padding-top: 20px;
border-top: 1px solid #ebeef5;
}
</style>

View File

@ -0,0 +1,353 @@
<template>
<div class="payment-config-container">
<!-- 页面标题 -->
<el-card class="page-header">
<div class="header-content">
<h2 class="page-title">微信支付配置</h2>
<span class="page-description">配置微信支付商户信息支持V2和V3版本</span>
</div>
</el-card>
<!-- 配置表单 -->
<el-card v-loading="state.loading" class="config-form-card">
<template #header>
<div class="card-header">
<span>商户列表</span>
<el-button type="primary" :icon="Plus" @click="addMerchant">添加商户</el-button>
</div>
</template>
<!-- 空状态 -->
<el-empty v-if="state.formData.merchants.length === 0" description="暂无支付商户配置,请点击上方按钮添加" />
<!-- 商户配置列表 -->
<div v-for="(merchant, index) in state.formData.merchants" :key="index" class="merchant-item">
<div class="item-header">
<span class="item-title">
<el-tag :type="merchant.is_enabled === '1' ? 'success' : 'info'" size="small">
{{ merchant.is_enabled === '1' ? '启用' : '禁用' }}
</el-tag>
{{ merchant.name || `商户 ${index + 1}` }}
</span>
<div class="item-actions">
<el-button
:type="merchant.is_enabled === '1' ? 'warning' : 'success'"
link
size="small"
@click="toggleEnabled(merchant)"
>{{ merchant.is_enabled === '1' ? '禁用' : '启用' }}</el-button>
<el-button
type="danger"
link
size="small"
:icon="Delete"
@click="removeMerchant(index)"
>删除</el-button>
</div>
</div>
<el-form label-width="160px" label-position="right">
<!-- 基础信息 -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="商户名称" required>
<el-input v-model="merchant.name" placeholder="请输入商户名称" clearable />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="商户号" required>
<el-input v-model="merchant.mch_id" placeholder="请输入微信支付商户号" clearable />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="订单前缀">
<el-input v-model="merchant.order_prefix" placeholder="3位字符" maxlength="3" clearable />
<div class="form-item-tip">用于区分不同商户的订单必须3位</div>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="支付版本">
<el-radio-group v-model="merchant.pay_version">
<el-radio value="V3">V3推荐</el-radio>
<el-radio value="V2">V2</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
</el-row>
<!-- V3 配置 -->
<template v-if="merchant.pay_version === 'V3'">
<el-divider content-position="left">V3 支付配置</el-divider>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="APIv3密钥">
<el-input
v-model="merchant.api_v3_key"
placeholder="32位APIv3密钥"
type="password"
show-password
clearable
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="证书序列号">
<el-input v-model="merchant.cert_serial_no" placeholder="商户API证书序列号" clearable />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="商户私钥路径">
<el-input v-model="merchant.private_key_path" placeholder="apiclient_key.pem 文件路径" clearable />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="微信支付公钥ID">
<el-input v-model="merchant.wechat_public_key_id" placeholder="微信支付公钥ID" clearable />
</el-form-item>
</el-col>
</el-row>
<el-form-item label="微信支付公钥路径">
<el-input v-model="merchant.wechat_public_key_path" placeholder="pub_key.pem 文件路径" clearable />
</el-form-item>
</template>
<!-- V2 配置 -->
<template v-if="merchant.pay_version === 'V2'">
<el-divider content-position="left">V2 支付配置</el-divider>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="API密钥">
<el-input
v-model="merchant.api_key"
placeholder="微信支付API密钥"
type="password"
show-password
clearable
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="证书路径">
<el-input v-model="merchant.cert_path" placeholder="apiclient_cert.p12 文件路径" clearable />
</el-form-item>
</el-col>
</el-row>
</template>
</el-form>
</div>
<!-- 操作按钮 -->
<div v-if="state.formData.merchants.length > 0" class="form-actions">
<el-button type="primary" :loading="state.saving" @click="handleSave">保存配置</el-button>
<el-button @click="handleReset">重置</el-button>
</div>
</el-card>
</div>
</template>
<script setup lang="ts">
/**
* 微信支付配置页面
*/
import { reactive, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Plus, Delete } from '@element-plus/icons-vue'
import {
getWeixinPayConfig,
updateWeixinPayConfig,
type WeixinPaySetting,
type WeixinPayMerchant
} from '@/api/system/config'
// State
const state = reactive({
loading: false,
saving: false,
formData: {
merchants: [] as WeixinPayMerchant[]
} as WeixinPaySetting
})
/**
* 加载配置
*/
async function loadConfig() {
state.loading = true
try {
const res = await getWeixinPayConfig()
if (res.code === 0 && res.data) {
state.formData = res.data
if (!state.formData.merchants) {
state.formData.merchants = []
}
}
} catch (error) {
console.error('加载支付配置失败:', error)
ElMessage.error('加载配置失败')
} finally {
state.loading = false
}
}
/**
* 添加商户
*/
function addMerchant() {
state.formData.merchants.push({
name: '',
mch_id: '',
order_prefix: '',
pay_version: 'V3',
api_v3_key: '',
cert_serial_no: '',
private_key_path: '',
wechat_public_key_id: '',
wechat_public_key_path: '',
api_key: '',
cert_path: '',
is_enabled: '1'
})
}
/**
* 删除商户
*/
async function removeMerchant(index: number) {
const merchant = state.formData.merchants[index]
await ElMessageBox.confirm(
`确定删除商户「${merchant.name || '未命名'}」吗?`,
'提示',
{ type: 'warning' }
)
state.formData.merchants.splice(index, 1)
}
/**
* 切换启用状态
*/
function toggleEnabled(merchant: WeixinPayMerchant) {
merchant.is_enabled = merchant.is_enabled === '1' ? '0' : '1'
}
/**
* 保存配置
*/
async function handleSave() {
//
for (const merchant of state.formData.merchants) {
if (!merchant.mch_id?.trim()) {
ElMessage.warning('请填写所有商户的商户号')
return
}
}
state.saving = true
try {
const res = await updateWeixinPayConfig(state.formData)
if (res.code === 0) {
ElMessage.success('保存成功')
} else {
ElMessage.error(res.message || '保存失败')
}
} catch (error) {
console.error('保存支付配置失败:', error)
ElMessage.error('保存失败')
} finally {
state.saving = false
}
}
/**
* 重置配置
*/
function handleReset() {
loadConfig()
}
// Lifecycle
onMounted(() => {
loadConfig()
})
</script>
<style scoped>
.page-header {
margin-bottom: 20px;
}
.header-content {
display: flex;
align-items: baseline;
gap: 12px;
}
.page-title {
margin: 0;
font-size: 18px;
font-weight: 600;
}
.page-description {
color: #909399;
font-size: 14px;
}
.config-form-card {
margin-bottom: 20px;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.merchant-item {
border: 1px solid #ebeef5;
border-radius: 8px;
padding: 20px;
margin-bottom: 16px;
background: #fafafa;
}
.item-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
padding-bottom: 12px;
border-bottom: 1px solid #ebeef5;
}
.item-title {
font-size: 15px;
font-weight: 500;
display: flex;
align-items: center;
gap: 8px;
}
.item-actions {
display: flex;
gap: 8px;
}
.form-item-tip {
font-size: 12px;
color: #909399;
line-height: 1.4;
margin-top: 4px;
}
.form-actions {
display: flex;
justify-content: center;
gap: 12px;
padding-top: 20px;
border-top: 1px solid #ebeef5;
}
</style>

View File

@ -14,7 +14,7 @@
<!-- 底部登录区域 -->
<view class="bottom-section">
<!-- 登录按钮 - 使用 open-type="getPhoneNumber" 获取手机号 -->
<!-- 登录按钮 -->
<button
class="btn-login"
:class="{ 'btn-disabled': !isAgreed }"
@ -28,12 +28,12 @@
<!-- 协议勾选 -->
<view class="agreement-section" @click="toggleAgreement">
<view class="checkbox" :class="{ 'checkbox-checked': isAgreed }">
<view v-if="isAgreed" class="checkbox-icon"></view>
<image v-if="isAgreed" src="/static/ic_check_s.png" class="checkbox-icon" mode="aspectFit" />
</view>
<view class="agreement-text">
<text>我已阅读并同意</text>
<text>注册即同意我们的</text>
<text class="link" @click.stop="goUserAgreement">用户协议</text>
<text></text>
<text></text>
<text class="link" @click.stop="goPrivacyPolicy">隐私政策</text>
</view>
</view>
@ -281,6 +281,10 @@ onMounted(() => {
<style lang="scss" scoped>
@import '@/styles/variables.scss';
//
$login-btn-color: #F5A623;
$login-btn-active: #E09518;
.login-page {
min-height: 100vh;
background-color: $bg-white;
@ -292,7 +296,7 @@ onMounted(() => {
flex: 1;
display: flex;
flex-direction: column;
padding: 0 54rpx;
padding: 0 60rpx;
}
.logo-section {
@ -300,18 +304,19 @@ onMounted(() => {
display: flex;
justify-content: center;
align-items: center;
padding-top: 80rpx;
.logo-wrapper {
width: 494rpx;
height: 494rpx;
width: 360rpx;
height: 360rpx;
border: 2rpx solid $border-color;
border-radius: $border-radius-md;
display: flex;
align-items: center;
justify-content: center;
.logo-img {
width: 100%;
height: 100%;
width: 280rpx;
height: 280rpx;
}
}
}
@ -321,21 +326,22 @@ onMounted(() => {
.btn-login {
width: 100%;
height: 72rpx;
line-height: 72rpx;
background-color: $primary-color;
border-radius: 36rpx;
font-size: 30rpx;
height: 88rpx;
line-height: 88rpx;
background-color: $login-btn-color;
border-radius: 44rpx;
font-size: $font-size-lg;
color: $text-white;
border: none;
font-weight: $font-weight-medium;
letter-spacing: 2rpx;
&::after {
border: none;
}
&:active {
opacity: 0.8;
background-color: $login-btn-active;
}
}
@ -347,43 +353,37 @@ onMounted(() => {
.agreement-section {
display: flex;
align-items: flex-start;
align-items: center;
justify-content: center;
margin-top: 32rpx;
padding: 0 20rpx;
.checkbox {
width: 32rpx;
height: 32rpx;
width: 28rpx;
height: 28rpx;
border: 2rpx solid $border-color;
border-radius: 4rpx;
margin-right: 12rpx;
margin-top: 4rpx;
border-radius: 50%;
margin-right: 10rpx;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
&-checked {
background-color: $primary-color;
border-color: $primary-color;
border-color: $success-color;
background-color: $success-color;
}
.checkbox-icon {
width: 16rpx;
height: 10rpx;
border-left: 3rpx solid $text-white;
border-bottom: 3rpx solid $text-white;
transform: rotate(-45deg);
margin-top: -4rpx;
width: 28rpx;
height: 28rpx;
}
}
.agreement-text {
font-size: 24rpx;
color: $text-secondary;
font-size: $font-size-sm;
color: $text-placeholder;
line-height: 1.6;
flex-wrap: wrap;
.link {
color: $primary-color;