feat(distribution): 支持分站发布群固定二维码

主站分销配置可为每个站点绑定可复用企微码,号主在该站上传账号后发到对应发布群,不再消耗主站码池。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
yml2213
2026-09-15 19:02:10 +08:00
co-authored by Cursor
parent 776c33f83c
commit 1c86bef765
9 changed files with 397 additions and 16 deletions
@@ -11,6 +11,7 @@ export interface DistributionSite {
tax_enabled: boolean
tax_rate_bps: number
settlement_hold_days: number
listing_qrcode_url?: string
created_at: string
}
@@ -150,6 +151,7 @@ export async function updateDistributionSite(
| 'tax_enabled'
| 'tax_rate_bps'
| 'settlement_hold_days'
| 'listing_qrcode_url'
>
>
) {
@@ -1,7 +1,16 @@
<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue'
import { ElMessage, type FormInstance, type FormRules } from 'element-plus'
import { Plus, Shop, Link, DocumentCopy, Refresh, Clock } from '@element-plus/icons-vue'
import {
Plus,
Shop,
Link,
DocumentCopy,
Refresh,
Clock,
Picture,
Upload,
} from '@element-plus/icons-vue'
import {
fetchDistributionSites,
fetchDistributionSiteDefaults,
@@ -12,6 +21,8 @@ import {
type OnboardDistributionSitePayload,
type OnboardDistributionSiteResult,
} from '@/features/admin/api/adminDistribution'
import { uploadAdminFile } from '@/shared/api/files'
import AuthImage from '@/shared/components/business/AuthImage.vue'
import { formatDateTime } from '@/shared/utils/time'
import { readError } from '@/shared/utils/error'
@@ -34,6 +45,15 @@ const settlementForm = reactive({
settlement_hold_days: defaultSettlementHoldDays.value,
})
const qrDialogVisible = ref(false)
const qrSubmitting = ref(false)
const qrUploading = ref(false)
const qrForm = reactive({
site_id: 0,
site_name: '',
listing_qrcode_url: '',
})
function handleOpenSettlement(row: DistributionSite) {
settlementForm.site_id = row.id
settlementForm.site_name = row.site_name
@@ -57,6 +77,51 @@ async function handleSettlementSubmit() {
}
}
function handleOpenQrcode(row: DistributionSite) {
qrForm.site_id = row.id
qrForm.site_name = row.site_name
qrForm.listing_qrcode_url = row.listing_qrcode_url || ''
qrDialogVisible.value = true
}
async function uploadSiteQrcode(file: File) {
qrUploading.value = true
try {
const uploaded = await uploadAdminFile(file, 'qrcode')
qrForm.listing_qrcode_url = uploaded.url
ElMessage.success('二维码已上传,保存后生效')
} catch (err) {
ElMessage.error(readError(err, '上传二维码失败'))
} finally {
qrUploading.value = false
}
return false
}
function clearSiteQrcode() {
qrForm.listing_qrcode_url = ''
}
async function handleQrcodeSubmit() {
qrSubmitting.value = true
try {
await updateDistributionSite(qrForm.site_id, {
listing_qrcode_url: qrForm.listing_qrcode_url.trim(),
})
ElMessage.success(
qrForm.listing_qrcode_url.trim()
? `【${qrForm.site_name}】发布群固定二维码已更新`
: `【${qrForm.site_name}】已清空发布群固定二维码`
)
qrDialogVisible.value = false
await loadSites()
} catch (err) {
ElMessage.error(readError(err, '更新发布群二维码失败'))
} finally {
qrSubmitting.value = false
}
}
const onboardForm = reactive({
site_name: '',
site_code: '',
@@ -192,7 +257,8 @@ onMounted(() => {
<div>
<h2 class="page-title">分销站点管理</h2>
<p class="page-subtitle">
开通分销站、配置结算冷却期。利润按 35% 收号 / 40% 卖出 / 25% 服务费切分。
开通分销站、配置结算冷却期与发布群固定二维码。利润按 35% 收号 / 40% 卖出 / 25%
服务费切分。
</p>
</div>
<div class="header-actions">
@@ -248,12 +314,24 @@ onMounted(() => {
</el-tag>
</template>
</el-table-column>
<el-table-column label="发布群二维码" width="120" align="center">
<template #default="{ row }">
<AuthImage
v-if="row.listing_qrcode_url"
:source="row.listing_qrcode_url"
fit="cover"
:image-style="{ width: '48px', height: '48px', borderRadius: '6px' }"
:preview-src-list="[row.listing_qrcode_url]"
/>
<el-tag v-else type="info" size="small">未配置</el-tag>
</template>
</el-table-column>
<el-table-column label="创建时间" prop="created_at" min-width="160">
<template #default="{ row }">
{{ formatDateTime(row.created_at) }}
</template>
</el-table-column>
<el-table-column label="操作" width="220" fixed="right">
<el-table-column label="操作" width="280" fixed="right">
<template #default="{ row }">
<el-button
type="primary"
@@ -267,6 +345,9 @@ onMounted(() => {
<el-button type="primary" link :icon="Clock" @click="handleOpenSettlement(row)">
结算设置
</el-button>
<el-button type="primary" link :icon="Picture" @click="handleOpenQrcode(row)">
二维码
</el-button>
</template>
</el-table-column>
</el-table>
@@ -440,6 +521,58 @@ onMounted(() => {
</el-button>
</template>
</el-dialog>
<!-- 发布群固定二维码 -->
<el-dialog v-model="qrDialogVisible" title="发布群固定二维码" width="520px" destroy-on-close>
<el-alert
type="info"
:closable="false"
show-icon
title="号主在该分站上传账号后,会把这张固定二维码发到对应发布群。同一分站可重复使用,不消耗主站二维码池。"
style="margin-bottom: 16px"
/>
<el-form label-width="110px">
<el-form-item label="分销站点">
<el-input :model-value="qrForm.site_name" disabled />
</el-form-item>
<el-form-item label="固定二维码">
<div class="qr-row">
<AuthImage
v-if="qrForm.listing_qrcode_url"
:source="qrForm.listing_qrcode_url"
fit="contain"
:image-style="{
width: '160px',
height: '160px',
borderRadius: '12px',
background: '#f7f9fc',
}"
:preview-src-list="[qrForm.listing_qrcode_url]"
/>
<div v-else class="qr-empty">尚未配置</div>
<div class="qr-actions">
<el-upload
:show-file-list="false"
accept="image/*"
:disabled="qrUploading"
:before-upload="uploadSiteQrcode"
>
<el-button :icon="Upload" :loading="qrUploading">上传二维码</el-button>
</el-upload>
<el-button :disabled="!qrForm.listing_qrcode_url" @click="clearSiteQrcode">
清空
</el-button>
</div>
</div>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="qrDialogVisible = false">取消</el-button>
<el-button type="primary" :loading="qrSubmitting" @click="handleQrcodeSubmit">
保存设置
</el-button>
</template>
</el-dialog>
</div>
</template>
@@ -508,4 +641,29 @@ export default {
color: #6b7280;
font-size: 13px;
}
.qr-row {
display: flex;
flex-wrap: wrap;
gap: 12px;
align-items: flex-start;
}
.qr-empty {
width: 160px;
height: 160px;
border-radius: 12px;
background: #f7f9fc;
color: #9ca3af;
display: flex;
align-items: center;
justify-content: center;
font-size: 13px;
}
.qr-actions {
display: flex;
flex-direction: column;
gap: 8px;
}
</style>