修复二维码库存与过期时间编辑
This commit is contained in:
@@ -58,11 +58,11 @@ func (h *Handler) ListQrCodesHandler(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
"data": qrcodes,
|
"data": PaginatedResult{
|
||||||
"pagination": gin.H{
|
Items: qrcodes,
|
||||||
"total": total,
|
Total: total,
|
||||||
"page": req.Page,
|
Page: req.Page,
|
||||||
"limit": req.Limit,
|
PageSize: req.Limit,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ import (
|
|||||||
|
|
||||||
// 会话类型常量
|
// 会话类型常量
|
||||||
const (
|
const (
|
||||||
ConversationTypeOrderGroup = "order_group"
|
ConversationTypeOrderGroup = "order_group"
|
||||||
ConversationTypeListingGroup = "listing_group"
|
ConversationTypeListingGroup = "listing_group"
|
||||||
ConversationTypeGeneralSupport = "general_support"
|
ConversationTypeGeneralSupport = "general_support"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -297,6 +297,7 @@ func checkQrCodeStockAndAlert(tx *gorm.DB, conversation *model.ChatConversation)
|
|||||||
var count int64
|
var count int64
|
||||||
if err := tx.Model(&model.ChatQrCode{}).
|
if err := tx.Model(&model.ChatQrCode{}).
|
||||||
Where("status = ?", QrCodeStatusUnused).
|
Where("status = ?", QrCodeStatusUnused).
|
||||||
|
Where("expires_at IS NULL OR expires_at > ?", time.Now()).
|
||||||
Count(&count).Error; err != nil {
|
Count(&count).Error; err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -319,6 +320,7 @@ func checkQrCodeStockAndAlert(tx *gorm.DB, conversation *model.ChatConversation)
|
|||||||
|
|
||||||
// 发送站内信给所有客服
|
// 发送站内信给所有客服
|
||||||
var entries []interface{}
|
var entries []interface{}
|
||||||
|
now := time.Now()
|
||||||
for _, admin := range csAdmins {
|
for _, admin := range csAdmins {
|
||||||
entries = append(entries, map[string]interface{}{
|
entries = append(entries, map[string]interface{}{
|
||||||
"admin_user_id": admin.ID,
|
"admin_user_id": admin.ID,
|
||||||
@@ -326,13 +328,13 @@ func checkQrCodeStockAndAlert(tx *gorm.DB, conversation *model.ChatConversation)
|
|||||||
"title": "二维码库存预警",
|
"title": "二维码库存预警",
|
||||||
"content": alertContent,
|
"content": alertContent,
|
||||||
"is_read": false,
|
"is_read": false,
|
||||||
"created_at": time.Now(),
|
"created_at": now,
|
||||||
"updated_at": time.Now(),
|
"updated_at": now,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(entries) > 0 {
|
if len(entries) > 0 {
|
||||||
// 批量插入通知
|
// 批量插入管理员通知
|
||||||
if err := tx.Table("admin_notifications").Create(entries).Error; err != nil {
|
if err := tx.Table("admin_notifications").Create(entries).Error; err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,9 +36,11 @@ type BatchCreateQrCodeRequest struct {
|
|||||||
|
|
||||||
// UpdateQrCodeRequest 更新二维码请求
|
// UpdateQrCodeRequest 更新二维码请求
|
||||||
type UpdateQrCodeRequest struct {
|
type UpdateQrCodeRequest struct {
|
||||||
Note *string `json:"note"`
|
ImageURL *string `json:"image_url"`
|
||||||
Status *string `json:"status"`
|
Note *string `json:"note"`
|
||||||
ExpiresAt *time.Time `json:"expires_at"`
|
Status *string `json:"status"`
|
||||||
|
ExpiresAt *time.Time `json:"expires_at"`
|
||||||
|
ClearExpiresAt bool `json:"clear_expires_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// QrCodeListRequest 列表查询请求
|
// QrCodeListRequest 列表查询请求
|
||||||
@@ -140,32 +142,29 @@ func (r *Repository) ListQrCodes(ctx context.Context, req QrCodeListRequest) ([]
|
|||||||
// GetQrCodeStats 获取二维码统计信息
|
// GetQrCodeStats 获取二维码统计信息
|
||||||
func (r *Repository) GetQrCodeStats(ctx context.Context) (*QrCodeStats, error) {
|
func (r *Repository) GetQrCodeStats(ctx context.Context) (*QrCodeStats, error) {
|
||||||
stats := &QrCodeStats{}
|
stats := &QrCodeStats{}
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
// 统计各状态数量
|
if err := r.db.WithContext(ctx).Model(&model.ChatQrCode{}).Count(&stats.TotalCount).Error; err != nil {
|
||||||
type CountResult struct {
|
|
||||||
Status string
|
|
||||||
Count int64
|
|
||||||
}
|
|
||||||
var results []CountResult
|
|
||||||
|
|
||||||
if err := r.db.WithContext(ctx).
|
|
||||||
Model(&model.ChatQrCode{}).
|
|
||||||
Select("status, COUNT(*) as count").
|
|
||||||
Group("status").
|
|
||||||
Find(&results).Error; err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if err := r.db.WithContext(ctx).
|
||||||
for _, r := range results {
|
Model(&model.ChatQrCode{}).
|
||||||
stats.TotalCount += r.Count
|
Where("status = ?", QrCodeStatusUsed).
|
||||||
switch r.Status {
|
Count(&stats.UsedCount).Error; err != nil {
|
||||||
case QrCodeStatusUnused:
|
return nil, err
|
||||||
stats.UnusedCount = r.Count
|
}
|
||||||
case QrCodeStatusUsed:
|
if err := r.db.WithContext(ctx).
|
||||||
stats.UsedCount = r.Count
|
Model(&model.ChatQrCode{}).
|
||||||
case QrCodeStatusDisabled:
|
Where("status = ?", QrCodeStatusDisabled).
|
||||||
stats.DisabledCount = r.Count
|
Count(&stats.DisabledCount).Error; err != nil {
|
||||||
}
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := r.db.WithContext(ctx).
|
||||||
|
Model(&model.ChatQrCode{}).
|
||||||
|
Where("status = ?", QrCodeStatusUnused).
|
||||||
|
Where("expires_at IS NULL OR expires_at > ?", now).
|
||||||
|
Count(&stats.UnusedCount).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return stats, nil
|
return stats, nil
|
||||||
@@ -175,6 +174,9 @@ func (r *Repository) GetQrCodeStats(ctx context.Context) (*QrCodeStats, error) {
|
|||||||
func (r *Repository) UpdateQrCode(ctx context.Context, id uint64, req UpdateQrCodeRequest) error {
|
func (r *Repository) UpdateQrCode(ctx context.Context, id uint64, req UpdateQrCodeRequest) error {
|
||||||
updates := make(map[string]interface{})
|
updates := make(map[string]interface{})
|
||||||
|
|
||||||
|
if req.ImageURL != nil {
|
||||||
|
updates["image_url"] = *req.ImageURL
|
||||||
|
}
|
||||||
if req.Note != nil {
|
if req.Note != nil {
|
||||||
updates["note"] = *req.Note
|
updates["note"] = *req.Note
|
||||||
}
|
}
|
||||||
@@ -187,6 +189,8 @@ func (r *Repository) UpdateQrCode(ctx context.Context, id uint64, req UpdateQrCo
|
|||||||
}
|
}
|
||||||
if req.ExpiresAt != nil {
|
if req.ExpiresAt != nil {
|
||||||
updates["expires_at"] = req.ExpiresAt
|
updates["expires_at"] = req.ExpiresAt
|
||||||
|
} else if req.ClearExpiresAt {
|
||||||
|
updates["expires_at"] = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(updates) == 0 {
|
if len(updates) == 0 {
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ func TestRepositoryUpdateAllowsOfflineListingResubmit(t *testing.T) {
|
|||||||
if err := db.AutoMigrate(&model.GameAccount{}, &model.RentalListing{}); err != nil {
|
if err := db.AutoMigrate(&model.GameAccount{}, &model.RentalListing{}); err != nil {
|
||||||
t.Fatalf("failed to migrate test db: %v", err)
|
t.Fatalf("failed to migrate test db: %v", err)
|
||||||
}
|
}
|
||||||
repo := NewRepository(db)
|
repo := NewRepository(db, nil)
|
||||||
req := CreateRequest{
|
req := CreateRequest{
|
||||||
Title: "测试账号",
|
Title: "测试账号",
|
||||||
ServerRegion: "QQ",
|
ServerRegion: "QQ",
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
-- +goose Up
|
||||||
|
-- +goose StatementBegin
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS admin_notifications (
|
||||||
|
id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
admin_user_id BIGINT UNSIGNED NOT NULL COMMENT '接收管理员ID',
|
||||||
|
type VARCHAR(32) NOT NULL COMMENT '通知类型: system系统, chat聊天等',
|
||||||
|
title VARCHAR(128) NOT NULL COMMENT '通知标题',
|
||||||
|
content TEXT NULL COMMENT '通知内容',
|
||||||
|
is_read TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否已读',
|
||||||
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
|
KEY idx_admin_notifications_user_read_created (admin_user_id, is_read, created_at)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='管理员通知消息表';
|
||||||
|
|
||||||
|
-- +goose StatementEnd
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
-- +goose StatementBegin
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS admin_notifications;
|
||||||
|
|
||||||
|
-- +goose StatementEnd
|
||||||
@@ -36,9 +36,11 @@ export interface CreateQrCodePayload {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateQrCodePayload {
|
export interface UpdateQrCodePayload {
|
||||||
|
image_url?: string
|
||||||
note?: string
|
note?: string
|
||||||
status?: QrCodeStatus
|
status?: QrCodeStatus
|
||||||
expires_at?: string | null
|
expires_at?: string | null
|
||||||
|
clear_expires_at?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchQrCodes(query: QrCodeListQuery = {}) {
|
export async function fetchQrCodes(query: QrCodeListQuery = {}) {
|
||||||
@@ -49,17 +51,7 @@ export async function fetchQrCodes(query: QrCodeListQuery = {}) {
|
|||||||
silent: true,
|
silent: true,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
// 后端返回 { data: [...], pagination: { total, page, limit } },适配为 PaginatedResult
|
return data.data
|
||||||
const resp = data.data as unknown as {
|
|
||||||
data: ChatQrCode[]
|
|
||||||
pagination: { total: number; page: number; limit: number }
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
items: resp.data,
|
|
||||||
total: resp.pagination.total,
|
|
||||||
page: resp.pagination.page,
|
|
||||||
page_size: resp.pagination.limit,
|
|
||||||
} as PaginatedResult<ChatQrCode>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchQrCodeStats() {
|
export async function fetchQrCodeStats() {
|
||||||
|
|||||||
@@ -39,8 +39,10 @@ const uploadForm = reactive({
|
|||||||
|
|
||||||
// 编辑弹窗
|
// 编辑弹窗
|
||||||
const editVisible = ref(false)
|
const editVisible = ref(false)
|
||||||
|
const editUploading = ref(false)
|
||||||
const editForm = reactive({
|
const editForm = reactive({
|
||||||
id: 0,
|
id: 0,
|
||||||
|
image_url: '',
|
||||||
note: '',
|
note: '',
|
||||||
status: 'unused' as QrCodeStatus,
|
status: 'unused' as QrCodeStatus,
|
||||||
expires_at: '' as string,
|
expires_at: '' as string,
|
||||||
@@ -144,17 +146,37 @@ async function submitUpload() {
|
|||||||
|
|
||||||
function openEdit(row: ChatQrCode) {
|
function openEdit(row: ChatQrCode) {
|
||||||
editForm.id = row.id
|
editForm.id = row.id
|
||||||
|
editForm.image_url = row.image_url
|
||||||
editForm.note = row.note
|
editForm.note = row.note
|
||||||
editForm.status = row.status
|
editForm.status = row.status
|
||||||
editForm.expires_at = row.expires_at ? row.expires_at.replace('T', ' ').slice(0, 16) : ''
|
editForm.expires_at = row.expires_at ? row.expires_at.replace('T', ' ').slice(0, 16) : ''
|
||||||
editVisible.value = true
|
editVisible.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function clearEditExpiresAt() {
|
||||||
|
editForm.expires_at = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleEditImageUpload(options: { file: File }) {
|
||||||
|
editUploading.value = true
|
||||||
|
try {
|
||||||
|
const uploaded = await uploadAdminFile(options.file, 'qrcode')
|
||||||
|
editForm.image_url = uploaded.url
|
||||||
|
ElMessage.success('图片已上传')
|
||||||
|
} catch {
|
||||||
|
ElMessage.error('图片上传失败')
|
||||||
|
} finally {
|
||||||
|
editUploading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function submitEdit() {
|
async function submitEdit() {
|
||||||
await updateQrCode(editForm.id, {
|
await updateQrCode(editForm.id, {
|
||||||
|
image_url: editForm.image_url,
|
||||||
note: editForm.note,
|
note: editForm.note,
|
||||||
status: editForm.status,
|
status: editForm.status,
|
||||||
expires_at: editForm.expires_at ? new Date(editForm.expires_at).toISOString() : null,
|
expires_at: editForm.expires_at ? new Date(editForm.expires_at).toISOString() : undefined,
|
||||||
|
clear_expires_at: !editForm.expires_at,
|
||||||
})
|
})
|
||||||
ElMessage.success('已更新')
|
ElMessage.success('已更新')
|
||||||
editVisible.value = false
|
editVisible.value = false
|
||||||
@@ -233,61 +255,67 @@ 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">
|
<el-table v-loading="loading" :data="qrcodes" class="table-panel qrcode-table" :fit="true">
|
||||||
<el-table-column label="二维码" width="120">
|
<el-table-column label="二维码" class-name="qr-col" align="center">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<AuthImage
|
<div class="qrcode-thumb">
|
||||||
:source="row.image_url"
|
<AuthImage
|
||||||
fit="cover"
|
:source="row.image_url"
|
||||||
:image-style="{ width: '72px', height: '72px', borderRadius: '6px' }"
|
fit="cover"
|
||||||
:preview-src-list="[row.image_url]"
|
:image-style="{ width: '56px', height: '56px', borderRadius: '6px' }"
|
||||||
/>
|
:preview-src-list="[row.image_url]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="状态" width="100">
|
<el-table-column label="状态" class-name="status-col" align="center">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-tag :type="(statusMap[row.status]?.type as any) || 'info'">
|
<el-tag :type="(statusMap[row.status]?.type as any) || 'info'" size="small">
|
||||||
{{ statusMap[row.status]?.label || row.status }}
|
{{ statusMap[row.status]?.label || row.status }}
|
||||||
</el-tag>
|
</el-tag>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="备注" min-width="160" show-overflow-tooltip>
|
<el-table-column label="备注" class-name="note-col" show-overflow-tooltip>
|
||||||
<template #default="{ row }">{{ row.note || '-' }}</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="绑定发布群" width="120">
|
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
{{ row.conversation_id ? `#${row.conversation_id}` : '-' }}
|
<span class="note-cell">{{ row.note || '-' }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="过期时间" width="170">
|
<el-table-column label="绑定群" class-name="group-col" align="center">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<span :class="{ 'expired-tag': row.expires_at && new Date(row.expires_at) < new Date() }">
|
<span class="group-cell">{{ row.conversation_id ? `#${row.conversation_id}` : '-' }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="过期时间" class-name="expire-col" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span
|
||||||
|
class="expire-cell"
|
||||||
|
:class="{ 'expired-tag': row.expires_at && new Date(row.expires_at) < new Date() }"
|
||||||
|
>
|
||||||
{{ row.expires_at ? formatDateTime(row.expires_at) : '永久' }}
|
{{ row.expires_at ? formatDateTime(row.expires_at) : '永久' }}
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="创建时间" width="170">
|
<el-table-column label="操作" class-name="action-col" align="center">
|
||||||
<template #default="{ row }">{{ formatDateTime(row.created_at) }}</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="操作" width="200" fixed="right">
|
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-button link type="primary" size="small" @click="openEdit(row)">编辑</el-button>
|
<div class="table-actions">
|
||||||
<el-button
|
<el-button text type="primary" size="small" @click="openEdit(row)">编辑</el-button>
|
||||||
v-if="row.status !== 'disabled'"
|
<el-button
|
||||||
link
|
v-if="row.status !== 'disabled'"
|
||||||
type="warning"
|
text
|
||||||
size="small"
|
type="warning"
|
||||||
@click="handleDisable(row)"
|
size="small"
|
||||||
>停用</el-button
|
@click="handleDisable(row)"
|
||||||
>
|
>停用</el-button
|
||||||
<el-button
|
>
|
||||||
v-if="row.status === 'unused'"
|
<el-button
|
||||||
link
|
v-if="row.status === 'unused'"
|
||||||
type="danger"
|
text
|
||||||
size="small"
|
type="danger"
|
||||||
@click="handleDelete(row)"
|
size="small"
|
||||||
>删除</el-button
|
@click="handleDelete(row)"
|
||||||
>
|
>删除</el-button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
@@ -355,6 +383,27 @@ onMounted(reloadAll)
|
|||||||
<!-- 编辑弹窗 -->
|
<!-- 编辑弹窗 -->
|
||||||
<el-dialog v-model="editVisible" title="编辑二维码" width="440px">
|
<el-dialog v-model="editVisible" title="编辑二维码" width="440px">
|
||||||
<el-form label-width="90px">
|
<el-form label-width="90px">
|
||||||
|
<el-form-item label="二维码图片">
|
||||||
|
<div class="edit-image-area">
|
||||||
|
<AuthImage
|
||||||
|
v-if="editForm.image_url"
|
||||||
|
:source="editForm.image_url"
|
||||||
|
fit="cover"
|
||||||
|
:image-style="{ width: '96px', height: '96px', borderRadius: '6px' }"
|
||||||
|
/>
|
||||||
|
<el-upload
|
||||||
|
:auto-upload="true"
|
||||||
|
:show-file-list="false"
|
||||||
|
:http-request="(opt: any) => handleEditImageUpload(opt)"
|
||||||
|
accept="image/*"
|
||||||
|
:disabled="editUploading"
|
||||||
|
>
|
||||||
|
<el-button size="small" :loading="editUploading">
|
||||||
|
{{ editForm.image_url ? '替换图片' : '上传图片' }}
|
||||||
|
</el-button>
|
||||||
|
</el-upload>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item label="状态">
|
<el-form-item label="状态">
|
||||||
<el-select v-model="editForm.status" style="width: 100%">
|
<el-select v-model="editForm.status" style="width: 100%">
|
||||||
<el-option label="待用" value="unused" />
|
<el-option label="待用" value="unused" />
|
||||||
@@ -365,14 +414,17 @@ onMounted(reloadAll)
|
|||||||
<el-input v-model="editForm.note" placeholder="如:7月企微群A" maxlength="50" />
|
<el-input v-model="editForm.note" placeholder="如:7月企微群A" maxlength="50" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="过期时间">
|
<el-form-item label="过期时间">
|
||||||
<el-date-picker
|
<div class="expire-editor">
|
||||||
v-model="editForm.expires_at"
|
<el-date-picker
|
||||||
type="datetime"
|
v-model="editForm.expires_at"
|
||||||
placeholder="留空为永久"
|
type="datetime"
|
||||||
format="YYYY-MM-DD HH:mm"
|
placeholder="留空为永久"
|
||||||
value-format="YYYY-MM-DDTHH:mm"
|
format="YYYY-MM-DD HH:mm"
|
||||||
style="width: 100%"
|
value-format="YYYY-MM-DDTHH:mm"
|
||||||
/>
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
<el-button :disabled="!editForm.expires_at" @click="clearEditExpiresAt">清空</el-button>
|
||||||
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
@@ -400,6 +452,15 @@ onMounted(reloadAll)
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.toolbar-card,
|
||||||
|
.table-card {
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-card :deep(.el-card__body) {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
.stat-value {
|
.stat-value {
|
||||||
font-size: 28px;
|
font-size: 28px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
@@ -426,6 +487,7 @@ onMounted(reloadAll)
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
gap: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.filter-area,
|
.filter-area,
|
||||||
@@ -435,6 +497,101 @@ onMounted(reloadAll)
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.qrcode-table {
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qrcode-table :deep(.el-table__inner-wrapper) {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qrcode-table :deep(.el-table__header th) {
|
||||||
|
height: 42px;
|
||||||
|
background: #f8f9fb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qrcode-table :deep(.el-table__cell) {
|
||||||
|
padding: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qrcode-table :deep(.cell) {
|
||||||
|
padding: 0 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qrcode-table :deep(.el-table__fixed-right) {
|
||||||
|
box-shadow: -8px 0 12px rgba(31, 45, 61, 0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
.qrcode-table :deep(.el-table__header),
|
||||||
|
.qrcode-table :deep(.el-table__body) {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qrcode-table :deep(.el-table__header-wrapper),
|
||||||
|
.qrcode-table :deep(.el-table__body-wrapper) {
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qrcode-table :deep(.qr-col) {
|
||||||
|
width: 12%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qrcode-table :deep(.status-col) {
|
||||||
|
width: 10%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qrcode-table :deep(.note-col) {
|
||||||
|
width: 40%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qrcode-table :deep(.group-col) {
|
||||||
|
width: 10%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qrcode-table :deep(.expire-col) {
|
||||||
|
width: 16%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qrcode-table :deep(.action-col) {
|
||||||
|
width: 12%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qrcode-thumb {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 64px;
|
||||||
|
height: 64px;
|
||||||
|
border: 1px solid #eef1f5;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-cell {
|
||||||
|
color: #303846;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-cell,
|
||||||
|
.expire-cell {
|
||||||
|
color: #5f6b7a;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 6px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-actions :deep(.el-button) {
|
||||||
|
margin-left: 0;
|
||||||
|
padding: 4px 6px;
|
||||||
|
}
|
||||||
|
|
||||||
.upload-preview {
|
.upload-preview {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -463,4 +620,17 @@ onMounted(reloadAll)
|
|||||||
.expired-tag {
|
.expired-tag {
|
||||||
color: var(--el-color-danger);
|
color: var(--el-color-danger);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.edit-image-area {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.expire-editor {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user