diff --git a/backend/internal/modules/systemconfig/repository.go b/backend/internal/modules/systemconfig/repository.go index 40bfba2..b239661 100644 --- a/backend/internal/modules/systemconfig/repository.go +++ b/backend/internal/modules/systemconfig/repository.go @@ -41,6 +41,7 @@ var defaultConfigs = []defaultConfig{ {Key: "settlement.platform_fee_rate", Value: "0.00", Description: "平台抽成比例"}, {Key: "settlement.owner_cycle_days", Value: "0", Description: "号主结算周期天数"}, {Key: "withdraw.min_amount", Value: "100", Description: "提现最低金额预留"}, + {Key: "chat.default_support_admin_id", Value: "1", Description: "订单群聊默认接入的客服管理员 ID"}, {Key: homeAnnouncementsConfigKey, Value: defaultHomeAnnouncementsConfigValue(), Description: "移动端首页公告 JSON 数组"}, {Key: homeBannersConfigKey, Value: defaultHomeBannersConfigValue(), Description: "移动端首页轮播图 JSON 数组"}, {Key: publishOptionsConfigKey, Value: defaultPublishOptionsConfigValue(), Description: "发布页选项配置 JSON"}, @@ -136,6 +137,11 @@ func (r *Repository) ensureDefaults() error { if err := tx.Clauses(clause.OnConflict{DoNothing: true}).Create(&row).Error; err != nil { return err } + if item.Key == "chat.default_support_admin_id" { + if err := updateDefaultDescription(tx, item); err != nil { + return err + } + } if item.Key == publishOptionsConfigKey { if err := updateLegacyPublishOptions(tx, item); err != nil { return err @@ -146,6 +152,18 @@ func (r *Repository) ensureDefaults() error { }) } +func updateDefaultDescription(tx *gorm.DB, item defaultConfig) error { + var row model.SystemConfig + if err := tx.Where("`key` = ?", item.Key).First(&row).Error; err != nil { + return err + } + if row.Description == item.Description { + return nil + } + row.Description = item.Description + return tx.Save(&row).Error +} + func updateLegacyPublishOptions(tx *gorm.DB, item defaultConfig) error { var row model.SystemConfig if err := tx.Where("`key` = ?", item.Key).First(&row).Error; err != nil { diff --git a/frontend/src/composables/useAdminPaginatedTable.ts b/frontend/src/composables/useAdminPaginatedTable.ts new file mode 100644 index 0000000..62481e6 --- /dev/null +++ b/frontend/src/composables/useAdminPaginatedTable.ts @@ -0,0 +1,52 @@ +import { ref, type Ref } from 'vue' + +interface PaginatedResult { + items: T[] + total: number +} + +interface AdminPaginatedTableOptions { + fetchFn: (page: number, pageSize: number) => Promise> + defaultPageSize?: number + immediate?: boolean +} + +interface AdminPaginatedTableResult { + loading: Ref + data: Ref + total: Ref + currentPage: Ref + currentPageSize: Ref + load: () => Promise + handleSizeChange: () => void +} + +export function useAdminPaginatedTable(options: AdminPaginatedTableOptions): AdminPaginatedTableResult { + const loading = ref(false) + const data = ref([]) as Ref + const total = ref(0) + const currentPage = ref(1) + const currentPageSize = ref(options.defaultPageSize || 20) + + async function load() { + loading.value = true + try { + const result = await options.fetchFn(currentPage.value, currentPageSize.value) + data.value = result.items + total.value = result.total + } finally { + loading.value = false + } + } + + function handleSizeChange() { + currentPage.value = 1 + load() + } + + if (options.immediate !== false) { + load() + } + + return { loading, data, total, currentPage, currentPageSize, load, handleSizeChange } +} diff --git a/frontend/src/composables/useAdminTable.ts b/frontend/src/composables/useAdminTable.ts new file mode 100644 index 0000000..9e768cc --- /dev/null +++ b/frontend/src/composables/useAdminTable.ts @@ -0,0 +1,41 @@ +import { ref, type Ref } from 'vue' + +interface AdminQueryOptions { + fetchFn: () => Promise + immediate?: boolean +} + +interface AdminQueryResult { + loading: Ref + error: Ref + data: Ref + load: () => Promise +} + +export function useAdminQuery(options: AdminQueryOptions): AdminQueryResult { + const loading = ref(false) + const error = ref(null) + const data = ref() as Ref + + async function load() { + loading.value = true + error.value = null + try { + data.value = await options.fetchFn() + } catch (e) { + error.value = e instanceof Error ? e.message : '加载失败' + console.error('Failed to load data:', e) + } finally { + loading.value = false + } + } + + if (options.immediate !== false) { + load() + } + + return { loading, error, data, load } +} + +// 保持向后兼容 +export const useAdminTable = useAdminQuery diff --git a/frontend/src/composables/useMoney.ts b/frontend/src/composables/useMoney.ts new file mode 100644 index 0000000..da286a5 --- /dev/null +++ b/frontend/src/composables/useMoney.ts @@ -0,0 +1,3 @@ +export function useMoney() { + return (value: number | undefined | null) => `¥${Math.round(Number(value || 0))}` +} diff --git a/frontend/src/layouts/AdminLayout.vue b/frontend/src/layouts/AdminLayout.vue index 556b438..6624fb1 100644 --- a/frontend/src/layouts/AdminLayout.vue +++ b/frontend/src/layouts/AdminLayout.vue @@ -1,7 +1,21 @@