Initial commit of akmon project

This commit is contained in:
2026-01-20 08:04:15 +08:00
commit 77a2bab985
1309 changed files with 343305 additions and 0 deletions

View File

@@ -0,0 +1,364 @@
<template>
<slot name="default" :data="data" :current="localPageCurrent" :total="total" :hasmore="hasmore" :loading="loading" :error="error">
</slot>
</template>
<script setup lang="uts"> import { ref, watch, onMounted } from 'vue';
import supa from './aksupainstance.uts';
import { AkSupaSelectOptions } from './aksupa.uts'
import { AkReqResponse } from '@/uni_modules/ak-req/index.uts';
import { toUniError } from '@/utils/utils.uts';
const props = defineProps({
collection: {
type: String,
default: ''
},
filter: {
type: UTSJSONObject,
default: () => ({}),
},
field: {
type: String,
default: '*'
},
where: Object,
orderby: String,
pageData: {
type: String,
default: 'add',
},
pageCurrent: {
type: Number,
default: 1,
},
pageSize: {
type: Number,
default: 10,
},
/*"exact" | "planned" | "estimated" */
getcount: {
type: String,
default: '',
},
getone: {
type: Boolean,
default: false,
},
loadtime: {
type: String,
default: 'auto',
},
datafunc: Function,
// RPC 函数名,当使用 RPC 时collection 参数可以为空
rpc: {
type: String,
default: ''
},
// RPC 参数,用于传递给 RPC 函数的额外参数
params: {
type: UTSJSONObject,
default: () => ({})
}
});
const emit = defineEmits<{
(e : 'process-data', val : UTSJSONObject) : void,
(e : 'load', val : any[]) : void,
(e : 'error', val : any) : void
}>();
const data = ref<any[]>([]);
const loading = ref(false);
const error = ref<any>('');
const total = ref(0);
const hasmore = ref(true);
// Use local refs for pagination state to avoid mutating props directly
const localPageCurrent = ref(props.pageCurrent);
const localPageSize = ref(props.pageSize);
type Pagination = {
count : Number;
current : Number;
size : Number;
};
let pagination = { total: 0 };
let hasMoreData = true;
/**
* Unified data loading method
* @param {UTSJSONObject} opt
* opt.append Whether to append data
* opt.clear Whether to clear data
* opt.page Specify page number
*/ const fetchData = async (opt : UTSJSONObject) => {
loading.value = true;
error.value = '';
// 检查是否为 RPC 调用
const isRpcCall = props.rpc != null && props.rpc.length > 0;
// 只有在非 RPC 调用时才检查 collection
if (!isRpcCall && (props.collection == null || props.collection.trim() == '')) {
error.value = 'collection/table 不能为空';
loading.value = false;
return;
}
// RPC 调用时检查 rpc 参数
if (isRpcCall && (props.rpc == null || props.rpc.trim() == '')) {
error.value = 'rpc 函数名不能为空';
loading.value = false;
return;
}try {
// Platform-specific parameter extraction for UTSJSONObject compatibility
let append: boolean = false
let clear: boolean = false
let page: number = localPageCurrent.value
// #ifdef APP-ANDROID || APP-IOS
// Native platform: use UTSJSONObject methods
append = opt.getBoolean('append') ?? false
clear = opt.getBoolean('clear') ?? false
page = opt.getNumber('page') ?? localPageCurrent.value
// #endif
// #ifndef APP-ANDROID || APP-IOS
// Web platform: direct property access
append = (opt as any)['append'] as boolean ?? false
clear = (opt as any)['clear'] as boolean ?? false
page = (opt as any)['page'] as number ?? localPageCurrent.value
// #endif
// Update local pagination state
localPageCurrent.value = page;
localPageSize.value = props.pageSize;
// Build query options
let selectOptions : AkSupaSelectOptions = {
limit: localPageSize.value,
order: props.orderby,
columns: props.field,
};
if (props.getcount != null && props.getcount.length > 0) {
selectOptions['getcount'] = props.getcount;
} let result: any;
if (isRpcCall) {
// 支持rpc调用 - RPC方法只接受functionName和params两个参数
// 将filter、params和selectOptions合并为rpcParams
const rpcParams = new UTSJSONObject();
// 首先添加props.params中的参数
if (props.params != null) {
const paramsKeys = UTSJSONObject.keys(props.params);
for (let i = 0; i < paramsKeys.length; i++) {
const key = paramsKeys[i];
// #ifdef APP-ANDROID || APP-IOS
// Native platform: use UTSJSONObject methods
rpcParams.set(key, props.params.get(key));
// #endif
// #ifndef APP-ANDROID || APP-IOS
// Web platform: direct property access
rpcParams.set(key, (props.params as any)[key]);
// #endif
}
}
// 然后添加filter中的参数可能会覆盖params中的同名参数
if (props.filter != null) {
// Platform-specific filter handling for UTSJSONObject compatibility
const filterKeys = UTSJSONObject.keys(props.filter);
for (let i = 0; i < filterKeys.length; i++) {
const key = filterKeys[i];
// #ifdef APP-ANDROID || APP-IOS
// Native platform: use UTSJSONObject methods
rpcParams.set(key, props.filter.get(key));
// #endif
// #ifndef APP-ANDROID || APP-IOS
// Web platform: direct property access
rpcParams.set(key, (props.filter as any)[key]);
// #endif
}
}
// 添加分页和排序参数
if (selectOptions.limit != null) rpcParams.set('limit', selectOptions.limit);
if (selectOptions.order != null) rpcParams.set('order', selectOptions.order);
if (selectOptions.columns != null) rpcParams.set('columns', selectOptions.columns);
if (selectOptions.getcount != null) rpcParams.set('getcount', selectOptions.getcount);
result = await supa.rpc(props.rpc, rpcParams);
} else {
// Query data
result = await supa.select_uts(props.collection, props.filter, selectOptions);
}
// headers 判空
let countstring = '';
let headers:UTSJSONObject = result.headers != null ? result.headers : {};
if (headers != null) {
if (typeof headers.getString == 'function') {
let val = headers.getString('content-range');
if (val != null && typeof val == 'string') {
countstring = val;
}
} else if (headers['content-range'] != null) {
// 类型断言为 string否则转为 string
countstring = `${headers['content-range']}`;
}
}
console.log(countstring)
if (countstring != null && countstring != '') {
try {
const rangeParts = countstring.split('/')
if (rangeParts.length == 2) {
// 检查第二部分是否为数字(不是 '*'
const totalPart = rangeParts[1].trim()
if (totalPart !== '*' && !isNaN(parseInt(totalPart))) {
total.value = parseInt(totalPart)
console.log('Total count from header:', total.value)
pagination.total = total.value;
const rangeValues = rangeParts[0].split('-')
if (rangeValues.length == 2) {
const end = parseInt(rangeValues[1])
hasmore.value = end < total.value - 1
hasMoreData = hasmore.value
} } else {
// 当总数未知时(返回 *),设置默认值
console.log('Total count unknown (*), using default pagination logic')
total.value = 0
pagination.total = 0
// 根据当前返回的数据量判断是否还有更多数据
hasmore.value = Array.isArray(result.data) && (result.data as any[]).length >= localPageSize.value
hasMoreData = hasmore.value
}
}
} catch (e) {
console.error('Failed to parse content-range header', e)
} } else {
// 当没有 content-range header 时,根据返回的数据量判断分页
console.log('No content-range header, using data length for pagination')
total.value = 0
pagination.total = 0
// 如果返回的数据量等于页面大小,可能还有更多数据
hasmore.value = Array.isArray(result.data) && (result.data as any[]).length >= localPageSize.value
hasMoreData = hasmore.value
}
// data 判空
// UTS 平台下无 UTSArray.fromAny需兼容
let items: Array<any> = (Array.isArray(result.data)) ? result.data as Array<any> : Array<any>();
try {
// emit('process-data', items != null ? items : []);
console.log(result)
// Manually create UTSJSONObject from AkReqResponse for cross-platform compatibility
const prodata = new UTSJSONObject()
prodata.set('status', result.status)
prodata.set('data', result.data)
prodata.set('headers', result.headers)
prodata.set('error', result.error)
prodata.set('total', total.value)
emit('process-data', prodata);
} catch (e) {
console.error('emit process-data error', e);
}
if (clear) {
data.value = [];
}
if (append) {
if(Array.isArray(items)) {
data.value = ([] as any[]).concat(data.value, items);
}
} else {
data.value = items as any[];
}
pagination.total = total.value;
hasMoreData = Array.isArray(items) && items.length == localPageSize.value; } catch (err : any) {
// 使用标准化错误处理
const uniError = toUniError(err, 'An error occurred while fetching data')
try {
emit('error', uniError);
} catch (e) {
console.error('emit error event error', e);
}
} finally {
loading.value = false;
}
};
// 页码变更统一通过 fetchData
const handlePageChange = (page : number) => {
fetchData({ page: page, clear: true, append: false } as UTSJSONObject);
};
// 主动加载数据,支持清空
const loadData = (opt : UTSJSONObject) => {
console.log('loadData')
// Platform-specific parameter extraction for UTSJSONObject compatibility
let clear: boolean = true
// #ifdef APP-ANDROID || APP-IOS
// Native platform: use UTSJSONObject methods
clear = opt.getBoolean('clear') ?? true
// #endif
// #ifndef APP-ANDROID || APP-IOS
// Web platform: direct property access
clear = (opt as any)['clear'] as boolean ?? true
// #endif
fetchData({ clear, append: false, page: 1 } as UTSJSONObject);
};
const refresh = () => {
console.log('refresh')
const clear = true;
fetchData({ clear, append: false, page: 1 } as UTSJSONObject);
};
// 加载更多,自动追加
const loadMore = () => {
if (hasMoreData) {
const nextPage = props.pageCurrent + 1;
fetchData({ append: true, clear: false, page: nextPage } as UTSJSONObject);
}
};
if (props.loadtime === 'auto' || props.loadtime === 'onready') {
onMounted(() => {
fetchData({} as UTSJSONObject);
});
}
// watch(data, (newValue : any) => {
// emit('load', newValue);
// });
watch(error, (newValue : any) => {
if (newValue != null) {
emit('error', newValue);
}
});
// Documented exposed methods for parent components
/**
* Exposed methods:
* - loadData(opt?): Load data, optionally clearing previous data
* - loadMore(): Load next page and append
*/
defineExpose({ loadData,refresh, loadMore,hasmore,total });
</script>
<style scoped>
/* 添加您的样式 */
</style>