From 75417417e5f8eb4e10911bd47eba5b179288b9c1 Mon Sep 17 00:00:00 2001 From: XingQue Date: Tue, 14 Jul 2026 02:44:44 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B0=8F=E7=A8=8B=E5=BA=8F=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=87=AA=E5=8A=A8=E6=8F=90=E7=8E=B0=E5=A4=9A?= =?UTF-8?q?=E5=95=86=E6=88=B7=E7=AE=A1=E7=90=86=EF=BC=9B=E8=A7=A3=E5=86=BB?= =?UTF-8?q?=E6=88=90=E5=8A=9F=E5=90=8E=E8=81=94=E5=8A=A8=E5=88=B7=E6=96=B0?= =?UTF-8?q?=E8=AF=A6=E6=83=85=E4=BD=99=E9=A2=9D=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增商户列表/保存/证书上传入口(8080a);解冻面板回写余额并提示变动明细。 Co-authored-by: Cursor --- src/components/FreezeConfigPanel.vue | 8 +- src/views/miniapp/MiniappConfig.vue | 246 +++++++++++++++++++++++++++ src/views/user/GuanliDetail.vue | 12 ++ src/views/user/PlayerDetail.vue | 20 +++ src/views/user/ShangjiaDetail.vue | 12 ++ 5 files changed, 297 insertions(+), 1 deletion(-) diff --git a/src/components/FreezeConfigPanel.vue b/src/components/FreezeConfigPanel.vue index bdf57a9..fe4750b 100644 --- a/src/components/FreezeConfigPanel.vue +++ b/src/components/FreezeConfigPanel.vue @@ -67,6 +67,8 @@ const props = defineProps({ isEditMode: { type: Boolean, default: false }, }) +const emit = defineEmits(['unfrozen']) + const username = computed(() => localStorage.getItem('username') || '') const info = reactive({ @@ -153,10 +155,14 @@ const handleUnfreeze = async () => { beizhu: unfreezeRemark.value, }) if (res.code === 0) { - ElMessage.success('解冻成功') + const tip = res.data?.unfreeze_result + ? `解冻成功:余额 ${res.data.unfreeze_result.balance_before} → ${res.data.unfreeze_result.balance_after} 元` + : '解冻成功' + ElMessage.success(tip) unfreezeAmount.value = 0 unfreezeRemark.value = '' await load() + emit('unfrozen', res.data || {}) } else { ElMessage.error(res.msg || '解冻失败') } diff --git a/src/views/miniapp/MiniappConfig.vue b/src/views/miniapp/MiniappConfig.vue index 312da72..9e1783d 100644 --- a/src/views/miniapp/MiniappConfig.vue +++ b/src/views/miniapp/MiniappConfig.vue @@ -170,6 +170,110 @@ + + + +
+ 新增商户 + 刷新 + 证书目录:{{ mchCertBase || '加载后显示' }} +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 数字越小越优先 + + + + + + + + 上传 apiclient_key.pem + + + + + + 上传 pub_key.pem + + + + + +
@@ -209,6 +313,147 @@ const assetList = ref([]) const assetLoading = ref(false) const assetSaving = ref(false) +const mchLoading = ref(false) +const mchSaving = ref(false) +const mchList = ref([]) +const mchCertBase = ref('') +const mchDialogVisible = ref(false) +const mchForm = reactive({ + id: null, + mch_id: '', + name: '', + appid: '', + api_v3_key: '', + cert_serial_no: '', + transfer_scene_id: '1005', + notify_url: '', + priority: 100, + enabled: true, + private_key_path: '', + platform_pub_key_path: '', +}) + +const loadMchList = async () => { + mchLoading.value = true + try { + const res = await request.post('/peizhi/wxmchlb', { username: username.value }) + if (res.code === 403) { + hasPermission.value = false + return + } + if (res.code === 0 && res.data) { + mchList.value = res.data.list || [] + mchCertBase.value = res.data.cert_base_dir || '' + } else { + ElMessage.error(res.msg || '加载商户失败') + } + } catch { + ElMessage.error('网络错误') + } finally { + mchLoading.value = false + } +} + +const openMchDialog = (row) => { + if (row) { + Object.assign(mchForm, { + id: row.id, + mch_id: row.mch_id, + name: row.name || '', + appid: row.appid || '', + api_v3_key: '', + cert_serial_no: row.cert_serial_no || '', + transfer_scene_id: row.transfer_scene_id || '1005', + notify_url: row.notify_url || '', + priority: row.priority ?? 100, + enabled: !!row.enabled, + private_key_path: row.private_key_path || '', + platform_pub_key_path: row.platform_pub_key_path || '', + }) + } else { + Object.assign(mchForm, { + id: null, + mch_id: '', + name: '', + appid: sysForm.weixin_appid || '', + api_v3_key: '', + cert_serial_no: '', + transfer_scene_id: '1005', + notify_url: '', + priority: 100, + enabled: true, + private_key_path: '', + platform_pub_key_path: '', + }) + } + mchDialogVisible.value = true +} + +const saveMch = async () => { + if (!mchForm.mch_id || !mchForm.appid || !mchForm.cert_serial_no) { + ElMessage.warning('请填写商户号、AppID、证书序列号') + return + } + if (!mchForm.id && !mchForm.api_v3_key) { + ElMessage.warning('新建必须填写 APIv3 密钥') + return + } + mchSaving.value = true + try { + const payload = { + username: username.value, + id: mchForm.id, + mch_id: mchForm.mch_id, + name: mchForm.name, + appid: mchForm.appid, + cert_serial_no: mchForm.cert_serial_no, + transfer_scene_id: mchForm.transfer_scene_id || '1005', + notify_url: mchForm.notify_url, + priority: mchForm.priority, + enabled: mchForm.enabled, + private_key_path: mchForm.private_key_path, + platform_pub_key_path: mchForm.platform_pub_key_path, + } + if (mchForm.api_v3_key) payload.api_v3_key = mchForm.api_v3_key + const res = await request.post('/peizhi/wxmchbj', payload) + if (res.code === 0) { + ElMessage.success(res.msg || '保存成功') + mchDialogVisible.value = false + await loadMchList() + } else { + ElMessage.error(res.msg || '保存失败') + } + } catch { + ElMessage.error('网络错误') + } finally { + mchSaving.value = false + } +} + +const uploadMchFile = async (opt, fileType) => { + if (!mchForm.mch_id) { + ElMessage.warning('请先填写商户号') + return + } + const formData = new FormData() + formData.append('username', username.value) + formData.append('mch_id', mchForm.mch_id) + formData.append('file_type', fileType) + formData.append('file', opt.file) + try { + const res = await request.post('/peizhi/wxmchscwj', formData) + if (res.code === 0 && res.data) { + ElMessage.success('上传成功') + if (fileType === 'private_key') mchForm.private_key_path = res.data.saved_path + if (fileType === 'platform_pub_key') mchForm.platform_pub_key_path = res.data.saved_path + } else { + ElMessage.error(res.msg || '上传失败') + } + } catch { + ElMessage.error('上传失败') + } +} + const fullUrl = (path) => { if (!path) return '' if (path.startsWith('http')) return path @@ -358,6 +603,7 @@ const uploadAsset = async ({ file }, row) => { onMounted(() => { loadSysConfig() loadAssets() + loadMchList() }) diff --git a/src/views/user/GuanliDetail.vue b/src/views/user/GuanliDetail.vue index cd3f0a3..0e4a14b 100644 --- a/src/views/user/GuanliDetail.vue +++ b/src/views/user/GuanliDetail.vue @@ -279,6 +279,7 @@ title="管事分红" :is-edit-mode="true" class="freeze-section" + @unfrozen="onFreezeUnfrozen" /> @@ -495,6 +496,17 @@ const fetchGuanliDetail = async () => { } } +const onFreezeUnfrozen = async (payload) => { + const ur = payload?.unfreeze_result + if (ur) { + const after = parseFloat(ur.balance_after) + if (!Number.isNaN(after)) { + balanceForm.yue = after + } + } + await fetchGuanliDetail() +} + // 进入编辑模式:备份原始数据 const enterEditMode = () => { originalData = { diff --git a/src/views/user/PlayerDetail.vue b/src/views/user/PlayerDetail.vue index 4ae72ed..5ff3e5c 100644 --- a/src/views/user/PlayerDetail.vue +++ b/src/views/user/PlayerDetail.vue @@ -354,6 +354,7 @@ :leixing="1" title="打手佣金" :is-edit-mode="true" + @unfrozen="onFreezeUnfrozen" /> @@ -362,6 +363,7 @@ :leixing="5" title="打手押金" :is-edit-mode="true" + @unfrozen="onFreezeUnfrozen" /> @@ -487,6 +489,24 @@ const fetchDetail = async () => { } } +// 解冻后同步上方「可提现余额/押金」展示(冻结面板与详情卡片区联动) +const onFreezeUnfrozen = async (payload) => { + const ur = payload?.unfreeze_result + if (ur) { + const after = parseFloat(ur.balance_after) + if (!Number.isNaN(after)) { + if (payload.leixing === 5) { + playerInfo.value.yajin = after + if (isEditMode.value) editData.value.yajin = after + } else if (payload.leixing === 1) { + playerInfo.value.yue = after + if (isEditMode.value) editData.value.yue = after + } + } + } + await fetchDetail() +} + // 编辑模式 const toggleEditMode = () => { editData.value = JSON.parse(JSON.stringify(playerInfo.value)) diff --git a/src/views/user/ShangjiaDetail.vue b/src/views/user/ShangjiaDetail.vue index 7f164cd..52efcd4 100644 --- a/src/views/user/ShangjiaDetail.vue +++ b/src/views/user/ShangjiaDetail.vue @@ -108,6 +108,7 @@ title="商家余额" :is-edit-mode="true" class="freeze-section" + @unfrozen="onFreezeUnfrozen" /> @@ -240,6 +241,17 @@ const fetchMerchantDetail = async () => { } } +const onFreezeUnfrozen = async (payload) => { + const ur = payload?.unfreeze_result + if (ur) { + const after = parseFloat(ur.balance_after) + if (!Number.isNaN(after) && merchant.value) { + merchant.value.yue = after + } + } + await fetchMerchantDetail() +} + // 修改余额 const openBalanceDialog = () => { balanceForm.value = { action: 'add_balance', amount: null }