新增二维码池独立权限
This commit is contained in:
@@ -591,14 +591,14 @@ func New(cfg config.Config, deps Dependencies, logger *zap.Logger) *gin.Engine {
|
||||
adminRoutes.PUT("/chats/listing-group-welcome", requirePerm("system_config:update"), chatHandler.AdminUpdateListingGroupWelcome)
|
||||
|
||||
// 二维码池管理
|
||||
adminRoutes.POST("/chats/qrcodes", requirePerm("chat:manage"), chatHandler.CreateQrCodeHandler)
|
||||
adminRoutes.POST("/chats/qrcodes/batch", requirePerm("chat:manage"), chatHandler.BatchCreateQrCodeHandler)
|
||||
adminRoutes.POST("/chats/qrcodes/batch-delete", requirePerm("chat:manage"), chatHandler.BatchDeleteQrCodeHandler)
|
||||
adminRoutes.GET("/chats/qrcodes", requirePerm("chat:view"), chatHandler.ListQrCodesHandler)
|
||||
adminRoutes.GET("/chats/qrcodes/stats", requirePerm("chat:view"), chatHandler.GetQrCodeStatsHandler)
|
||||
adminRoutes.POST("/chats/qrcodes/ocr-group-name", requirePerm("chat:manage"), chatHandler.RecognizeQrCodeGroupNameHandler)
|
||||
adminRoutes.PATCH("/chats/qrcodes/:id", requirePerm("chat:manage"), chatHandler.UpdateQrCodeHandler)
|
||||
adminRoutes.DELETE("/chats/qrcodes/:id", requirePerm("chat:manage"), chatHandler.DeleteQrCodeHandler)
|
||||
adminRoutes.POST("/chats/qrcodes", requirePerm("qrcode:manage"), chatHandler.CreateQrCodeHandler)
|
||||
adminRoutes.POST("/chats/qrcodes/batch", requirePerm("qrcode:manage"), chatHandler.BatchCreateQrCodeHandler)
|
||||
adminRoutes.POST("/chats/qrcodes/batch-delete", requirePerm("qrcode:manage"), chatHandler.BatchDeleteQrCodeHandler)
|
||||
adminRoutes.GET("/chats/qrcodes", requirePerm("qrcode:view"), chatHandler.ListQrCodesHandler)
|
||||
adminRoutes.GET("/chats/qrcodes/stats", requirePerm("qrcode:view"), chatHandler.GetQrCodeStatsHandler)
|
||||
adminRoutes.POST("/chats/qrcodes/ocr-group-name", requirePerm("qrcode:manage"), chatHandler.RecognizeQrCodeGroupNameHandler)
|
||||
adminRoutes.PATCH("/chats/qrcodes/:id", requirePerm("qrcode:manage"), chatHandler.UpdateQrCodeHandler)
|
||||
adminRoutes.DELETE("/chats/qrcodes/:id", requirePerm("qrcode:manage"), chatHandler.DeleteQrCodeHandler)
|
||||
|
||||
// 客服分组
|
||||
adminRoutes.GET("/chat-support-groups", requirePerm("chat:manage"), supportGroupHandler.List)
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
-- +goose Up
|
||||
-- +goose StatementBegin
|
||||
|
||||
INSERT INTO permissions (code, name, resource, action) VALUES
|
||||
('qrcode:view', '查看二维码池', 'qrcode', 'view'),
|
||||
('qrcode:manage', '管理二维码池', 'qrcode', 'manage')
|
||||
ON DUPLICATE KEY UPDATE
|
||||
name = VALUES(name),
|
||||
resource = VALUES(resource),
|
||||
action = VALUES(action);
|
||||
|
||||
-- 超级管理员补齐新增权限。
|
||||
INSERT IGNORE INTO role_permissions (role_id, permission_id)
|
||||
SELECT r.id, p.id
|
||||
FROM roles r
|
||||
JOIN permissions p ON p.code IN ('qrcode:view', 'qrcode:manage')
|
||||
WHERE r.code = 'super_admin';
|
||||
|
||||
-- 客服默认可以查看和维护二维码池。
|
||||
INSERT IGNORE INTO role_permissions (role_id, permission_id)
|
||||
SELECT r.id, p.id
|
||||
FROM roles r
|
||||
JOIN permissions p ON p.code IN ('qrcode:view', 'qrcode:manage')
|
||||
WHERE r.code = 'cs';
|
||||
|
||||
-- 已拥有客服配置管理权限的角色,迁移后继续拥有二维码池管理能力。
|
||||
INSERT IGNORE INTO role_permissions (role_id, permission_id)
|
||||
SELECT DISTINCT rp.role_id, p.id
|
||||
FROM role_permissions rp
|
||||
JOIN permissions oldp ON oldp.id = rp.permission_id AND oldp.code = 'chat:manage'
|
||||
JOIN permissions p ON p.code IN ('qrcode:view', 'qrcode:manage');
|
||||
|
||||
-- 已拥有客服群聊查看权限的角色,迁移后继续能进入二维码池查看页。
|
||||
INSERT IGNORE INTO role_permissions (role_id, permission_id)
|
||||
SELECT DISTINCT rp.role_id, p.id
|
||||
FROM role_permissions rp
|
||||
JOIN permissions oldp ON oldp.id = rp.permission_id AND oldp.code = 'chat:view'
|
||||
JOIN permissions p ON p.code = 'qrcode:view';
|
||||
|
||||
-- +goose StatementEnd
|
||||
|
||||
-- +goose Down
|
||||
-- +goose StatementBegin
|
||||
|
||||
DELETE rp FROM role_permissions rp
|
||||
JOIN permissions p ON p.id = rp.permission_id
|
||||
WHERE p.code IN ('qrcode:view', 'qrcode:manage');
|
||||
|
||||
DELETE FROM permissions WHERE code IN ('qrcode:view', 'qrcode:manage');
|
||||
|
||||
-- +goose StatementEnd
|
||||
@@ -102,6 +102,7 @@ const resourceLabels: Record<string, string> = {
|
||||
listing: '商品管理',
|
||||
dispute: '仲裁中心',
|
||||
chat: '客服群聊',
|
||||
qrcode: '二维码池',
|
||||
wallet: '资金流水',
|
||||
withdrawal: '提现审核',
|
||||
payment_config: '支付配置',
|
||||
|
||||
@@ -29,10 +29,13 @@ import { formatDateTime } from '@/shared/utils/time'
|
||||
import AdminTablePagination from '../components/AdminTablePagination.vue'
|
||||
import AuthImage from '@/shared/components/business/AuthImage.vue'
|
||||
import { readError } from '@/shared/utils/error'
|
||||
import { useAdminSessionStore } from '@/stores/adminSession'
|
||||
|
||||
const adminSession = useAdminSessionStore()
|
||||
const loading = ref(false)
|
||||
const qrcodes = ref<ChatQrCode[]>([])
|
||||
const stats = ref<QrCodeStats>({ unused_count: 0, used_count: 0, disabled_count: 0, total_count: 0 })
|
||||
const canManageQrCodes = computed(() => adminSession.hasPermission('qrcode:manage'))
|
||||
|
||||
const currentPage = ref(1)
|
||||
const currentPageSize = ref(20)
|
||||
@@ -239,6 +242,7 @@ function handleUploadExceed() {
|
||||
}
|
||||
|
||||
function openUpload() {
|
||||
if (!canManageQrCodes.value) return
|
||||
clearUploadedImages()
|
||||
uploadForm.note = ''
|
||||
uploadForm.expires_at = ''
|
||||
@@ -246,6 +250,7 @@ function openUpload() {
|
||||
}
|
||||
|
||||
async function submitUpload() {
|
||||
if (!canManageQrCodes.value) return
|
||||
if (uploadPendingCount.value > 0) {
|
||||
ElMessage.warning('图片还在上传中,请稍后保存')
|
||||
return
|
||||
@@ -279,6 +284,7 @@ onBeforeUnmount(() => {
|
||||
})
|
||||
|
||||
function openEdit(row: ChatQrCode) {
|
||||
if (!canManageQrCodes.value) return
|
||||
editForm.id = row.id
|
||||
editForm.image_url = row.image_url
|
||||
editForm.group_name = row.group_name || ''
|
||||
@@ -295,6 +301,7 @@ function clearEditExpiresAt() {
|
||||
}
|
||||
|
||||
async function handleEditImageUpload(options: { file: File }) {
|
||||
if (!canManageQrCodes.value) return
|
||||
editUploading.value = true
|
||||
try {
|
||||
const uploaded = await uploadAdminFile(options.file, 'qrcode')
|
||||
@@ -308,6 +315,7 @@ async function handleEditImageUpload(options: { file: File }) {
|
||||
}
|
||||
|
||||
async function submitEdit() {
|
||||
if (!canManageQrCodes.value) return
|
||||
await updateQrCode(editForm.id, {
|
||||
image_url: editForm.image_url,
|
||||
group_name: editForm.group_name,
|
||||
@@ -356,6 +364,7 @@ function setRenameLoading(id: number, loading: boolean) {
|
||||
}
|
||||
|
||||
async function handleRenameFlagChange(row: ChatQrCode, value: string | number | boolean) {
|
||||
if (!canManageQrCodes.value) return
|
||||
const nextValue = Boolean(value)
|
||||
const previous = !nextValue
|
||||
row.wecom_renamed = nextValue
|
||||
@@ -372,6 +381,7 @@ async function handleRenameFlagChange(row: ChatQrCode, value: string | number |
|
||||
}
|
||||
|
||||
async function handleDisable(row: ChatQrCode) {
|
||||
if (!canManageQrCodes.value) return
|
||||
await ElMessageBox.confirm('确定停用该二维码?停用后不再发放给新发布群。', '停用确认', {
|
||||
type: 'warning',
|
||||
})
|
||||
@@ -381,10 +391,10 @@ async function handleDisable(row: ChatQrCode) {
|
||||
}
|
||||
|
||||
async function handleDelete(row: ChatQrCode) {
|
||||
const tip =
|
||||
qrcodeWasIssued(row)
|
||||
? '确定删除这条已发放二维码记录?删除后仅清理二维码池旧记录,不会影响已发送到群聊里的图片消息。'
|
||||
: '确定删除该二维码?删除后将从二维码池移除。'
|
||||
if (!canManageQrCodes.value) return
|
||||
const tip = qrcodeWasIssued(row)
|
||||
? '确定删除这条已发放二维码记录?删除后仅清理二维码池旧记录,不会影响已发送到群聊里的图片消息。'
|
||||
: '确定删除该二维码?删除后将从二维码池移除。'
|
||||
await ElMessageBox.confirm(tip, '删除确认', {
|
||||
type: 'warning',
|
||||
})
|
||||
@@ -394,6 +404,7 @@ async function handleDelete(row: ChatQrCode) {
|
||||
}
|
||||
|
||||
async function handleBatchDelete() {
|
||||
if (!canManageQrCodes.value) return
|
||||
if (!selectedCount.value) {
|
||||
ElMessage.warning('请先选择要删除的二维码')
|
||||
return
|
||||
@@ -460,6 +471,7 @@ onMounted(reloadAll)
|
||||
<div class="action-area">
|
||||
<span v-if="selectedCount" class="selection-summary">已选 {{ selectedCount }} 条</span>
|
||||
<el-button
|
||||
v-if="canManageQrCodes"
|
||||
type="danger"
|
||||
:icon="Delete"
|
||||
:disabled="!selectedCount"
|
||||
@@ -468,7 +480,9 @@ onMounted(reloadAll)
|
||||
批量删除
|
||||
</el-button>
|
||||
<el-button :icon="Refresh" @click="reloadAll">刷新</el-button>
|
||||
<el-button type="primary" :icon="Plus" @click="openUpload">添加二维码</el-button>
|
||||
<el-button v-if="canManageQrCodes" type="primary" :icon="Plus" @click="openUpload">
|
||||
添加二维码
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
@@ -483,7 +497,12 @@ onMounted(reloadAll)
|
||||
row-key="id"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" class-name="selection-col" align="center" />
|
||||
<el-table-column
|
||||
v-if="canManageQrCodes"
|
||||
type="selection"
|
||||
class-name="selection-col"
|
||||
align="center"
|
||||
/>
|
||||
<el-table-column label="二维码" class-name="qr-col" align="center">
|
||||
<template #default="{ row }">
|
||||
<div class="qrcode-thumb">
|
||||
@@ -539,6 +558,7 @@ onMounted(reloadAll)
|
||||
<el-switch
|
||||
:model-value="row.wecom_renamed"
|
||||
:loading="renameLoadingIds.has(row.id)"
|
||||
:disabled="!canManageQrCodes"
|
||||
inline-prompt
|
||||
:active-icon="Check"
|
||||
active-text="已改"
|
||||
@@ -560,7 +580,12 @@ onMounted(reloadAll)
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" class-name="action-col" align="center">
|
||||
<el-table-column
|
||||
v-if="canManageQrCodes"
|
||||
label="操作"
|
||||
class-name="action-col"
|
||||
align="center"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<div class="table-actions">
|
||||
<el-button text type="primary" size="small" @click="openEdit(row)">编辑</el-button>
|
||||
@@ -572,13 +597,7 @@ onMounted(reloadAll)
|
||||
@click="handleDisable(row)"
|
||||
>停用</el-button
|
||||
>
|
||||
<el-button
|
||||
text
|
||||
type="danger"
|
||||
size="small"
|
||||
@click="handleDelete(row)"
|
||||
>删除</el-button
|
||||
>
|
||||
<el-button text type="danger" size="small" @click="handleDelete(row)">删除</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -594,7 +613,12 @@ onMounted(reloadAll)
|
||||
</el-card>
|
||||
|
||||
<!-- 上传弹窗 -->
|
||||
<el-dialog v-model="uploadVisible" title="批量添加企业微信群二维码" width="680px" @close="clearUploadedImages">
|
||||
<el-dialog
|
||||
v-model="uploadVisible"
|
||||
title="批量添加企业微信群二维码"
|
||||
width="680px"
|
||||
@close="clearUploadedImages"
|
||||
>
|
||||
<el-form class="qrcode-upload-form" label-width="108px">
|
||||
<el-form-item label="二维码图片" required>
|
||||
<div class="batch-upload-area">
|
||||
|
||||
@@ -104,7 +104,7 @@ const allNavGroups: NavGroup[] = [
|
||||
permission: 'dispute:view',
|
||||
},
|
||||
{ label: '客服群聊', to: adminPath('chats'), icon: ChatDotRound, permission: 'chat:view' },
|
||||
{ label: '二维码池', to: adminPath('qrcode-pool'), icon: Picture, permission: 'chat:view' },
|
||||
{ label: '二维码池', to: adminPath('qrcode-pool'), icon: Picture, permission: 'qrcode:view' },
|
||||
{ label: '客服分组', to: adminPath('support-groups'), icon: User, permission: 'chat:manage' },
|
||||
],
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user