admin模块接入数据库

This commit is contained in:
comlibmb
2026-02-13 17:29:50 +08:00
parent 56209b7a75
commit ec636dc703
58 changed files with 5586 additions and 1394 deletions

View File

@@ -0,0 +1,180 @@
import { rpcOrNull, rpcOrValue, rpcOrEmptyArray } from '@/services/analytics/rpc.uts'
/**
* 文章分类类型
*/
export type ArticleCategory = {
id: string
name: string
icon: string | null
sort: number
status: number
created_at: string
updated_at: string
}
/**
* 文章简要信息类型 (列表使用)
*/
export type ArticleItem = {
id: string
category_id: string
category_name: string
title: string
author: string | null
image: string | null
description: string | null
status: number
views: number
is_banner: boolean
is_hot: boolean
created_at: string
updated_at: string
}
/**
* 文章详情类型
*/
export type ArticleDetail = {
id: string
category_id: string
category_name: string
title: string
author: string | null
image: string | null
description: string | null
content: string
status: number
views: number
is_banner: boolean
is_hot: boolean
linked_product_id: string | null
created_at: string
updated_at: string
}
/**
* 获取文章分类列表
*/
export async function fetchArticleCategoryPage(
page : number,
pageSize : number,
search : string | null = null
) : Promise<{ total : number, items : Array<ArticleCategory> }> {
const res = await rpcOrNull('rpc_admin_article_category_list', {
p_page: page,
p_page_size: pageSize,
p_search: search
} as UTSJSONObject)
if (res == null) return { total: 0, items: [] as Array<ArticleCategory> }
return {
total: (res as any).total as number,
items: (res as any).items as Array<ArticleCategory>
}
}
/**
* 保存文章分类
*/
export async function saveArticleCategory(
id : string | null,
name : string,
icon : string | null,
sort : number,
status : number
) : Promise<string | null> {
const res = await rpcOrValue('rpc_admin_article_category_save', {
p_id: id,
p_name: name,
p_icon: icon,
p_sort: sort,
p_status: status
} as any)
return res != null ? String(res) : null
}
/**
* 删除文章分类
*/
export async function deleteArticleCategory(id : string) : Promise<boolean> {
const ok = await rpcOrValue('rpc_admin_article_category_delete', { p_id: id } as any)
return ok === true
}
/**
* 设置文章分类状态
*/
export async function setArticleCategoryStatus(id : string, status : number) : Promise<boolean> {
const ok = await rpcOrValue('rpc_admin_article_category_set_status', { p_id: id, p_status: status } as any)
return ok === true
}
/**
* 获取文章列表
*/
export async function fetchArticlePage(
page : number,
pageSize : number,
categoryId : string | null = null,
status : number | null = null,
search : string | null = null
) : Promise<{ total : number, items : Array<ArticleItem> }> {
const res = await rpcOrNull('rpc_admin_article_list', {
p_page: page,
p_page_size: pageSize,
p_category_id: categoryId,
p_status: status,
p_search: search
} as UTSJSONObject)
if (res == null) return { total: 0, items: [] as Array<ArticleItem> }
return {
total: (res as any).total as number,
items: (res as any).items as Array<ArticleItem>
}
}
/**
* 获取文章详情
*/
export async function fetchArticleDetail(id : string) : Promise<ArticleDetail | null> {
const res = await rpcOrNull('rpc_admin_article_get_detail', { p_id: id } as any)
return res as ArticleDetail | null
}
/**
* 保存文章内容
*/
export async function saveArticle(payload : any) : Promise<string | null> {
const res = await rpcOrValue('rpc_admin_article_save', {
p_id: payload.id ?? null,
p_category_id: payload.category_id,
p_title: payload.title,
p_author: payload.author ?? null,
p_image: payload.image ?? null,
p_description: payload.description ?? null,
p_content: payload.content,
p_status: payload.status ?? 0,
p_is_banner: payload.is_banner ?? false,
p_is_hot: payload.is_hot ?? false,
p_linked_product_id: payload.linked_product_id ?? null
} as any)
return res != null ? String(res) : null
}
/**
* 删除文章记录
*/
export async function deleteArticle(id : string) : Promise<boolean> {
const ok = await rpcOrValue('rpc_admin_article_delete', { p_id: id } as any)
return ok === true
}
/**
* 设置文章发布状态
*/
export async function setArticleStatus(id : string, status : number) : Promise<boolean> {
const ok = await rpcOrValue('rpc_admin_article_set_status', { p_id: id, p_status: status } as any)
return ok === true
}

View File

@@ -0,0 +1,199 @@
import supa from '@/components/supadb/aksupainstance.uts'
/**
* 分销配置模型 (与 ak_distribution_config 表对齐)
*/
export type DistributionConfig = {
id?: string
// 分销模式
is_enabled: boolean
extract_type: string
bind_type: string
store_brokerage_binding_status: string
brokerage_poster_status: string | null
brokerage_level: number
is_area_manager: boolean
is_agent_apply: boolean
is_commission_window: boolean
// 返佣设置
is_self_brokerage: boolean
is_member_brokerage: boolean
brokerage_type: string
is_promoter_brokerage: boolean
promoter_brokerage_price: number
promoter_brokerage_day_max: number
store_brokerage_ratio: number
store_brokerage_two_ratio: number
extract_frozen_time: number
// 提现设置
user_extract_min_price: number
extract_bank_list: string
extract_type_list: string[]
wechat_extract_type: string
alipay_extract_type: string
user_extract_fee: number
updated_at?: string
updated_by?: string
}
/**
* 推广员模型
*/
export type Promoter = {
id: string
nickname: string
name: string
phone: string
avatar_url: string
level: string
userCount: number
orderCount: number
orderAmount: number
commissionTotal: number
withdrawnAmount: number
withdrawCount: number
unwithdrawnAmount: number
}
/**
* 获取分销全局配置
*/
export async function getDistributionConfig(): Promise<DistributionConfig | null> {
const { data, error } = await supa
.from('ak_distribution_config')
.select('*')
.eq('id', 'global_config')
.single()
.execute()
if (error != null) {
console.error('获取分销配置失败:', error)
return null
}
return data as DistributionConfig | null
}
/**
* 保存分销全局配置
*/
export async function saveDistributionConfig(config: DistributionConfig): Promise<boolean> {
const { error } = await supa
.from('ak_distribution_config')
.upsert({
...config,
id: 'global_config',
updated_at: new Date().toISOString()
})
.execute()
if (error != null) {
console.error('保存分销配置失败:', error)
return false
}
return true
}
/**
* 分销等级模型
*/
export type DistributionLevel = {
id?: string
name: string
level: number
percent1: number
percent2: number
task_total: number
task_finish: number
is_visible: boolean
created_at?: string
updated_at?: string
}
/**
* 获取分销等级列表
*/
export async function getDistributionLevelList(): Promise<DistributionLevel[]> {
const { data, error } = await supa
.from('ak_distribution_level')
.select('*')
.order('level', { ascending: true })
.execute()
if (error != null) {
console.error('获取分销等级列表失败:', error)
return [] as DistributionLevel[]
}
return data as DistributionLevel[]
}
/**
* 保存/更新分销等级
*/
export async function saveDistributionLevel(level: DistributionLevel): Promise<boolean> {
const { error } = await supa
.from('ak_distribution_level')
.upsert({
...level,
updated_at: new Date().toISOString()
})
.execute()
if (error != null) {
console.error('保存分销等级失败:', error)
return false
}
return true
}
/**
* 删除分销等级
*/
export async function deleteDistributionLevel(id: string): Promise<boolean> {
const { error } = await supa
.from('ak_distribution_level')
.delete()
.eq('id', id)
.execute()
if (error != null) {
console.error('删除分销等级失败:', error)
return false
}
return true
}
/**
* 获取推广员列表
*/
export type PromoterListParams = {
search?: string | null
page?: number
pageSize?: number
startTime?: string | null
endTime?: string | null
}
/**
* 获取推广员列表(聚合统计)
*/
export async function getPromoterList(params?: PromoterListParams): Promise<Promoter[]> {
const payload = {
p_search: params?.search ?? null,
p_page: params?.page ?? 1,
p_page_size: params?.pageSize ?? 20,
p_start_time: params?.startTime ?? null,
p_end_time: params?.endTime ?? null
} as any
const { data, error } = await supa
.rpc('rpc_admin_get_promoter_list', payload as any)
if (error != null) {
console.error('获取推广员列表失败:', error)
return [] as Promoter[]
}
return (data ?? []) as Promoter[]
}

View File

@@ -0,0 +1,139 @@
import supa from '@/components/supadb/aksupainstance.uts'
/**
* 优惠券模板模型
*/
export type CouponTemplate = {
id?: string
cid?: number
merchant_id?: string
name: string
description: string | null
coupon_type: number // 1:满减, 2:折扣, 3:免运费
discount_type: number // 1:固定金额, 2:百分比
discount_value: number
min_order_amount: number
max_discount_amount: number | null
total_quantity: number | null
per_user_limit: number
usage_limit: number
applicable_products: any[]
applicable_categories: any[]
start_time: string
end_time: string
status: number // 1:正常, 2:暂停, 3:已结束
created_at?: string
updated_at?: string
}
export type CouponQuery = {
name?: string | null
type?: number | null
status?: number | null
page?: number
pageSize?: number
}
function getCurrentUid(): string | null {
try {
const session = supa.getSession()
return session?.user?.getString('id') ?? null
} catch (e) {
return null
}
}
/**
* 分页获取优惠券模板列表
*/
export async function fetchAdminCoupons(query?: CouponQuery): Promise<{ total: number; items: CouponTemplate[] }> {
let q = supa.from('ml_coupon_templates').select('*', { count: 'exact' })
if (query?.name != null && query.name !== '') {
q = q.ilike('name', `%${query.name}%`)
}
if (query?.type != null) {
q = q.eq('coupon_type', query.type)
}
if (query?.status != null) {
q = q.eq('status', query.status)
}
const p = query?.page ?? 1
const ps = query?.pageSize ?? 20
const from = (p - 1) * ps
const to = from + ps - 1
const { data, error, count } = await q
.order('created_at', { ascending: false })
.range(from, to)
.execute()
if (error != null) {
console.error('获取优惠券列表失败:', error)
return { total: 0, items: [] as CouponTemplate[] }
}
return {
total: count ?? 0,
items: (data ?? []) as CouponTemplate[]
}
}
/**
* 保存优惠券模板(新增/更新)
*/
export async function saveCouponTemplate(tpl: CouponTemplate): Promise<boolean> {
const uid = getCurrentUid()
if (uid == null) return false
const { error } = await supa
.from('ml_coupon_templates')
.upsert({
...tpl,
merchant_id: uid,
updated_at: new Date().toISOString()
})
.execute()
if (error != null) {
console.error('保存优惠券模板失败:', error)
return false
}
return true
}
/**
* 切换优惠券状态
*/
export async function toggleCouponStatus(id: string, isOpen: boolean): Promise<boolean> {
const nextStatus = isOpen ? 1 : 2 // 1:正常, 2:暂停
const { error } = await supa
.from('ml_coupon_templates')
.update({ status: nextStatus, updated_at: new Date().toISOString() })
.eq('id', id)
.execute()
if (error != null) {
console.error('更新优惠券状态失败:', error)
return false
}
return true
}
/**
* 删除优惠券模板
*/
export async function deleteCouponTemplate(id: string): Promise<boolean> {
const { error } = await supa
.from('ml_coupon_templates')
.delete()
.eq('id', id)
.execute()
if (error != null) {
console.error('删除优惠券失败:', error)
return false
}
return true
}

View File

@@ -0,0 +1,147 @@
import supa from '@/components/supadb/aksupainstance.uts'
/**
* 商品标签分组模型
*/
export type ProductLabelGroup = {
id?: string
merchant_id?: string
name: string
sort_order: number
created_at?: string
updated_at?: string
}
/**
* 商品标签模型
*/
export type ProductLabel = {
id?: string
group_id: string | null
merchant_id?: string
name: string
is_active: boolean
show_in_mobile: boolean
sort_order: number
created_at?: string
updated_at?: string
}
/**
* 获取所有标签分组
*/
export async function fetchLabelGroups(): Promise<ProductLabelGroup[]> {
const { data, error } = await supa
.from('ak_product_label_groups')
.select('*')
.order('sort_order', { ascending: true })
.execute()
if (error != null) {
console.error('获取标签分组失败:', error)
return [] as ProductLabelGroup[]
}
return (data ?? []) as ProductLabelGroup[]
}
/**
* 保存标签分组(新增/更新)
*/
export async function saveLabelGroup(group: ProductLabelGroup): Promise<boolean> {
const session = supa.getSession()
const uid = session?.user?.getString('id')
if (uid == null) return false
const { error } = await supa
.from('ak_product_label_groups')
.upsert({
...group,
merchant_id: uid,
updated_at: new Date().toISOString()
})
.execute()
if (error != null) {
console.error('保存标签分组失败:', error)
return false
}
return true
}
/**
* 删除标签分组
*/
export async function deleteLabelGroup(id: string): Promise<boolean> {
const { error } = await supa
.from('ak_product_label_groups')
.delete()
.eq('id', id)
.execute()
if (error != null) {
console.error('删除标签分组失败:', error)
return false
}
return true
}
/**
* 获取标签列表
*/
export async function fetchLabels(groupId: string | null = null): Promise<ProductLabel[]> {
let query = supa.from('ak_product_labels').select('*')
if (groupId != null) {
query = query.eq('group_id', groupId)
}
const { data, error } = await query
.order('sort_order', { ascending: true })
.execute()
if (error != null) {
console.error('获取标签失败:', error)
return [] as ProductLabel[]
}
return (data ?? []) as ProductLabel[]
}
/**
* 保存标签(新增/更新)
*/
export async function saveLabel(label: ProductLabel): Promise<boolean> {
const session = supa.getSession()
const uid = session?.user?.getString('id')
if (uid == null) return false
const { error } = await supa
.from('ak_product_labels')
.upsert({
...label,
merchant_id: uid,
updated_at: new Date().toISOString()
})
.execute()
if (error != null) {
console.error('保存标签失败:', error)
return false
}
return true
}
/**
* 删除标签
*/
export async function deleteLabel(id: string): Promise<boolean> {
const { error } = await supa
.from('ak_product_labels')
.delete()
.eq('id', id)
.execute()
if (error != null) {
console.error('删除标签失败:', error)
return false
}
return true
}

View File

@@ -0,0 +1,119 @@
import supa from '@/components/supadb/aksupainstance.uts'
export type UserLevel = {
id: string
name: string
level_weight: number
discount_percent: number
is_visible: boolean
status: number
deleted_at: string | null
}
export type ProductSku = {
id: string
product_id: string
sku_code: string
specifications: any
price: number
stock: number
status: number
image_url: string | null
}
export type ProductMemberPrice = {
id?: string
merchant_id?: string
product_id: string
sku_id: string
level_id: string
member_price: number
created_at?: string
updated_at?: string
}
export type MemberPriceMatrixRow = {
sku: ProductSku
pricesByLevel: Record<string, number | null>
}
function getCurrentUid(): string | null {
try {
const session = supa.getSession()
const uid = session?.user?.getString('id')
return uid ?? null
} catch (e) {
return null
}
}
export async function fetchActiveUserLevels(): Promise<UserLevel[]> {
const { data, error } = await supa
.from('ak_user_levels')
.select('id,name,level_weight,discount_percent,is_visible,status,deleted_at')
.is('deleted_at', null)
.eq('status', 1)
.eq('is_visible', true)
.order('level_weight', { ascending: true })
.execute()
if (error != null) {
console.error('获取会员等级失败:', error)
return [] as UserLevel[]
}
return (data ?? []) as UserLevel[]
}
export async function fetchProductSkus(productId: string): Promise<ProductSku[]> {
const { data, error } = await supa
.from('ml_product_skus')
.select('id,product_id,sku_code,specifications,price,stock,status,image_url')
.eq('product_id', productId)
.order('created_at', { ascending: true })
.execute()
if (error != null) {
console.error('获取 SKU 失败:', error)
return [] as ProductSku[]
}
return (data ?? []) as ProductSku[]
}
export async function fetchMemberPrices(productId: string): Promise<ProductMemberPrice[]> {
const { data, error } = await supa
.from('ak_product_member_prices')
.select('id,product_id,sku_id,level_id,member_price,created_at,updated_at')
.eq('product_id', productId)
.execute()
if (error != null) {
console.error('获取会员价失败:', error)
return [] as ProductMemberPrice[]
}
return (data ?? []) as ProductMemberPrice[]
}
export async function saveMemberPrices(productId: string, rows: Array<{ sku_id: string; level_id: string; member_price: number }>): Promise<boolean> {
const uid = getCurrentUid()
if (uid == null) return false
const payload = rows.map(r => ({
merchant_id: uid,
product_id: productId,
sku_id: r.sku_id,
level_id: r.level_id,
member_price: r.member_price,
updated_at: new Date().toISOString()
}))
const { error } = await supa
.from('ak_product_member_prices')
.upsert(payload as any)
.execute()
if (error != null) {
console.error('保存会员价失败:', error)
return false
}
return true
}

View File

@@ -0,0 +1,85 @@
import supa from '@/components/supadb/aksupainstance.uts'
export type ProductProtection = {
id?: string
merchant_id?: string
name: string
description: string
icon_url: string | null
sort_order: number
is_active: boolean
created_at?: string
updated_at?: string
}
function getCurrentUid(): string | null {
try {
const session = supa.getSession()
const uid = session?.user?.getString('id')
return uid ?? null
} catch (e) {
return null
}
}
export async function fetchProductProtections(): Promise<ProductProtection[]> {
const { data, error } = await supa
.from('ak_product_protections')
.select('*')
.order('sort_order', { ascending: true })
.execute()
if (error != null) {
console.error('获取商品保障失败:', error)
return [] as ProductProtection[]
}
return (data ?? []) as ProductProtection[]
}
export async function saveProductProtection(item: ProductProtection): Promise<boolean> {
const uid = getCurrentUid()
if (uid == null) return false
const { error } = await supa
.from('ak_product_protections')
.upsert({
...item,
merchant_id: uid,
updated_at: new Date().toISOString()
})
.execute()
if (error != null) {
console.error('保存商品保障失败:', error)
return false
}
return true
}
export async function deleteProductProtection(id: string): Promise<boolean> {
const { error } = await supa
.from('ak_product_protections')
.delete()
.eq('id', id)
.execute()
if (error != null) {
console.error('删除商品保障失败:', error)
return false
}
return true
}
export async function setProductProtectionActive(id: string, isActive: boolean): Promise<boolean> {
const { error } = await supa
.from('ak_product_protections')
.update({ is_active: isActive, updated_at: new Date().toISOString() })
.eq('id', id)
.execute()
if (error != null) {
console.error('更新商品保障状态失败:', error)
return false
}
return true
}

View File

@@ -0,0 +1,85 @@
import { rpcOrValue } from '@/services/analytics/rpc.uts'
import supa from '@/components/supadb/aksupainstance.uts'
export type ProductReviewItem = {
id: string
product_id: string
product_name: string
product_image: string | null
user_id: string
username: string | null
rating: number
content: string | null
merchant_reply: string | null
status: number
created_at: string
total_count: number
}
export type ProductReviewQuery = {
searchProduct?: string | null
searchUser?: string | null
status?: number | null
startTime?: string | null
endTime?: string | null
page?: number
pageSize?: number
}
export async function fetchAdminProductReviews(query?: ProductReviewQuery): Promise<{ total: number; items: Array<ProductReviewItem> }> {
const payload = {
p_search_product: query?.searchProduct ?? null,
p_search_user: query?.searchUser ?? null,
p_status: query?.status ?? null,
p_start_time: query?.startTime ?? null,
p_end_time: query?.endTime ?? null,
p_page: query?.page ?? 1,
p_page_size: query?.pageSize ?? 20
} as any
const res = await rpcOrValue('rpc_admin_get_product_reviews', payload as any)
const arr = Array.isArray(res) ? (res as Array<any>) : ([] as Array<any>)
const total = arr.length > 0 ? parseInt(String(arr[0]?.total_count ?? '0')) : 0
return { total, items: arr as Array<ProductReviewItem> }
}
export async function approveProductReview(id: string): Promise<boolean> {
const { error } = await supa.from('ml_product_reviews').update({ status: 1, updated_at: new Date().toISOString() }).eq('id', id).execute()
if (error != null) {
console.error('审核通过失败:', error)
return false
}
return true
}
export async function rejectProductReview(id: string): Promise<boolean> {
const { error } = await supa.from('ml_product_reviews').update({ status: 3, updated_at: new Date().toISOString() }).eq('id', id).execute()
if (error != null) {
console.error('审核驳回/隐藏失败:', error)
return false
}
return true
}
export async function replyProductReview(id: string, reply: string): Promise<boolean> {
const { error } = await supa
.from('ml_product_reviews')
.update({ merchant_reply: reply, merchant_replied_at: new Date().toISOString(), updated_at: new Date().toISOString() })
.eq('id', id)
.execute()
if (error != null) {
console.error('回复失败:', error)
return false
}
return true
}
export async function deleteProductReview(id: string): Promise<boolean> {
const { error } = await supa.from('ml_product_reviews').update({ status: 2, updated_at: new Date().toISOString() }).eq('id', id).execute()
if (error != null) {
console.error('删除失败:', error)
return false
}
return true
}

View File

@@ -1,3 +1,4 @@
import supa from '@/components/supadb/aksupainstance.uts'
import { rpcOrNull, rpcOrValue } from '@/services/analytics/rpc.uts'
export type AdminProduct = {
@@ -9,7 +10,27 @@ export type AdminProduct = {
sales: number
status: number
created_at: string
category_id: string
category_name: string
subtitle: string | null
description: string | null
product_code: string | null
unit: string | null
image_urls: string[] | null
video_urls: string[] | null
tags: string[] | null
attributes: any | null
}
export type AdminProductSku = {
id?: string
product_id?: string
sku_code: string
specifications: any
price: number
stock: number
status: number
image_url: string | null
}
export type ProductPageResult = {
@@ -50,6 +71,87 @@ export async function fetchAdminProductPage(
}
}
/**
* 获取商品详情(包含 SKU 列表)
*/
export async function fetchAdminProductDetail(productId: string): Promise<{ product: AdminProduct | null, skus: AdminProductSku[] }> {
const { data: productData, error: productError } = await supa
.from('ml_products')
.select('*, ml_categories(name)')
.eq('id', productId)
.single()
.execute()
if (productError != null) {
console.error('获取商品详情失败:', productError)
return { product: null, skus: [] }
}
const { data: skuData, error: skuError } = await supa
.from('ml_product_skus')
.select('*')
.eq('product_id', productId)
.execute()
const product = productData as any
if (product != null && product.ml_categories != null) {
product.category_name = (product.ml_categories as any).name
}
return {
product: product as AdminProduct,
skus: (skuData ?? []) as AdminProductSku[]
}
}
/**
* 保存商品(新增或更新,含 SKU 关联保存)
*/
export async function saveAdminProduct(product: Partial<AdminProduct>, skus: AdminProductSku[]): Promise<boolean> {
const session = supa.getSession()
const uid = session?.user?.getString('id')
if (uid == null) return false
// 1. 保存商品主表
const { data: savedProduct, error: productError } = await supa
.from('ml_products')
.upsert({
...product,
merchant_id: uid,
updated_at: new Date().toISOString()
})
.select()
.single()
.execute()
if (productError != null || savedProduct == null) {
console.error('保存商品主表失败:', productError)
return false
}
const savedProductId = (savedProduct as any).id as string
// 2. 保存 SKU简单策略先删除旧的再插入新的或使用 upsert
// 注意:如果涉及订单关联,不能简单的物理删除旧 SKU
const skuPayload = skus.map(s => ({
...s,
product_id: savedProductId,
updated_at: new Date().toISOString()
}))
const { error: skuError } = await supa
.from('ml_product_skus')
.upsert(skuPayload as any)
.execute()
if (skuError != null) {
console.error('保存 SKU 失败:', skuError)
return false
}
return true
}
/**
* 获取商品状态汇总统计
*/

View File

@@ -0,0 +1,125 @@
import supa from '@/components/supadb/aksupainstance.uts'
export type ProductSpecTemplate = {
id?: string
merchant_id?: string
name: string
specs: string
attrs: string
sort_order: number
is_active: boolean
created_at?: string
updated_at?: string
}
export type ProductParamKV = {
label: string
value: string
}
export type ProductParamTemplate = {
id?: string
merchant_id?: string
name: string
sort_order: number
params: ProductParamKV[]
is_active: boolean
created_at?: string
updated_at?: string
}
function getCurrentUid(): string | null {
try {
const session = supa.getSession()
const uid = session?.user?.getString('id')
return uid ?? null
} catch (e) {
return null
}
}
// -------------------- Spec Templates --------------------
export async function fetchSpecTemplates(search: string | null = null): Promise<ProductSpecTemplate[]> {
let q = supa.from('ak_product_spec_templates').select('*')
if (search != null && search.trim() !== '') {
q = q.ilike('name', `%${search.trim()}%`)
}
const { data, error } = await q.order('sort_order', { ascending: true }).execute()
if (error != null) {
console.error('获取规格模板失败:', error)
return [] as ProductSpecTemplate[]
}
return (data ?? []) as ProductSpecTemplate[]
}
export async function saveSpecTemplate(tpl: ProductSpecTemplate): Promise<boolean> {
const uid = getCurrentUid()
if (uid == null) return false
const { error } = await supa
.from('ak_product_spec_templates')
.upsert({
...tpl,
merchant_id: uid,
updated_at: new Date().toISOString()
})
.execute()
if (error != null) {
console.error('保存规格模板失败:', error)
return false
}
return true
}
export async function deleteSpecTemplate(id: string): Promise<boolean> {
const { error } = await supa.from('ak_product_spec_templates').delete().eq('id', id).execute()
if (error != null) {
console.error('删除规格模板失败:', error)
return false
}
return true
}
// -------------------- Param Templates --------------------
export async function fetchParamTemplates(search: string | null = null): Promise<ProductParamTemplate[]> {
let q = supa.from('ak_product_param_templates').select('*')
if (search != null && search.trim() !== '') {
q = q.ilike('name', `%${search.trim()}%`)
}
const { data, error } = await q.order('sort_order', { ascending: true }).execute()
if (error != null) {
console.error('获取参数模板失败:', error)
return [] as ProductParamTemplate[]
}
return (data ?? []) as ProductParamTemplate[]
}
export async function saveParamTemplate(tpl: ProductParamTemplate): Promise<boolean> {
const uid = getCurrentUid()
if (uid == null) return false
const { error } = await supa
.from('ak_product_param_templates')
.upsert({
...tpl,
merchant_id: uid,
updated_at: new Date().toISOString()
})
.execute()
if (error != null) {
console.error('保存参数模板失败:', error)
return false
}
return true
}
export async function deleteParamTemplate(id: string): Promise<boolean> {
const { error } = await supa.from('ak_product_param_templates').delete().eq('id', id).execute()
if (error != null) {
console.error('删除参数模板失败:', error)
return false
}
return true
}