完成编辑商品信息同步
This commit is contained in:
144
manifest.json
144
manifest.json
@@ -1,74 +1,74 @@
|
|||||||
{
|
{
|
||||||
"name": "mall",
|
"name": "mall",
|
||||||
"appid": "__UNI__YOUR_APP_ID__",
|
"appid": "__UNI__81482FF",
|
||||||
"description": "A multi-role e-commerce application.",
|
"description": "A multi-role e-commerce application.",
|
||||||
"versionName": "1.0.0",
|
"versionName": "1.0.0",
|
||||||
"versionCode": "100",
|
"versionCode": "100",
|
||||||
"transformPx": false,
|
"transformPx": false,
|
||||||
"app-plus": {
|
"app-plus": {
|
||||||
"usingComponents": true,
|
"usingComponents": true,
|
||||||
"nvueStyleCompiler": "uni-app",
|
"nvueStyleCompiler": "uni-app",
|
||||||
"compilerVersion": 3,
|
"compilerVersion": 3,
|
||||||
"splashscreen": {
|
"splashscreen": {
|
||||||
"alwaysShowBeforeRender": true,
|
"alwaysShowBeforeRender": true,
|
||||||
"waiting": true,
|
"waiting": true,
|
||||||
"autoclose": true,
|
"autoclose": true,
|
||||||
"delay": 0
|
"delay": 0
|
||||||
},
|
},
|
||||||
"modules": {},
|
"modules": {},
|
||||||
"distribute": {
|
"distribute": {
|
||||||
"android": {
|
"android": {
|
||||||
"permissions": [
|
"permissions": [
|
||||||
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
|
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
|
||||||
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
|
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
|
||||||
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
|
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
|
||||||
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
|
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
|
||||||
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
|
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
|
||||||
"<uses-feature android:name=\"android.hardware.camera\"/>",
|
"<uses-feature android:name=\"android.hardware.camera\"/>",
|
||||||
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
|
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
|
||||||
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
|
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
|
||||||
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
|
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
|
||||||
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
|
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
|
||||||
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
|
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
|
||||||
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
|
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
|
||||||
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
|
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
|
||||||
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
|
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
|
||||||
"<uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"/>",
|
"<uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"/>",
|
||||||
"<uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\"/>",
|
"<uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\"/>",
|
||||||
"<uses-permission android:name=\"android.permission.INTERNET\"/>"
|
"<uses-permission android:name=\"android.permission.INTERNET\"/>"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"ios": {},
|
"ios": {},
|
||||||
"sdkConfigs": {}
|
"sdkConfigs": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"quickapp": {},
|
"quickapp": {},
|
||||||
"mp-weixin": {
|
"mp-weixin": {
|
||||||
"appid": "",
|
"appid": "",
|
||||||
"setting": {
|
"setting": {
|
||||||
"urlCheck": false
|
"urlCheck": false
|
||||||
},
|
},
|
||||||
"usingComponents": true
|
"usingComponents": true
|
||||||
},
|
},
|
||||||
"mp-alipay": {
|
"mp-alipay": {
|
||||||
"usingComponents": true
|
"usingComponents": true
|
||||||
},
|
},
|
||||||
"mp-baidu": {
|
"mp-baidu": {
|
||||||
"usingComponents": true
|
"usingComponents": true
|
||||||
},
|
},
|
||||||
"mp-toutiao": {
|
"mp-toutiao": {
|
||||||
"usingComponents": true
|
"usingComponents": true
|
||||||
},
|
},
|
||||||
"uniStatistics": {
|
"uniStatistics": {
|
||||||
"enable": false
|
"enable": false
|
||||||
},
|
},
|
||||||
"vueVersion": "3",
|
"vueVersion": "3",
|
||||||
"uni-app-x": {},
|
"uni-app-x": {},
|
||||||
"h5": {
|
"h5": {
|
||||||
"title": "mall",
|
"title": "mall",
|
||||||
"router": {
|
"router": {
|
||||||
"mode": "hash",
|
"mode": "hash",
|
||||||
"base": "./"
|
"base": "./"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
<view class="product-edit-page">
|
<view class="product-edit-page">
|
||||||
<view class="page-header">
|
<view class="page-header">
|
||||||
<view class="back-link" @click="goBack">
|
<view class="back-link" @click="goBack">
|
||||||
<text class="arrow">{"<"}</text>
|
<text class="arrow"><</text>
|
||||||
<text class="back-txt">返回</text>
|
<text class="back-txt">返回</text>
|
||||||
</view>
|
</view>
|
||||||
<text class="header-title">编辑商品</text>
|
<text class="header-title">编辑商品</text>
|
||||||
@@ -232,41 +232,76 @@ async function loadCategoryOptions() {
|
|||||||
|
|
||||||
async function fetchProductDetail(id: string, mId: string) {
|
async function fetchProductDetail(id: string, mId: string) {
|
||||||
try {
|
try {
|
||||||
const { data, error } = await supa
|
const res = await supa
|
||||||
.from('ml_products')
|
.from('ml_products')
|
||||||
.select('*')
|
.select('*')
|
||||||
.eq('id', id)
|
.eq('id', id)
|
||||||
.eq('merchant_id', mId)
|
.eq('merchant_id', mId)
|
||||||
.single()
|
.single()
|
||||||
|
.execute()
|
||||||
|
|
||||||
if (error) throw error
|
if (res.error != null) {
|
||||||
if (data) {
|
console.error('[ProductEdit] 查询失败:', res.error)
|
||||||
formData.value.id = data.id
|
uni.showToast({ title: '获取商品信息失败,或者无权限编辑', icon: 'none' })
|
||||||
formData.value.name = data.name || ''
|
return
|
||||||
formData.value.base_price = data.base_price || 0
|
}
|
||||||
formData.value.available_stock = data.available_stock || 0
|
|
||||||
formData.value.total_stock = data.total_stock || data.available_stock || 0
|
|
||||||
formData.value.status = data.status || 1
|
|
||||||
formData.value.main_image_url = data.main_image_url || ''
|
|
||||||
formData.value.image_urls = data.image_urls || []
|
|
||||||
formData.value.video_urls = data.video_urls || []
|
|
||||||
formData.value.tags = data.tags || []
|
|
||||||
|
|
||||||
if (data.attributes && typeof data.attributes === 'object') {
|
// .single() 返回时 data 可能是 UTSJSONObject 或包含一条记录的数组,兼容处理
|
||||||
const attrs = data.attributes as Record<string, any>
|
let row : UTSJSONObject | null = null
|
||||||
formData.value.attributes.unit = attrs['unit'] || '件'
|
if (res.data != null) {
|
||||||
}
|
const raw = res.data
|
||||||
|
if (Array.isArray(raw)) {
|
||||||
// Try to map category
|
const arr = raw as Array<UTSJSONObject>
|
||||||
formData.value.category_id = data.category_id as string || ''
|
if (arr.length > 0) row = arr[0] as UTSJSONObject
|
||||||
if (formData.value.category_id) {
|
} else {
|
||||||
const cat = categoryOptions.value.find((c: CategoryOption): boolean => c.id === formData.value.category_id)
|
row = raw as UTSJSONObject
|
||||||
categoryName.value = cat ? cat.name : '未知分类'
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
|
||||||
console.error('获取详情失败', e)
|
if (row == null) {
|
||||||
uni.showToast({ title: '获取商品信息失败,或者无权限编辑', icon: 'none' })
|
uni.showToast({ title: '未找到该商品', icon: 'none' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
formData.value.id = row.getString('id') ?? ''
|
||||||
|
formData.value.name = row.getString('name') ?? ''
|
||||||
|
formData.value.base_price = row.getNumber('base_price') ?? 0
|
||||||
|
formData.value.available_stock = row.getNumber('available_stock') ?? 0
|
||||||
|
formData.value.total_stock = row.getNumber('total_stock') ?? row.getNumber('available_stock') ?? 0
|
||||||
|
formData.value.status = row.getNumber('status') ?? 1
|
||||||
|
formData.value.main_image_url = row.getString('main_image_url') ?? ''
|
||||||
|
formData.value.category_id = row.getString('category_id') ?? ''
|
||||||
|
|
||||||
|
// image_urls / video_urls / tags 是 JSONB 数组,用 .get() 取原始值再强转
|
||||||
|
const imgRaw = row.get('image_urls')
|
||||||
|
if (imgRaw != null && Array.isArray(imgRaw)) {
|
||||||
|
formData.value.image_urls = imgRaw as string[]
|
||||||
|
}
|
||||||
|
const vidRaw = row.get('video_urls')
|
||||||
|
if (vidRaw != null && Array.isArray(vidRaw)) {
|
||||||
|
formData.value.video_urls = vidRaw as string[]
|
||||||
|
}
|
||||||
|
const tagsRaw = row.get('tags')
|
||||||
|
if (tagsRaw != null && Array.isArray(tagsRaw)) {
|
||||||
|
formData.value.tags = tagsRaw as string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
// attributes JSONB 是对象,用 getJSON 取
|
||||||
|
const attrsRaw = row.getJSON('attributes')
|
||||||
|
if (attrsRaw != null) {
|
||||||
|
formData.value.attributes.unit = attrsRaw.getString('unit') ?? '件'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 同步分类名称
|
||||||
|
if (formData.value.category_id) {
|
||||||
|
const cat = categoryOptions.value.find((c: CategoryOption): boolean => c.id === formData.value.category_id)
|
||||||
|
categoryName.value = cat ? cat.name : ''
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[ProductEdit] 加载成功,id=', formData.value.id, 'name=', formData.value.name)
|
||||||
|
} catch (e : any) {
|
||||||
|
console.error('[ProductEdit] 获取详情异常:', e)
|
||||||
|
uni.showToast({ title: '获取商品信息失败', icon: 'none' })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,54 @@ export async function fetchReportRows(reportId: string, params: any): Promise<Ar
|
|||||||
return Array.isArray(anyData) ? (anyData as Array<UTSJSONObject>) : ([] as Array<UTSJSONObject>)
|
return Array.isArray(anyData) ? (anyData as Array<UTSJSONObject>) : ([] as Array<UTSJSONObject>)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 供 data-detail.uvue 使用:包含 period 字段的报表信息
|
||||||
|
export type DataDetailReportInfo = {
|
||||||
|
id: string
|
||||||
|
title: string
|
||||||
|
description: string
|
||||||
|
definition: any
|
||||||
|
updated_at: string
|
||||||
|
period: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchDataDetailReportInfo(reportId: string): Promise<DataDetailReportInfo | null> {
|
||||||
|
const row = await rpcOrNull('rpc_data_detail_report_info', {
|
||||||
|
p_report_id: reportId
|
||||||
|
} as any)
|
||||||
|
|
||||||
|
if (row == null) return null
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: safeString(row.getAny?.('id')),
|
||||||
|
title: safeString(row.getAny?.('title')),
|
||||||
|
description: safeString(row.getAny?.('description')),
|
||||||
|
definition: row.getAny?.('definition'),
|
||||||
|
updated_at: safeString(row.getAny?.('updated_at')),
|
||||||
|
period: safeString(row.getAny?.('period'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 供 data-detail.uvue 使用:支持排序和分页参数
|
||||||
|
export async function fetchDataDetailRows(
|
||||||
|
reportId: string,
|
||||||
|
sortKey: string,
|
||||||
|
sortDir: string,
|
||||||
|
limit: number,
|
||||||
|
offset: number
|
||||||
|
): Promise<Array<UTSJSONObject>> {
|
||||||
|
const result = await rpcOrNull('rpc_data_detail_rows', {
|
||||||
|
p_report_id: reportId,
|
||||||
|
p_sort_key: sortKey,
|
||||||
|
p_sort_dir: sortDir,
|
||||||
|
p_limit: limit,
|
||||||
|
p_offset: offset
|
||||||
|
} as any)
|
||||||
|
|
||||||
|
if (result == null) return []
|
||||||
|
const anyData = result as any
|
||||||
|
return Array.isArray(anyData) ? (anyData as Array<UTSJSONObject>) : ([] as Array<UTSJSONObject>)
|
||||||
|
}
|
||||||
|
|
||||||
// 保留调用,但 RPC 是模拟数据
|
// 保留调用,但 RPC 是模拟数据
|
||||||
export async function fetchDrilldown(reportId: string, itemId: string): Promise<Array<UTSJSONObject>> {
|
export async function fetchDrilldown(reportId: string, itemId: string): Promise<Array<UTSJSONObject>> {
|
||||||
return await rpcOrEmptyArray('rpc_data_detail_drill_items', {
|
return await rpcOrEmptyArray('rpc_data_detail_drill_items', {
|
||||||
|
|||||||
@@ -13,18 +13,13 @@ export default defineConfig({
|
|||||||
// 生产环境构建配置
|
// 生产环境构建配置
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
output: {
|
output: {
|
||||||
// 手动拆包策略:将第三方库拆分成独立文件,减少首屏主包体积
|
// 将 vue + @dcloudio 合并到同一个 chunk,避免拆包后循环依赖导致 TDZ 错误
|
||||||
|
// ("Cannot access '?' before initialization" in vendor-vue chunk)
|
||||||
manualChunks(id) {
|
manualChunks(id) {
|
||||||
if (id.includes("node_modules")) {
|
if (id.includes("node_modules")) {
|
||||||
// 将 vue 相关依赖拆分到 vendor-vue
|
if (id.includes("vue") || id.includes("@dcloudio")) {
|
||||||
if (id.includes("vue")) {
|
|
||||||
return "vendor-vue";
|
return "vendor-vue";
|
||||||
}
|
}
|
||||||
// 将 dcloudio (uni-app) 相关依赖拆分
|
|
||||||
if (id.includes("@dcloudio")) {
|
|
||||||
return "vendor-uni";
|
|
||||||
}
|
|
||||||
// 其他 node_modules 依赖放入 vendor
|
|
||||||
return "vendor";
|
return "vendor";
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user