优化删除
This commit is contained in:
@@ -44,6 +44,23 @@ func (h *Handler) BatchCreateQrCodeHandler(c *gin.Context) {
|
|||||||
c.JSON(http.StatusOK, gin.H{"data": qrcodes})
|
c.JSON(http.StatusOK, gin.H{"data": qrcodes})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BatchDeleteQrCodeHandler 批量删除二维码
|
||||||
|
func (h *Handler) BatchDeleteQrCodeHandler(c *gin.Context) {
|
||||||
|
var req BatchDeleteQrCodeRequest
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
deleted, err := h.service.repo.BatchDeleteQrCodes(c.Request.Context(), req.IDs)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, gin.H{"data": gin.H{"deleted": deleted}})
|
||||||
|
}
|
||||||
|
|
||||||
// ListQrCodesHandler 列表查询二维码
|
// ListQrCodesHandler 列表查询二维码
|
||||||
func (h *Handler) ListQrCodesHandler(c *gin.Context) {
|
func (h *Handler) ListQrCodesHandler(c *gin.Context) {
|
||||||
var req QrCodeListRequest
|
var req QrCodeListRequest
|
||||||
@@ -157,10 +174,6 @@ func (h *Handler) DeleteQrCodeHandler(c *gin.Context) {
|
|||||||
c.JSON(http.StatusNotFound, gin.H{"error": "二维码不存在"})
|
c.JSON(http.StatusNotFound, gin.H{"error": "二维码不存在"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err == ErrQrCodeCannotDelete {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "已使用的二维码不能删除"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ const (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
ErrQrCodeNotFound = errors.New("二维码不存在")
|
ErrQrCodeNotFound = errors.New("二维码不存在")
|
||||||
ErrQrCodeCannotDelete = errors.New("已使用的二维码不能删除")
|
|
||||||
ErrQrCodeCannotReenable = errors.New("已发放的二维码不能改回待用")
|
ErrQrCodeCannotReenable = errors.New("已发放的二维码不能改回待用")
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -43,6 +42,11 @@ type BatchCreateQrCodeRequest struct {
|
|||||||
Items []CreateQrCodeRequest `json:"items" binding:"required,min=1,max=20"`
|
Items []CreateQrCodeRequest `json:"items" binding:"required,min=1,max=20"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BatchDeleteQrCodeRequest 批量删除二维码请求
|
||||||
|
type BatchDeleteQrCodeRequest struct {
|
||||||
|
IDs []uint64 `json:"ids" binding:"required,min=1,max=100"`
|
||||||
|
}
|
||||||
|
|
||||||
// UpdateQrCodeRequest 更新二维码请求
|
// UpdateQrCodeRequest 更新二维码请求
|
||||||
type UpdateQrCodeRequest struct {
|
type UpdateQrCodeRequest struct {
|
||||||
ImageURL *string `json:"image_url"`
|
ImageURL *string `json:"image_url"`
|
||||||
@@ -313,7 +317,7 @@ func qrcodeWasIssued(qrcode model.ChatQrCode) bool {
|
|||||||
return qrcode.Status == QrCodeStatusUsed || qrcode.ConversationID != nil || qrcode.UsedAt != nil
|
return qrcode.Status == QrCodeStatusUsed || qrcode.ConversationID != nil || qrcode.UsedAt != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteQrCode 删除二维码(仅未使用的可删除)
|
// DeleteQrCode 删除二维码
|
||||||
func (r *Repository) DeleteQrCode(ctx context.Context, id uint64) error {
|
func (r *Repository) DeleteQrCode(ctx context.Context, id uint64) error {
|
||||||
var qrcode model.ChatQrCode
|
var qrcode model.ChatQrCode
|
||||||
if err := r.db.WithContext(ctx).First(&qrcode, id).Error; err != nil {
|
if err := r.db.WithContext(ctx).First(&qrcode, id).Error; err != nil {
|
||||||
@@ -323,12 +327,20 @@ func (r *Repository) DeleteQrCode(ctx context.Context, id uint64) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 已使用的不能删除
|
return r.db.WithContext(ctx).Delete(&qrcode).Error
|
||||||
if qrcode.Status == QrCodeStatusUsed {
|
|
||||||
return ErrQrCodeCannotDelete
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return r.db.WithContext(ctx).Delete(&qrcode).Error
|
// BatchDeleteQrCodes 批量删除二维码
|
||||||
|
func (r *Repository) BatchDeleteQrCodes(ctx context.Context, ids []uint64) (int64, error) {
|
||||||
|
if len(ids) == 0 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
result := r.db.WithContext(ctx).Where("id IN ?", ids).Delete(&model.ChatQrCode{})
|
||||||
|
if result.Error != nil {
|
||||||
|
return 0, result.Error
|
||||||
|
}
|
||||||
|
return result.RowsAffected, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// fetchUnusedQrCode 获取一个未使用且未过期的二维码(带行锁)
|
// fetchUnusedQrCode 获取一个未使用且未过期的二维码(带行锁)
|
||||||
|
|||||||
@@ -231,6 +231,80 @@ func TestUpdateQrCodeGroupNameAndRenameFlag(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDeleteQrCodeAllowsIssuedQrCode(t *testing.T) {
|
||||||
|
db := setupQrCodeTestDB(t)
|
||||||
|
repo := NewRepository(db, nil)
|
||||||
|
now := time.Date(2026, 6, 19, 17, 8, 0, 0, time.UTC)
|
||||||
|
conversationID := uint64(1003)
|
||||||
|
qrcode := model.ChatQrCode{
|
||||||
|
ImageURL: "/api/files/object?key=qrcode/issued.png",
|
||||||
|
Status: QrCodeStatusUsed,
|
||||||
|
ConversationID: &conversationID,
|
||||||
|
UsedAt: &now,
|
||||||
|
CreatedBy: 1,
|
||||||
|
}
|
||||||
|
if err := db.Create(&qrcode).Error; err != nil {
|
||||||
|
t.Fatalf("创建二维码失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := repo.DeleteQrCode(t.Context(), qrcode.ID); err != nil {
|
||||||
|
t.Fatalf("删除已发放二维码失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var count int64
|
||||||
|
if err := db.Model(&model.ChatQrCode{}).Where("id = ?", qrcode.ID).Count(&count).Error; err != nil {
|
||||||
|
t.Fatalf("统计二维码失败: %v", err)
|
||||||
|
}
|
||||||
|
if count != 0 {
|
||||||
|
t.Fatalf("二维码数量 = %d, want 0", count)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBatchDeleteQrCodes(t *testing.T) {
|
||||||
|
db := setupQrCodeTestDB(t)
|
||||||
|
repo := NewRepository(db, nil)
|
||||||
|
now := time.Date(2026, 6, 19, 17, 8, 0, 0, time.UTC)
|
||||||
|
conversationID := uint64(1004)
|
||||||
|
qrcodes := []model.ChatQrCode{
|
||||||
|
{
|
||||||
|
ImageURL: "/api/files/object?key=qrcode/unused.png",
|
||||||
|
Status: QrCodeStatusUnused,
|
||||||
|
CreatedBy: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ImageURL: "/api/files/object?key=qrcode/used.png",
|
||||||
|
Status: QrCodeStatusUsed,
|
||||||
|
ConversationID: &conversationID,
|
||||||
|
UsedAt: &now,
|
||||||
|
CreatedBy: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ImageURL: "/api/files/object?key=qrcode/keep.png",
|
||||||
|
Status: QrCodeStatusDisabled,
|
||||||
|
CreatedBy: 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if err := db.Create(&qrcodes).Error; err != nil {
|
||||||
|
t.Fatalf("创建二维码失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
deleted, err := repo.BatchDeleteQrCodes(t.Context(), []uint64{qrcodes[0].ID, qrcodes[1].ID})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("批量删除二维码失败: %v", err)
|
||||||
|
}
|
||||||
|
if deleted != 2 {
|
||||||
|
t.Fatalf("删除数量 = %d, want 2", deleted)
|
||||||
|
}
|
||||||
|
|
||||||
|
var remaining []model.ChatQrCode
|
||||||
|
if err := db.Order("id ASC").Find(&remaining).Error; err != nil {
|
||||||
|
t.Fatalf("查询剩余二维码失败: %v", err)
|
||||||
|
}
|
||||||
|
if len(remaining) != 1 || remaining[0].ID != qrcodes[2].ID {
|
||||||
|
t.Fatalf("剩余二维码 = %+v, want id %d", remaining, qrcodes[2].ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestEnsureListingConversationCreatesQrCodeDeliveryTaskWhenStockEmpty(t *testing.T) {
|
func TestEnsureListingConversationCreatesQrCodeDeliveryTaskWhenStockEmpty(t *testing.T) {
|
||||||
db := setupQrCodeTestDB(t)
|
db := setupQrCodeTestDB(t)
|
||||||
listing := model.RentalListing{
|
listing := model.RentalListing{
|
||||||
|
|||||||
@@ -593,6 +593,7 @@ func New(cfg config.Config, deps Dependencies, logger *zap.Logger) *gin.Engine {
|
|||||||
// 二维码池管理
|
// 二维码池管理
|
||||||
adminRoutes.POST("/chats/qrcodes", requirePerm("chat:manage"), chatHandler.CreateQrCodeHandler)
|
adminRoutes.POST("/chats/qrcodes", requirePerm("chat:manage"), chatHandler.CreateQrCodeHandler)
|
||||||
adminRoutes.POST("/chats/qrcodes/batch", requirePerm("chat:manage"), chatHandler.BatchCreateQrCodeHandler)
|
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", requirePerm("chat:view"), chatHandler.ListQrCodesHandler)
|
||||||
adminRoutes.GET("/chats/qrcodes/stats", requirePerm("chat:view"), chatHandler.GetQrCodeStatsHandler)
|
adminRoutes.GET("/chats/qrcodes/stats", requirePerm("chat:view"), chatHandler.GetQrCodeStatsHandler)
|
||||||
adminRoutes.POST("/chats/qrcodes/ocr-group-name", requirePerm("chat:manage"), chatHandler.RecognizeQrCodeGroupNameHandler)
|
adminRoutes.POST("/chats/qrcodes/ocr-group-name", requirePerm("chat:manage"), chatHandler.RecognizeQrCodeGroupNameHandler)
|
||||||
|
|||||||
@@ -55,6 +55,10 @@ export interface UpdateQrCodePayload {
|
|||||||
clear_expires_at?: boolean
|
clear_expires_at?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface BatchDeleteQrCodeResult {
|
||||||
|
deleted: number
|
||||||
|
}
|
||||||
|
|
||||||
export async function fetchQrCodes(query: QrCodeListQuery = {}) {
|
export async function fetchQrCodes(query: QrCodeListQuery = {}) {
|
||||||
const { data } = await apiClient.get<ApiResponse<PaginatedResult<ChatQrCode>>>(
|
const { data } = await apiClient.get<ApiResponse<PaginatedResult<ChatQrCode>>>(
|
||||||
'/admin/chats/qrcodes',
|
'/admin/chats/qrcodes',
|
||||||
@@ -111,3 +115,11 @@ export async function deleteQrCode(id: number) {
|
|||||||
const { data } = await apiClient.delete<ApiResponse<null>>(`/admin/chats/qrcodes/${id}`)
|
const { data } = await apiClient.delete<ApiResponse<null>>(`/admin/chats/qrcodes/${id}`)
|
||||||
return data.data
|
return data.data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function batchDeleteQrCodes(ids: number[]) {
|
||||||
|
const { data } = await apiClient.post<ApiResponse<BatchDeleteQrCodeResult>>(
|
||||||
|
'/admin/chats/qrcodes/batch-delete',
|
||||||
|
{ ids }
|
||||||
|
)
|
||||||
|
return data.data
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,10 +1,19 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, onBeforeUnmount, onMounted, reactive, ref } from 'vue'
|
import { computed, onBeforeUnmount, onMounted, reactive, ref } from 'vue'
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
import { Check, CopyDocument, Plus, Refresh, Search, UploadFilled } from '@element-plus/icons-vue'
|
import {
|
||||||
|
Check,
|
||||||
|
CopyDocument,
|
||||||
|
Delete,
|
||||||
|
Plus,
|
||||||
|
Refresh,
|
||||||
|
Search,
|
||||||
|
UploadFilled,
|
||||||
|
} from '@element-plus/icons-vue'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
batchCreateQrCodes,
|
batchCreateQrCodes,
|
||||||
|
batchDeleteQrCodes,
|
||||||
deleteQrCode,
|
deleteQrCode,
|
||||||
fetchQrCodeStats,
|
fetchQrCodeStats,
|
||||||
fetchQrCodes,
|
fetchQrCodes,
|
||||||
@@ -88,6 +97,9 @@ const uploading = computed(() => uploadPendingCount.value > 0)
|
|||||||
const uploadBusy = computed(() => uploading.value || uploadSaving.value)
|
const uploadBusy = computed(() => uploading.value || uploadSaving.value)
|
||||||
const uploadedImageCountText = computed(() => `${uploadedImages.value.length}/${maxBatchUploadCount}`)
|
const uploadedImageCountText = computed(() => `${uploadedImages.value.length}/${maxBatchUploadCount}`)
|
||||||
const renameLoadingIds = ref(new Set<number>())
|
const renameLoadingIds = ref(new Set<number>())
|
||||||
|
const selectedRows = ref<ChatQrCode[]>([])
|
||||||
|
const selectedCount = computed(() => selectedRows.value.length)
|
||||||
|
const selectedIssuedCount = computed(() => selectedRows.value.filter(qrcodeWasIssued).length)
|
||||||
|
|
||||||
async function loadList() {
|
async function loadList() {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
@@ -99,6 +111,7 @@ async function loadList() {
|
|||||||
})
|
})
|
||||||
qrcodes.value = res.items
|
qrcodes.value = res.items
|
||||||
total.value = res.total
|
total.value = res.total
|
||||||
|
selectedRows.value = []
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
@@ -121,6 +134,10 @@ function handlePageChange() {
|
|||||||
loadList()
|
loadList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleSelectionChange(rows: ChatQrCode[]) {
|
||||||
|
selectedRows.value = rows
|
||||||
|
}
|
||||||
|
|
||||||
function parseGroupNameFromOcrText(text: string) {
|
function parseGroupNameFromOcrText(text: string) {
|
||||||
const lines = text
|
const lines = text
|
||||||
.split(/\r?\n/)
|
.split(/\r?\n/)
|
||||||
@@ -324,6 +341,10 @@ function boundConversationLabel(row: ChatQrCode) {
|
|||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function qrcodeWasIssued(row: ChatQrCode) {
|
||||||
|
return row.status === 'used' || Boolean(row.conversation_id || row.used_at)
|
||||||
|
}
|
||||||
|
|
||||||
function setRenameLoading(id: number, loading: boolean) {
|
function setRenameLoading(id: number, loading: boolean) {
|
||||||
const next = new Set(renameLoadingIds.value)
|
const next = new Set(renameLoadingIds.value)
|
||||||
if (loading) {
|
if (loading) {
|
||||||
@@ -360,7 +381,11 @@ async function handleDisable(row: ChatQrCode) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function handleDelete(row: ChatQrCode) {
|
async function handleDelete(row: ChatQrCode) {
|
||||||
await ElMessageBox.confirm('确定删除该二维码?仅未使用的二维码可删除。', '删除确认', {
|
const tip =
|
||||||
|
qrcodeWasIssued(row)
|
||||||
|
? '确定删除这条已发放二维码记录?删除后仅清理二维码池旧记录,不会影响已发送到群聊里的图片消息。'
|
||||||
|
: '确定删除该二维码?删除后将从二维码池移除。'
|
||||||
|
await ElMessageBox.confirm(tip, '删除确认', {
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
})
|
})
|
||||||
await deleteQrCode(row.id)
|
await deleteQrCode(row.id)
|
||||||
@@ -368,6 +393,25 @@ async function handleDelete(row: ChatQrCode) {
|
|||||||
await reloadAll()
|
await reloadAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleBatchDelete() {
|
||||||
|
if (!selectedCount.value) {
|
||||||
|
ElMessage.warning('请先选择要删除的二维码')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const tip = selectedIssuedCount.value
|
||||||
|
? `确定删除选中的 ${selectedCount.value} 条二维码记录?其中 ${selectedIssuedCount.value} 条已发放,删除后仅清理二维码池旧记录,不会影响已发送到群聊里的图片消息。`
|
||||||
|
: `确定删除选中的 ${selectedCount.value} 条二维码?删除后将从二维码池移除。`
|
||||||
|
await ElMessageBox.confirm(tip, '批量删除确认', {
|
||||||
|
type: 'warning',
|
||||||
|
})
|
||||||
|
|
||||||
|
const result = await batchDeleteQrCodes(selectedRows.value.map(row => row.id))
|
||||||
|
selectedRows.value = []
|
||||||
|
ElMessage.success(`已删除 ${result.deleted} 条`)
|
||||||
|
await reloadAll()
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(reloadAll)
|
onMounted(reloadAll)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -414,6 +458,15 @@ onMounted(reloadAll)
|
|||||||
<el-button :icon="Search" @click="handleFilter">筛选</el-button>
|
<el-button :icon="Search" @click="handleFilter">筛选</el-button>
|
||||||
</div>
|
</div>
|
||||||
<div class="action-area">
|
<div class="action-area">
|
||||||
|
<span v-if="selectedCount" class="selection-summary">已选 {{ selectedCount }} 条</span>
|
||||||
|
<el-button
|
||||||
|
type="danger"
|
||||||
|
:icon="Delete"
|
||||||
|
:disabled="!selectedCount"
|
||||||
|
@click="handleBatchDelete"
|
||||||
|
>
|
||||||
|
批量删除
|
||||||
|
</el-button>
|
||||||
<el-button :icon="Refresh" @click="reloadAll">刷新</el-button>
|
<el-button :icon="Refresh" @click="reloadAll">刷新</el-button>
|
||||||
<el-button type="primary" :icon="Plus" @click="openUpload">添加二维码</el-button>
|
<el-button type="primary" :icon="Plus" @click="openUpload">添加二维码</el-button>
|
||||||
</div>
|
</div>
|
||||||
@@ -422,7 +475,15 @@ onMounted(reloadAll)
|
|||||||
|
|
||||||
<!-- 列表 -->
|
<!-- 列表 -->
|
||||||
<el-card shadow="never" class="table-card">
|
<el-card shadow="never" class="table-card">
|
||||||
<el-table v-loading="loading" :data="qrcodes" class="table-panel qrcode-table" :fit="true">
|
<el-table
|
||||||
|
v-loading="loading"
|
||||||
|
:data="qrcodes"
|
||||||
|
class="table-panel qrcode-table"
|
||||||
|
:fit="true"
|
||||||
|
row-key="id"
|
||||||
|
@selection-change="handleSelectionChange"
|
||||||
|
>
|
||||||
|
<el-table-column type="selection" class-name="selection-col" align="center" />
|
||||||
<el-table-column label="二维码" class-name="qr-col" align="center">
|
<el-table-column label="二维码" class-name="qr-col" align="center">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<div class="qrcode-thumb">
|
<div class="qrcode-thumb">
|
||||||
@@ -512,7 +573,6 @@ onMounted(reloadAll)
|
|||||||
>停用</el-button
|
>停用</el-button
|
||||||
>
|
>
|
||||||
<el-button
|
<el-button
|
||||||
v-if="row.status === 'unused'"
|
|
||||||
text
|
text
|
||||||
type="danger"
|
type="danger"
|
||||||
size="small"
|
size="small"
|
||||||
@@ -798,6 +858,10 @@ onMounted(reloadAll)
|
|||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.qrcode-table :deep(.selection-col) {
|
||||||
|
width: 4%;
|
||||||
|
}
|
||||||
|
|
||||||
.qrcode-table :deep(.qr-col) {
|
.qrcode-table :deep(.qr-col) {
|
||||||
width: 8%;
|
width: 8%;
|
||||||
}
|
}
|
||||||
@@ -807,15 +871,15 @@ onMounted(reloadAll)
|
|||||||
}
|
}
|
||||||
|
|
||||||
.qrcode-table :deep(.name-col) {
|
.qrcode-table :deep(.name-col) {
|
||||||
width: 18%;
|
width: 17%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.qrcode-table :deep(.note-col) {
|
.qrcode-table :deep(.note-col) {
|
||||||
width: 18%;
|
width: 17%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.qrcode-table :deep(.group-col) {
|
.qrcode-table :deep(.group-col) {
|
||||||
width: 18%;
|
width: 17%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.qrcode-table :deep(.rename-col) {
|
.qrcode-table :deep(.rename-col) {
|
||||||
|
|||||||
Reference in New Issue
Block a user