| @@ -36,10 +36,10 @@ export const userApi = { | |||||
| } | } | ||||
| export const inviteApi = { | export const inviteApi = { | ||||
| getConfig: (params) => request({ url: API.INTERFACE_INVITE_GET_CONFIG, method: 'POST', params }), | |||||
| getMy: (params) => request({ url: API.INTERFACE_INVITE_GET_MY, method: 'POST', params }), | |||||
| getList: (params) => request({ url: API.INTERFACE_INVITE_GET_LIST, method: 'POST', params }), | |||||
| getQrcode: (params) => request({ url: API.INTERFACE_INVITE_GET_QRCODE, method: 'POST', params }) | |||||
| getConfig: (params, options = {}) => request({ url: API.INTERFACE_INVITE_GET_CONFIG, method: 'POST', params, ...options }), | |||||
| getMy: (params, options = {}) => request({ url: API.INTERFACE_INVITE_GET_MY, method: 'POST', params, ...options }), | |||||
| getList: (params, options = {}) => request({ url: API.INTERFACE_INVITE_GET_LIST, method: 'POST', params, ...options }), | |||||
| getQrcode: (params, options = {}) => request({ url: API.INTERFACE_INVITE_GET_QRCODE, method: 'POST', params, ...options }) | |||||
| } | } | ||||
| export const commonApi = { | export const commonApi = { | ||||
| @@ -112,7 +112,7 @@ export const oldPhotoApi = { | |||||
| } | } | ||||
| export const toolApi = { | export const toolApi = { | ||||
| getMyWorkList: (params) => request({ url: API.INTERFACE_WORK_GET_LIST, method: 'POST', params }), | |||||
| getMyWorkList: (params, options = {}) => request({ url: API.INTERFACE_WORK_GET_LIST, method: 'POST', params, ...options }), | |||||
| getAppList: (params) => request({ url: API.INTERFACE_APP_GET_LIST, method: 'POST', params }), | getAppList: (params) => request({ url: API.INTERFACE_APP_GET_LIST, method: 'POST', params }), | ||||
| getAppDetail: (params) => request({ url: API.INTERFACE_APP_GET_DETAIL, method: 'POST', params }), | getAppDetail: (params) => request({ url: API.INTERFACE_APP_GET_DETAIL, method: 'POST', params }), | ||||
| getWorkList: (params) => request({ url: API.INTERFACE_ART_GET_LIST, method: 'POST', params }), | getWorkList: (params) => request({ url: API.INTERFACE_ART_GET_LIST, method: 'POST', params }), | ||||
| @@ -92,6 +92,7 @@ import { onShareAppMessage, onShow } from '@dcloudio/uni-app' | |||||
| import { inviteApi } from '@/api/index.js' | import { inviteApi } from '@/api/index.js' | ||||
| import { useUserStore } from '@/store/user.js' | import { useUserStore } from '@/store/user.js' | ||||
| import { useApp } from '@/utils/useApp.js' | import { useApp } from '@/utils/useApp.js' | ||||
| import { buildShareOptions } from '@/utils/share.js' | |||||
| const userStore = useUserStore() | const userStore = useUserStore() | ||||
| const { toast } = useApp() | const { toast } = useApp() | ||||
| @@ -113,10 +114,11 @@ onShow(() => { | |||||
| loadData() | loadData() | ||||
| }) | }) | ||||
| onShareAppMessage(() => ({ | |||||
| onShareAppMessage(() => buildShareOptions({ | |||||
| title: (share.value && share.value.title) || config.value.share_title || '邀请你体验 AI ONLINE', | title: (share.value && share.value.title) || config.value.share_title || '邀请你体验 AI ONLINE', | ||||
| path: sharePath.value, | path: sharePath.value, | ||||
| imageUrl: (share.value && share.value.image) || config.value.share_image || '' | |||||
| imageUrl: (share.value && share.value.image) || config.value.share_image || '', | |||||
| source: 'share_invite' | |||||
| })) | })) | ||||
| async function loadData() { | async function loadData() { | ||||
| @@ -32,6 +32,7 @@ import { onLoad, onShareAppMessage } from '@dcloudio/uni-app' | |||||
| import { inviteApi } from '@/api/index.js' | import { inviteApi } from '@/api/index.js' | ||||
| import { useUserStore } from '@/store/user.js' | import { useUserStore } from '@/store/user.js' | ||||
| import { useApp } from '@/utils/useApp.js' | import { useApp } from '@/utils/useApp.js' | ||||
| import { buildShareOptions } from '@/utils/share.js' | |||||
| const userStore = useUserStore() | const userStore = useUserStore() | ||||
| const { toast } = useApp() | const { toast } = useApp() | ||||
| @@ -47,10 +48,11 @@ onLoad((options = {}) => { | |||||
| loadConfig(options.scene || '') | loadConfig(options.scene || '') | ||||
| }) | }) | ||||
| onShareAppMessage(() => ({ | |||||
| onShareAppMessage(() => buildShareOptions({ | |||||
| title: config.value.share_title || '邀请你体验 AI ONLINE', | title: config.value.share_title || '邀请你体验 AI ONLINE', | ||||
| path: `/pages/invite/landing?invite_code=${inviteCode.value}`, | path: `/pages/invite/landing?invite_code=${inviteCode.value}`, | ||||
| imageUrl: config.value.share_image || '' | |||||
| imageUrl: config.value.share_image || '', | |||||
| source: 'share_invite_landing' | |||||
| })) | })) | ||||
| function parseInviteScene(scene) { | function parseInviteScene(scene) { | ||||
| @@ -107,14 +107,23 @@ | |||||
| <script setup> | <script setup> | ||||
| import { computed, ref } from 'vue' | import { computed, ref } from 'vue' | ||||
| import { onLoad, onShow, onUnload } from '@dcloudio/uni-app' | |||||
| import { onLoad, onShareAppMessage, onShow, onUnload } from '@dcloudio/uni-app' | |||||
| import { pointApi } from '@/api/index.js' | import { pointApi } from '@/api/index.js' | ||||
| import { useUserStore } from '@/store/user.js' | import { useUserStore } from '@/store/user.js' | ||||
| import { useApp } from '@/utils/useApp.js' | import { useApp } from '@/utils/useApp.js' | ||||
| import { buildShareOptions, showPageShareMenu } from '@/utils/share.js' | |||||
| const userStore = useUserStore() | const userStore = useUserStore() | ||||
| const { toast } = useApp() | const { toast } = useApp() | ||||
| const pageShare = { | |||||
| title: '获得点数|签到、邀请、看视频都能拿点数', | |||||
| source: 'share_earn_points' | |||||
| } | |||||
| onShow(() => showPageShareMenu()) | |||||
| onShareAppMessage(() => buildShareOptions(pageShare)) | |||||
| const loading = ref(true) | const loading = ref(true) | ||||
| const balance = ref(0) | const balance = ref(0) | ||||
| const rewarding = ref(false) | const rewarding = ref(false) | ||||
| @@ -237,15 +237,25 @@ | |||||
| <script setup> | <script setup> | ||||
| import { computed, reactive, ref } from 'vue' | import { computed, reactive, ref } from 'vue' | ||||
| import { onLoad, onShow } from '@dcloudio/uni-app' | |||||
| import { onLoad, onShareAppMessage, onShow } from '@dcloudio/uni-app' | |||||
| import dayjs from 'dayjs' | import dayjs from 'dayjs' | ||||
| import { billingApi, toolApi, userApi } from '@/api/index.js' | import { billingApi, toolApi, userApi } from '@/api/index.js' | ||||
| import { useUserStore } from '@/store/user.js' | import { useUserStore } from '@/store/user.js' | ||||
| import { useApp } from '@/utils/useApp.js' | import { useApp } from '@/utils/useApp.js' | ||||
| import { buildShareOptions, showPageShareMenu } from '@/utils/share.js' | |||||
| import LunarCalendar from '@/uni_modules/uview-plus/libs/util/calendar.js' | import LunarCalendar from '@/uni_modules/uview-plus/libs/util/calendar.js' | ||||
| const userStore = useUserStore() | const userStore = useUserStore() | ||||
| const { toast } = useApp() | const { toast } = useApp() | ||||
| const pageShare = { | |||||
| title: 'AI取名|给宝宝取一个有出处的好名字', | |||||
| source: 'share_ai_name' | |||||
| } | |||||
| onShow(() => showPageShareMenu()) | |||||
| onShareAppMessage(() => buildShareOptions(pageShare)) | |||||
| const appDetail = ref({}) | const appDetail = ref({}) | ||||
| const robotInfo = ref({}) | const robotInfo = ref({}) | ||||
| const todayDate = dayjs().format('YYYY-MM-DD') | const todayDate = dayjs().format('YYYY-MM-DD') | ||||
| @@ -181,16 +181,25 @@ | |||||
| <script setup> | <script setup> | ||||
| import { computed, reactive, ref } from 'vue' | import { computed, reactive, ref } from 'vue' | ||||
| import { onLoad, onShow } from '@dcloudio/uni-app' | |||||
| import { onLoad, onShareAppMessage, onShow } from '@dcloudio/uni-app' | |||||
| import { billingApi, commonApi, toolApi, userApi } from '@/api/index.js' | import { billingApi, commonApi, toolApi, userApi } from '@/api/index.js' | ||||
| import { useUserStore } from '@/store/user.js' | import { useUserStore } from '@/store/user.js' | ||||
| import { useApp } from '@/utils/useApp.js' | import { useApp } from '@/utils/useApp.js' | ||||
| import { uploadImage } from '@/utils/upload.js' | import { uploadImage } from '@/utils/upload.js' | ||||
| import { buildShareOptions, showPageShareMenu } from '@/utils/share.js' | |||||
| const userStore = useUserStore() | const userStore = useUserStore() | ||||
| const { toast } = useApp() | const { toast } = useApp() | ||||
| const ANIME_BILLING_MODEL = 'tencent_anime' | const ANIME_BILLING_MODEL = 'tencent_anime' | ||||
| const pageShare = { | |||||
| title: 'AI动漫头像|上传自拍生成你的专属头像', | |||||
| source: 'share_anime_avatar' | |||||
| } | |||||
| onShow(() => showPageShareMenu()) | |||||
| onShareAppMessage(() => buildShareOptions(pageShare)) | |||||
| const appDetail = ref({}) | const appDetail = ref({}) | ||||
| const demoList = ref([]) | const demoList = ref([]) | ||||
| const styleList = ref([{ value: '201', name: '默认', model: 2, pic: 'https://cdn.aionline.cc/ai/toolbox/home_card_avatar.png' }]) | const styleList = ref([{ value: '201', name: '默认', model: 2, pic: 'https://cdn.aionline.cc/ai/toolbox/home_card_avatar.png' }]) | ||||
| @@ -79,7 +79,7 @@ | |||||
| <script setup> | <script setup> | ||||
| import { computed, ref } from 'vue' | import { computed, ref } from 'vue' | ||||
| import { onLoad, onUnload } from '@dcloudio/uni-app' | |||||
| import { onLoad, onShow, onUnload } from '@dcloudio/uni-app' | |||||
| import dayjs from 'dayjs' | import dayjs from 'dayjs' | ||||
| import { toolApi } from '@/api/index.js' | import { toolApi } from '@/api/index.js' | ||||
| import { useApp } from '@/utils/useApp.js' | import { useApp } from '@/utils/useApp.js' | ||||
| @@ -87,15 +87,25 @@ import { useApp } from '@/utils/useApp.js' | |||||
| const { toast } = useApp() | const { toast } = useApp() | ||||
| const list = ref([]) | const list = ref([]) | ||||
| const page = ref(1) | const page = ref(1) | ||||
| const pageSize = 10 | |||||
| const total = ref(0) | const total = ref(0) | ||||
| const loading = ref(false) | const loading = ref(false) | ||||
| const refreshing = ref(false) | const refreshing = ref(false) | ||||
| let fetching = false | |||||
| let firstShow = true | |||||
| let timer = 0 | let timer = 0 | ||||
| const pendingCount = computed(() => list.value.filter((item) => Number(item.is_suc) === 0).length) | const pendingCount = computed(() => list.value.filter((item) => Number(item.is_suc) === 0).length) | ||||
| const successCount = computed(() => list.value.filter((item) => Number(item.is_suc) > 0).length) | const successCount = computed(() => list.value.filter((item) => Number(item.is_suc) > 0).length) | ||||
| onLoad(() => refresh()) | |||||
| onLoad(() => fetchList({ reset: true })) | |||||
| onShow(() => { | |||||
| if (firstShow) { | |||||
| firstShow = false | |||||
| return | |||||
| } | |||||
| refreshLoaded(true) | |||||
| }) | |||||
| onUnload(() => clearTimeout(timer)) | onUnload(() => clearTimeout(timer)) | ||||
| function formatTime(value) { | function formatTime(value) { | ||||
| @@ -103,46 +113,73 @@ function formatTime(value) { | |||||
| return dayjs(Number(value) * 1000).format('YYYY-MM-DD HH:mm') | return dayjs(Number(value) * 1000).format('YYYY-MM-DD HH:mm') | ||||
| } | } | ||||
| async function fetchList(reset = false) { | |||||
| if (loading.value) return | |||||
| loading.value = true | |||||
| async function fetchList(options = {}) { | |||||
| const { | |||||
| reset = false, | |||||
| silent = false, | |||||
| replace = reset, | |||||
| size = pageSize | |||||
| } = options | |||||
| let queryPage = options.pageNo || page.value | |||||
| if (fetching) { | |||||
| finishRefresh() | |||||
| return | |||||
| } | |||||
| fetching = true | |||||
| if (!silent) loading.value = true | |||||
| if (reset) { | |||||
| page.value = 1 | |||||
| queryPage = 1 | |||||
| } | |||||
| try { | try { | ||||
| const res = await toolApi.getAnimeList({ | const res = await toolApi.getAnimeList({ | ||||
| page_no: page.value, | |||||
| page_size: 10, | |||||
| page_no: queryPage, | |||||
| page_size: size, | |||||
| role: 2 | role: 2 | ||||
| }) | }) | ||||
| const rows = res.list || [] | const rows = res.list || [] | ||||
| total.value = Number((res.data && res.data.total) || rows.length) | total.value = Number((res.data && res.data.total) || rows.length) | ||||
| list.value = reset ? rows : list.value.concat(rows) | |||||
| list.value = replace ? rows : list.value.concat(rows) | |||||
| scheduleRefresh() | scheduleRefresh() | ||||
| } catch (e) { | } catch (e) { | ||||
| toast(e.msg || '历史记录加载失败') | |||||
| if (!silent) toast(e.msg || '历史记录加载失败') | |||||
| } finally { | } finally { | ||||
| fetching = false | |||||
| loading.value = false | loading.value = false | ||||
| refreshing.value = false | |||||
| finishRefresh() | |||||
| } | } | ||||
| } | } | ||||
| function scheduleRefresh() { | function scheduleRefresh() { | ||||
| clearTimeout(timer) | clearTimeout(timer) | ||||
| if (list.value.some((item) => Number(item.is_suc) === 0)) { | if (list.value.some((item) => Number(item.is_suc) === 0)) { | ||||
| timer = setTimeout(() => refreshSilent(), 2500) | |||||
| timer = setTimeout(() => refreshLoaded(true), 2500) | |||||
| } | } | ||||
| } | } | ||||
| function finishRefresh() { | |||||
| refreshing.value = false | |||||
| uni.stopPullDownRefresh() | |||||
| } | |||||
| function refresh() { | function refresh() { | ||||
| refreshing.value = true | refreshing.value = true | ||||
| refreshSilent() | |||||
| fetchList({ reset: true }) | |||||
| } | } | ||||
| function refreshSilent() { | |||||
| page.value = 1 | |||||
| fetchList(true) | |||||
| function refreshLoaded(silent = true) { | |||||
| const currentPage = Math.max(1, Number(page.value) || 1) | |||||
| fetchList({ | |||||
| pageNo: 1, | |||||
| size: currentPage * pageSize, | |||||
| replace: true, | |||||
| silent | |||||
| }) | |||||
| } | } | ||||
| function loadMore() { | function loadMore() { | ||||
| if (loading.value) return | |||||
| if (fetching) return | |||||
| if (list.value.length >= total.value) return | if (list.value.length >= total.value) return | ||||
| page.value += 1 | page.value += 1 | ||||
| fetchList() | fetchList() | ||||
| @@ -190,7 +227,7 @@ function deleteItem(item) { | |||||
| try { | try { | ||||
| await toolApi.deleteAnime({ chat_open_id: item.chat_open_id }) | await toolApi.deleteAnime({ chat_open_id: item.chat_open_id }) | ||||
| toast('删除成功') | toast('删除成功') | ||||
| refresh() | |||||
| refreshLoaded(true) | |||||
| } catch (e) { | } catch (e) { | ||||
| toast(e.msg || '删除失败') | toast(e.msg || '删除失败') | ||||
| } | } | ||||
| @@ -91,15 +91,25 @@ import { useApp } from '@/utils/useApp.js' | |||||
| const { toast } = useApp() | const { toast } = useApp() | ||||
| const list = ref([]) | const list = ref([]) | ||||
| const page = ref(1) | const page = ref(1) | ||||
| const pageSize = 10 | |||||
| const total = ref(0) | const total = ref(0) | ||||
| const loading = ref(false) | const loading = ref(false) | ||||
| const refreshing = ref(false) | const refreshing = ref(false) | ||||
| let fetching = false | |||||
| let firstShow = true | |||||
| let timer = 0 | let timer = 0 | ||||
| const pendingCount = computed(() => list.value.filter((item) => isPending(item)).length) | const pendingCount = computed(() => list.value.filter((item) => isPending(item)).length) | ||||
| const successCount = computed(() => list.value.filter((item) => Number(item.status) === 1).length) | const successCount = computed(() => list.value.filter((item) => Number(item.status) === 1).length) | ||||
| onShow(() => refreshSilent()) | |||||
| onShow(() => { | |||||
| if (firstShow) { | |||||
| firstShow = false | |||||
| fetchList({ reset: true }) | |||||
| return | |||||
| } | |||||
| refreshLoaded(true) | |||||
| }) | |||||
| onHide(() => clearTimer()) | onHide(() => clearTimer()) | ||||
| onUnload(() => clearTimer()) | onUnload(() => clearTimer()) | ||||
| @@ -112,22 +122,35 @@ function formatTime(value) { | |||||
| return dayjs(Number(value) * 1000).format('YYYY-MM-DD HH:mm') | return dayjs(Number(value) * 1000).format('YYYY-MM-DD HH:mm') | ||||
| } | } | ||||
| async function fetchList(reset = false) { | |||||
| if (loading.value) { | |||||
| async function fetchList(options = {}) { | |||||
| const { | |||||
| reset = false, | |||||
| silent = false, | |||||
| replace = reset, | |||||
| size = pageSize | |||||
| } = options | |||||
| let queryPage = options.pageNo || page.value | |||||
| if (fetching) { | |||||
| finishRefresh() | finishRefresh() | ||||
| return | return | ||||
| } | } | ||||
| loading.value = true | |||||
| if (reset) page.value = 1 | |||||
| fetching = true | |||||
| if (!silent) loading.value = true | |||||
| if (reset) { | |||||
| page.value = 1 | |||||
| queryPage = 1 | |||||
| } | |||||
| try { | try { | ||||
| const res = await idPhotoApi.getList({ page_no: page.value, page_size: 10 }) | |||||
| const res = await idPhotoApi.getList({ page_no: queryPage, page_size: size }) | |||||
| const rows = res.list || [] | const rows = res.list || [] | ||||
| total.value = Number((res.data && res.data.total) || rows.length) | total.value = Number((res.data && res.data.total) || rows.length) | ||||
| list.value = reset ? rows : list.value.concat(rows) | |||||
| list.value = replace ? rows : list.value.concat(rows) | |||||
| scheduleRefresh() | scheduleRefresh() | ||||
| } catch (e) { | } catch (e) { | ||||
| toast((e && e.msg) || '记录加载失败') | |||||
| if (!silent) toast((e && e.msg) || '记录加载失败') | |||||
| } finally { | } finally { | ||||
| fetching = false | |||||
| loading.value = false | loading.value = false | ||||
| finishRefresh() | finishRefresh() | ||||
| } | } | ||||
| @@ -136,7 +159,7 @@ async function fetchList(reset = false) { | |||||
| function scheduleRefresh() { | function scheduleRefresh() { | ||||
| clearTimer() | clearTimer() | ||||
| if (list.value.some((item) => isPending(item))) { | if (list.value.some((item) => isPending(item))) { | ||||
| timer = setTimeout(() => refreshSilent(), 3000) | |||||
| timer = setTimeout(() => refreshLoaded(true), 3000) | |||||
| } | } | ||||
| } | } | ||||
| @@ -152,16 +175,21 @@ function finishRefresh() { | |||||
| function refresh() { | function refresh() { | ||||
| refreshing.value = true | refreshing.value = true | ||||
| refreshSilent() | |||||
| fetchList({ reset: true }) | |||||
| } | } | ||||
| function refreshSilent() { | |||||
| page.value = 1 | |||||
| fetchList(true) | |||||
| function refreshLoaded(silent = true) { | |||||
| const currentPage = Math.max(1, Number(page.value) || 1) | |||||
| fetchList({ | |||||
| pageNo: 1, | |||||
| size: currentPage * pageSize, | |||||
| replace: true, | |||||
| silent | |||||
| }) | |||||
| } | } | ||||
| function loadMore() { | function loadMore() { | ||||
| if (loading.value || list.value.length >= total.value) return | |||||
| if (fetching || list.value.length >= total.value) return | |||||
| page.value += 1 | page.value += 1 | ||||
| fetchList() | fetchList() | ||||
| } | } | ||||
| @@ -211,7 +239,7 @@ function deleteItem(item) { | |||||
| try { | try { | ||||
| await idPhotoApi.delete({ photo_open_id: id }) | await idPhotoApi.delete({ photo_open_id: id }) | ||||
| toast('删除成功') | toast('删除成功') | ||||
| refreshSilent() | |||||
| refreshLoaded(true) | |||||
| } catch (e) { | } catch (e) { | ||||
| toast((e && e.msg) || '删除失败') | toast((e && e.msg) || '删除失败') | ||||
| } | } | ||||
| @@ -284,12 +284,21 @@ | |||||
| <script setup> | <script setup> | ||||
| import { computed, nextTick, reactive, ref } from 'vue' | import { computed, nextTick, reactive, ref } from 'vue' | ||||
| import { onLoad, onShow, onUnload } from '@dcloudio/uni-app' | |||||
| import { onLoad, onShareAppMessage, onShow, onUnload } from '@dcloudio/uni-app' | |||||
| import { idPhotoApi } from '@/api/index.js' | import { idPhotoApi } from '@/api/index.js' | ||||
| import { createIdPhoto } from '@/utils/upload.js' | import { createIdPhoto } from '@/utils/upload.js' | ||||
| import { guid } from '@/utils/util.js' | import { guid } from '@/utils/util.js' | ||||
| import { useUserStore } from '@/store/user.js' | import { useUserStore } from '@/store/user.js' | ||||
| import { useApp } from '@/utils/useApp.js' | import { useApp } from '@/utils/useApp.js' | ||||
| import { buildShareOptions, showPageShareMenu } from '@/utils/share.js' | |||||
| const pageShare = { | |||||
| title: 'AI证件照|一张自拍制作标准证件照', | |||||
| source: 'share_id_photo' | |||||
| } | |||||
| onShow(() => showPageShareMenu()) | |||||
| onShareAppMessage(() => buildShareOptions(pageShare)) | |||||
| const STANDARD_BACKGROUNDS = [ | const STANDARD_BACKGROUNDS = [ | ||||
| { key: 'blue', name: '常用蓝', color: '#438edb' }, | { key: 'blue', name: '常用蓝', color: '#438edb' }, | ||||
| @@ -38,7 +38,7 @@ | |||||
| <script setup> | <script setup> | ||||
| import { ref } from 'vue' | import { ref } from 'vue' | ||||
| import { onLoad, onUnload } from '@dcloudio/uni-app' | |||||
| import { onLoad, onShow, onUnload } from '@dcloudio/uni-app' | |||||
| import dayjs from 'dayjs' | import dayjs from 'dayjs' | ||||
| import { toolApi } from '@/api/index.js' | import { toolApi } from '@/api/index.js' | ||||
| import { useApp } from '@/utils/useApp.js' | import { useApp } from '@/utils/useApp.js' | ||||
| @@ -47,14 +47,25 @@ const { toast } = useApp() | |||||
| const robotId = ref('') | const robotId = ref('') | ||||
| const list = ref([]) | const list = ref([]) | ||||
| const page = ref(1) | const page = ref(1) | ||||
| const pageSize = 10 | |||||
| const total = ref(0) | const total = ref(0) | ||||
| const loading = ref(false) | const loading = ref(false) | ||||
| const refreshing = ref(false) | const refreshing = ref(false) | ||||
| let fetching = false | |||||
| let firstShow = true | |||||
| let timer = 0 | let timer = 0 | ||||
| onLoad((options) => { | onLoad((options) => { | ||||
| robotId.value = options.robot_id || '' | robotId.value = options.robot_id || '' | ||||
| refresh() | |||||
| fetchList({ reset: true }) | |||||
| }) | |||||
| onShow(() => { | |||||
| if (firstShow) { | |||||
| firstShow = false | |||||
| return | |||||
| } | |||||
| refreshLoaded(true) | |||||
| }) | }) | ||||
| onUnload(() => clearTimeout(timer)) | onUnload(() => clearTimeout(timer)) | ||||
| @@ -64,47 +75,74 @@ function formatTime(value) { | |||||
| return dayjs(Number(value) * 1000).format('YYYY-MM-DD HH:mm') | return dayjs(Number(value) * 1000).format('YYYY-MM-DD HH:mm') | ||||
| } | } | ||||
| async function fetchList(reset = false) { | |||||
| if (loading.value) return | |||||
| loading.value = true | |||||
| async function fetchList(options = {}) { | |||||
| const { | |||||
| reset = false, | |||||
| silent = false, | |||||
| replace = reset, | |||||
| size = pageSize | |||||
| } = options | |||||
| let queryPage = options.pageNo || page.value | |||||
| if (fetching) { | |||||
| finishRefresh() | |||||
| return | |||||
| } | |||||
| fetching = true | |||||
| if (!silent) loading.value = true | |||||
| if (reset) { | |||||
| page.value = 1 | |||||
| queryPage = 1 | |||||
| } | |||||
| try { | try { | ||||
| const res = await toolApi.getNameList({ | const res = await toolApi.getNameList({ | ||||
| page_no: page.value, | |||||
| page_size: 10, | |||||
| page_no: queryPage, | |||||
| page_size: size, | |||||
| robot_open_id: robotId.value, | robot_open_id: robotId.value, | ||||
| role: 2 | role: 2 | ||||
| }) | }) | ||||
| const rows = res.list || [] | const rows = res.list || [] | ||||
| total.value = Number((res.data && res.data.total) || rows.length) | total.value = Number((res.data && res.data.total) || rows.length) | ||||
| list.value = reset ? rows : list.value.concat(rows) | |||||
| list.value = replace ? rows : list.value.concat(rows) | |||||
| scheduleRefresh() | scheduleRefresh() | ||||
| } catch (e) { | } catch (e) { | ||||
| toast(e.msg || '历史记录加载失败') | |||||
| if (!silent) toast(e.msg || '历史记录加载失败') | |||||
| } finally { | } finally { | ||||
| fetching = false | |||||
| loading.value = false | loading.value = false | ||||
| refreshing.value = false | |||||
| finishRefresh() | |||||
| } | } | ||||
| } | } | ||||
| function scheduleRefresh() { | function scheduleRefresh() { | ||||
| clearTimeout(timer) | clearTimeout(timer) | ||||
| if (list.value.some((item) => Number(item.is_suc) === 0)) { | if (list.value.some((item) => Number(item.is_suc) === 0)) { | ||||
| timer = setTimeout(() => refreshSilent(), 2500) | |||||
| timer = setTimeout(() => refreshLoaded(true), 2500) | |||||
| } | } | ||||
| } | } | ||||
| function finishRefresh() { | |||||
| refreshing.value = false | |||||
| uni.stopPullDownRefresh() | |||||
| } | |||||
| function refresh() { | function refresh() { | ||||
| refreshing.value = true | refreshing.value = true | ||||
| refreshSilent() | |||||
| fetchList({ reset: true }) | |||||
| } | } | ||||
| function refreshSilent() { | |||||
| page.value = 1 | |||||
| fetchList(true) | |||||
| function refreshLoaded(silent = true) { | |||||
| const currentPage = Math.max(1, Number(page.value) || 1) | |||||
| fetchList({ | |||||
| pageNo: 1, | |||||
| size: currentPage * pageSize, | |||||
| replace: true, | |||||
| silent | |||||
| }) | |||||
| } | } | ||||
| function loadMore() { | function loadMore() { | ||||
| if (loading.value || list.value.length >= total.value) return | |||||
| if (fetching || list.value.length >= total.value) return | |||||
| page.value += 1 | page.value += 1 | ||||
| fetchList() | fetchList() | ||||
| } | } | ||||
| @@ -224,7 +262,7 @@ function deleteItem(item) { | |||||
| try { | try { | ||||
| await toolApi.deleteName({ chat_open_id: item.chat_open_id }) | await toolApi.deleteName({ chat_open_id: item.chat_open_id }) | ||||
| toast('删除成功') | toast('删除成功') | ||||
| refresh() | |||||
| refreshLoaded(true) | |||||
| } catch (e) { | } catch (e) { | ||||
| toast(e.msg || '删除失败') | toast(e.msg || '删除失败') | ||||
| } | } | ||||
| @@ -78,7 +78,7 @@ | |||||
| <script setup> | <script setup> | ||||
| import { computed, ref } from 'vue' | import { computed, ref } from 'vue' | ||||
| import { onLoad, onUnload } from '@dcloudio/uni-app' | |||||
| import { onLoad, onShow, onUnload } from '@dcloudio/uni-app' | |||||
| import dayjs from 'dayjs' | import dayjs from 'dayjs' | ||||
| import { oldPhotoApi } from '@/api/index.js' | import { oldPhotoApi } from '@/api/index.js' | ||||
| import { useApp } from '@/utils/useApp.js' | import { useApp } from '@/utils/useApp.js' | ||||
| @@ -86,15 +86,25 @@ import { useApp } from '@/utils/useApp.js' | |||||
| const { toast } = useApp() | const { toast } = useApp() | ||||
| const list = ref([]) | const list = ref([]) | ||||
| const page = ref(1) | const page = ref(1) | ||||
| const pageSize = 10 | |||||
| const total = ref(0) | const total = ref(0) | ||||
| const loading = ref(false) | const loading = ref(false) | ||||
| const refreshing = ref(false) | const refreshing = ref(false) | ||||
| let fetching = false | |||||
| let firstShow = true | |||||
| let timer = 0 | let timer = 0 | ||||
| const pendingCount = computed(() => list.value.filter((item) => isPending(item)).length) | const pendingCount = computed(() => list.value.filter((item) => isPending(item)).length) | ||||
| const successCount = computed(() => list.value.filter((item) => Number(item.status) === 30).length) | const successCount = computed(() => list.value.filter((item) => Number(item.status) === 30).length) | ||||
| onLoad(() => refresh()) | |||||
| onLoad(() => fetchList({ reset: true })) | |||||
| onShow(() => { | |||||
| if (firstShow) { | |||||
| firstShow = false | |||||
| return | |||||
| } | |||||
| refreshLoaded(true) | |||||
| }) | |||||
| onUnload(() => clearTimeout(timer)) | onUnload(() => clearTimeout(timer)) | ||||
| function isPending(item) { | function isPending(item) { | ||||
| @@ -106,20 +116,37 @@ function formatTime(value) { | |||||
| return dayjs(Number(value) * 1000).format('YYYY-MM-DD HH:mm') | return dayjs(Number(value) * 1000).format('YYYY-MM-DD HH:mm') | ||||
| } | } | ||||
| async function fetchList(reset = false) { | |||||
| if (loading.value) return | |||||
| loading.value = true | |||||
| async function fetchList(options = {}) { | |||||
| const { | |||||
| reset = false, | |||||
| silent = false, | |||||
| replace = reset, | |||||
| size = pageSize | |||||
| } = options | |||||
| let queryPage = options.pageNo || page.value | |||||
| if (fetching) { | |||||
| finishRefresh() | |||||
| return | |||||
| } | |||||
| fetching = true | |||||
| if (!silent) loading.value = true | |||||
| if (reset) { | |||||
| page.value = 1 | |||||
| queryPage = 1 | |||||
| } | |||||
| try { | try { | ||||
| const res = await oldPhotoApi.getList({ page_no: page.value, page_size: 10 }) | |||||
| const res = await oldPhotoApi.getList({ page_no: queryPage, page_size: size }) | |||||
| const rows = res.list || [] | const rows = res.list || [] | ||||
| total.value = Number((res.data && res.data.total) || rows.length) | total.value = Number((res.data && res.data.total) || rows.length) | ||||
| list.value = reset ? rows : list.value.concat(rows) | |||||
| list.value = replace ? rows : list.value.concat(rows) | |||||
| scheduleRefresh() | scheduleRefresh() | ||||
| } catch (e) { | } catch (e) { | ||||
| toast((e && e.msg) || '历史记录加载失败') | |||||
| if (!silent) toast((e && e.msg) || '历史记录加载失败') | |||||
| } finally { | } finally { | ||||
| fetching = false | |||||
| loading.value = false | loading.value = false | ||||
| refreshing.value = false | |||||
| finishRefresh() | |||||
| } | } | ||||
| } | } | ||||
| @@ -127,22 +154,32 @@ async function fetchList(reset = false) { | |||||
| function scheduleRefresh() { | function scheduleRefresh() { | ||||
| clearTimeout(timer) | clearTimeout(timer) | ||||
| if (list.value.some((item) => isPending(item))) { | if (list.value.some((item) => isPending(item))) { | ||||
| timer = setTimeout(() => refreshSilent(), 3000) | |||||
| timer = setTimeout(() => refreshLoaded(true), 3000) | |||||
| } | } | ||||
| } | } | ||||
| function finishRefresh() { | |||||
| refreshing.value = false | |||||
| uni.stopPullDownRefresh() | |||||
| } | |||||
| function refresh() { | function refresh() { | ||||
| refreshing.value = true | refreshing.value = true | ||||
| refreshSilent() | |||||
| fetchList({ reset: true }) | |||||
| } | } | ||||
| function refreshSilent() { | |||||
| page.value = 1 | |||||
| fetchList(true) | |||||
| function refreshLoaded(silent = true) { | |||||
| const currentPage = Math.max(1, Number(page.value) || 1) | |||||
| fetchList({ | |||||
| pageNo: 1, | |||||
| size: currentPage * pageSize, | |||||
| replace: true, | |||||
| silent | |||||
| }) | |||||
| } | } | ||||
| function loadMore() { | function loadMore() { | ||||
| if (loading.value) return | |||||
| if (fetching) return | |||||
| if (list.value.length >= total.value) return | if (list.value.length >= total.value) return | ||||
| page.value += 1 | page.value += 1 | ||||
| fetchList() | fetchList() | ||||
| @@ -185,7 +222,7 @@ function deleteItem(item) { | |||||
| try { | try { | ||||
| await oldPhotoApi.delete({ photo_open_id: item.photo_open_id }) | await oldPhotoApi.delete({ photo_open_id: item.photo_open_id }) | ||||
| toast('删除成功') | toast('删除成功') | ||||
| refresh() | |||||
| refreshLoaded(true) | |||||
| } catch (e) { | } catch (e) { | ||||
| toast((e && e.msg) || '删除失败') | toast((e && e.msg) || '删除失败') | ||||
| } | } | ||||
| @@ -171,16 +171,25 @@ | |||||
| <script setup> | <script setup> | ||||
| import { computed, ref } from 'vue' | import { computed, ref } from 'vue' | ||||
| import { onLoad, onShow, onUnload } from '@dcloudio/uni-app' | |||||
| import { onLoad, onShareAppMessage, onShow, onUnload } from '@dcloudio/uni-app' | |||||
| import { commonApi, oldPhotoApi, userApi } from '@/api/index.js' | import { commonApi, oldPhotoApi, userApi } from '@/api/index.js' | ||||
| import { createOldPhoto } from '@/utils/upload.js' | import { createOldPhoto } from '@/utils/upload.js' | ||||
| import { guid } from '@/utils/util.js' | import { guid } from '@/utils/util.js' | ||||
| import { useUserStore } from '@/store/user.js' | import { useUserStore } from '@/store/user.js' | ||||
| import { useApp } from '@/utils/useApp.js' | import { useApp } from '@/utils/useApp.js' | ||||
| import { buildShareOptions, showPageShareMenu } from '@/utils/share.js' | |||||
| const userStore = useUserStore() | const userStore = useUserStore() | ||||
| const { toast } = useApp() | const { toast } = useApp() | ||||
| const pageShare = { | |||||
| title: '老照片修复上色|让旧照片焕然一新', | |||||
| source: 'share_old_photo' | |||||
| } | |||||
| onShow(() => showPageShareMenu()) | |||||
| onShareAppMessage(() => buildShareOptions(pageShare)) | |||||
| // 轮询:混元未买并发,排队 + 生成通常几十秒,最长按后端 20 分钟超时来兜 | // 轮询:混元未买并发,排队 + 生成通常几十秒,最长按后端 20 分钟超时来兜 | ||||
| const POLL_INTERVAL = 3000 | const POLL_INTERVAL = 3000 | ||||
| const POLL_LIMIT = 5 * 60 * 1000 | const POLL_LIMIT = 5 * 60 * 1000 | ||||
| @@ -79,7 +79,7 @@ | |||||
| <script setup> | <script setup> | ||||
| import { computed, ref } from 'vue' | import { computed, ref } from 'vue' | ||||
| import { onLoad, onUnload } from '@dcloudio/uni-app' | |||||
| import { onLoad, onShow, onUnload } from '@dcloudio/uni-app' | |||||
| import dayjs from 'dayjs' | import dayjs from 'dayjs' | ||||
| import { portraitApi } from '@/api/index.js' | import { portraitApi } from '@/api/index.js' | ||||
| import { useApp } from '@/utils/useApp.js' | import { useApp } from '@/utils/useApp.js' | ||||
| @@ -87,9 +87,12 @@ import { useApp } from '@/utils/useApp.js' | |||||
| const { toast } = useApp() | const { toast } = useApp() | ||||
| const list = ref([]) | const list = ref([]) | ||||
| const page = ref(1) | const page = ref(1) | ||||
| const pageSize = 10 | |||||
| const total = ref(0) | const total = ref(0) | ||||
| const loading = ref(false) | const loading = ref(false) | ||||
| const refreshing = ref(false) | const refreshing = ref(false) | ||||
| let fetching = false | |||||
| let firstShow = true | |||||
| let timer = 0 | let timer = 0 | ||||
| const pendingCount = computed(() => list.value.filter((item) => isPending(item)).length) | const pendingCount = computed(() => list.value.filter((item) => isPending(item)).length) | ||||
| @@ -97,7 +100,14 @@ const photoCount = computed(() => | |||||
| list.value.reduce((sum, item) => sum + Number(item.success_count || 0), 0) | list.value.reduce((sum, item) => sum + Number(item.success_count || 0), 0) | ||||
| ) | ) | ||||
| onLoad(() => refresh()) | |||||
| onLoad(() => fetchList({ reset: true })) | |||||
| onShow(() => { | |||||
| if (firstShow) { | |||||
| firstShow = false | |||||
| return | |||||
| } | |||||
| refreshLoaded(true) | |||||
| }) | |||||
| onUnload(() => clearTimeout(timer)) | onUnload(() => clearTimeout(timer)) | ||||
| function isPending(item) { | function isPending(item) { | ||||
| @@ -109,20 +119,37 @@ function formatTime(value) { | |||||
| return dayjs(Number(value) * 1000).format('YYYY-MM-DD HH:mm') | return dayjs(Number(value) * 1000).format('YYYY-MM-DD HH:mm') | ||||
| } | } | ||||
| async function fetchList(reset = false) { | |||||
| if (loading.value) return | |||||
| loading.value = true | |||||
| async function fetchList(options = {}) { | |||||
| const { | |||||
| reset = false, | |||||
| silent = false, | |||||
| replace = reset, | |||||
| size = pageSize | |||||
| } = options | |||||
| let queryPage = options.pageNo || page.value | |||||
| if (fetching) { | |||||
| finishRefresh() | |||||
| return | |||||
| } | |||||
| fetching = true | |||||
| if (!silent) loading.value = true | |||||
| if (reset) { | |||||
| page.value = 1 | |||||
| queryPage = 1 | |||||
| } | |||||
| try { | try { | ||||
| const res = await portraitApi.getList({ page_no: page.value, page_size: 10 }) | |||||
| const res = await portraitApi.getList({ page_no: queryPage, page_size: size }) | |||||
| const rows = res.list || [] | const rows = res.list || [] | ||||
| total.value = Number((res.data && res.data.total) || rows.length) | total.value = Number((res.data && res.data.total) || rows.length) | ||||
| list.value = reset ? rows : list.value.concat(rows) | |||||
| list.value = replace ? rows : list.value.concat(rows) | |||||
| scheduleRefresh() | scheduleRefresh() | ||||
| } catch (e) { | } catch (e) { | ||||
| toast((e && e.msg) || '记录加载失败') | |||||
| if (!silent) toast((e && e.msg) || '记录加载失败') | |||||
| } finally { | } finally { | ||||
| fetching = false | |||||
| loading.value = false | loading.value = false | ||||
| refreshing.value = false | |||||
| finishRefresh() | |||||
| } | } | ||||
| } | } | ||||
| @@ -130,22 +157,32 @@ async function fetchList(reset = false) { | |||||
| function scheduleRefresh() { | function scheduleRefresh() { | ||||
| clearTimeout(timer) | clearTimeout(timer) | ||||
| if (list.value.some((item) => isPending(item))) { | if (list.value.some((item) => isPending(item))) { | ||||
| timer = setTimeout(() => refreshSilent(), 3000) | |||||
| timer = setTimeout(() => refreshLoaded(true), 3000) | |||||
| } | } | ||||
| } | } | ||||
| function finishRefresh() { | |||||
| refreshing.value = false | |||||
| uni.stopPullDownRefresh() | |||||
| } | |||||
| function refresh() { | function refresh() { | ||||
| refreshing.value = true | refreshing.value = true | ||||
| refreshSilent() | |||||
| fetchList({ reset: true }) | |||||
| } | } | ||||
| function refreshSilent() { | |||||
| page.value = 1 | |||||
| fetchList(true) | |||||
| function refreshLoaded(silent = true) { | |||||
| const currentPage = Math.max(1, Number(page.value) || 1) | |||||
| fetchList({ | |||||
| pageNo: 1, | |||||
| size: currentPage * pageSize, | |||||
| replace: true, | |||||
| silent | |||||
| }) | |||||
| } | } | ||||
| function loadMore() { | function loadMore() { | ||||
| if (loading.value) return | |||||
| if (fetching) return | |||||
| if (list.value.length >= total.value) return | if (list.value.length >= total.value) return | ||||
| page.value += 1 | page.value += 1 | ||||
| fetchList() | fetchList() | ||||
| @@ -187,7 +224,7 @@ function deleteItem(item) { | |||||
| try { | try { | ||||
| await portraitApi.delete({ photo_open_id: item.photo_open_id }) | await portraitApi.delete({ photo_open_id: item.photo_open_id }) | ||||
| toast('删除成功') | toast('删除成功') | ||||
| refresh() | |||||
| refreshLoaded(true) | |||||
| } catch (e) { | } catch (e) { | ||||
| toast((e && e.msg) || '删除失败') | toast((e && e.msg) || '删除失败') | ||||
| } | } | ||||
| @@ -270,16 +270,25 @@ | |||||
| <script setup> | <script setup> | ||||
| import { computed, reactive, ref, watch } from 'vue' | import { computed, reactive, ref, watch } from 'vue' | ||||
| import { onLoad, onShow, onUnload } from '@dcloudio/uni-app' | |||||
| import { onLoad, onShareAppMessage, onShow, onUnload } from '@dcloudio/uni-app' | |||||
| import { commonApi, portraitApi, userApi } from '@/api/index.js' | import { commonApi, portraitApi, userApi } from '@/api/index.js' | ||||
| import { createPortrait } from '@/utils/upload.js' | import { createPortrait } from '@/utils/upload.js' | ||||
| import { guid } from '@/utils/util.js' | import { guid } from '@/utils/util.js' | ||||
| import { useUserStore } from '@/store/user.js' | import { useUserStore } from '@/store/user.js' | ||||
| import { useApp } from '@/utils/useApp.js' | import { useApp } from '@/utils/useApp.js' | ||||
| import { buildShareOptions, showPageShareMenu } from '@/utils/share.js' | |||||
| const userStore = useUserStore() | const userStore = useUserStore() | ||||
| const { toast } = useApp() | const { toast } = useApp() | ||||
| const pageShare = { | |||||
| title: 'AI形象照|职业照、艺术写真一键生成', | |||||
| source: 'share_portrait_photo' | |||||
| } | |||||
| onShow(() => showPageShareMenu()) | |||||
| onShareAppMessage(() => buildShareOptions(pageShare)) | |||||
| const POLL_INTERVAL = 3000 | const POLL_INTERVAL = 3000 | ||||
| const POLL_LIMIT = 8 * 60 * 1000 | const POLL_LIMIT = 8 * 60 * 1000 | ||||
| @@ -49,15 +49,24 @@ | |||||
| <script setup> | <script setup> | ||||
| import { ref } from 'vue' | import { ref } from 'vue' | ||||
| import { onLoad } from '@dcloudio/uni-app' | |||||
| import { onLoad, onShareAppMessage, onShow } from '@dcloudio/uni-app' | |||||
| import { commonApi } from '@/api/index.js' | import { commonApi } from '@/api/index.js' | ||||
| import { useApp } from '@/utils/useApp.js' | import { useApp } from '@/utils/useApp.js' | ||||
| import { buildShareOptions, showPageShareMenu } from '@/utils/share.js' | |||||
| const { toast } = useApp() | const { toast } = useApp() | ||||
| const refreshing = ref(false) | const refreshing = ref(false) | ||||
| const bannerIndex = ref(0) | const bannerIndex = ref(0) | ||||
| const AI_CDN_BASE = 'https://cdn.aionline.cc/ai' | const AI_CDN_BASE = 'https://cdn.aionline.cc/ai' | ||||
| const pageShare = { | |||||
| title: 'AI在线|开箱即用的 AI 工具箱', | |||||
| source: 'share_home' | |||||
| } | |||||
| onShow(() => showPageShareMenu()) | |||||
| onShareAppMessage(() => buildShareOptions(pageShare)) | |||||
| const defaultBanners = [] | const defaultBanners = [] | ||||
| const defaultRecommends = [ | const defaultRecommends = [ | ||||
| @@ -122,6 +122,7 @@ const refreshing = ref(false) | |||||
| const counts = ref({ all: 0, ainame: 0, anime: 0, image: 0, photo: 0, oldphoto: 0, portrait: 0 }) | const counts = ref({ all: 0, ainame: 0, anime: 0, image: 0, photo: 0, oldphoto: 0, portrait: 0 }) | ||||
| // 类型标签栏的横向滚动位置 | // 类型标签栏的横向滚动位置 | ||||
| const tabsScrollLeft = ref(0) | const tabsScrollLeft = ref(0) | ||||
| let fetching = false | |||||
| let timer = 0 | let timer = 0 | ||||
| const tabs = computed(() => [ | const tabs = computed(() => [ | ||||
| @@ -135,11 +136,17 @@ const tabs = computed(() => [ | |||||
| onShow(() => { | onShow(() => { | ||||
| const preferredTab = uni.getStorageSync('works_active_tab') | const preferredTab = uni.getStorageSync('works_active_tab') | ||||
| let needReset = false | |||||
| if (tabs.value.some((item) => item.key === preferredTab)) { | if (tabs.value.some((item) => item.key === preferredTab)) { | ||||
| needReset = activeTab.value !== preferredTab | |||||
| activeTab.value = preferredTab | activeTab.value = preferredTab | ||||
| uni.removeStorageSync('works_active_tab') | uni.removeStorageSync('works_active_tab') | ||||
| } | } | ||||
| refreshSilent() | |||||
| if (needReset) { | |||||
| fetchList({ reset: true }) | |||||
| } else { | |||||
| refreshLoaded(list.value.length > 0) | |||||
| } | |||||
| // 从其它页面带着类型进来时,把该类型滚到可视区中间(等渲染完成再量位置) | // 从其它页面带着类型进来时,把该类型滚到可视区中间(等渲染完成再量位置) | ||||
| nextTick(() => centerTab(activeTab.value)) | nextTick(() => centerTab(activeTab.value)) | ||||
| }) | }) | ||||
| @@ -152,7 +159,15 @@ function formatTime(value) { | |||||
| return dayjs(Number(value) * 1000).format('YYYY-MM-DD HH:mm') | return dayjs(Number(value) * 1000).format('YYYY-MM-DD HH:mm') | ||||
| } | } | ||||
| async function fetchList(reset = false, silent = false) { | |||||
| async function fetchList(options = {}) { | |||||
| const { | |||||
| reset = false, | |||||
| silent = false, | |||||
| replace = reset, | |||||
| size = pageSize | |||||
| } = options | |||||
| let queryPage = options.pageNo || page.value | |||||
| if (!userStore.isLogin) { | if (!userStore.isLogin) { | ||||
| list.value = [] | list.value = [] | ||||
| total.value = 0 | total.value = 0 | ||||
| @@ -160,27 +175,48 @@ async function fetchList(reset = false, silent = false) { | |||||
| uni.stopPullDownRefresh() | uni.stopPullDownRefresh() | ||||
| return | return | ||||
| } | } | ||||
| if (loading.value) { | |||||
| if (fetching) { | |||||
| if (refreshing.value) uni.stopPullDownRefresh() | if (refreshing.value) uni.stopPullDownRefresh() | ||||
| return | return | ||||
| } | } | ||||
| loading.value = true | |||||
| if (reset) page.value = 1 | |||||
| fetching = true | |||||
| if (!silent) loading.value = true | |||||
| if (reset) { | |||||
| page.value = 1 | |||||
| queryPage = 1 | |||||
| } | |||||
| try { | try { | ||||
| const res = await toolApi.getMyWorkList({ | const res = await toolApi.getMyWorkList({ | ||||
| type: activeTab.value, | type: activeTab.value, | ||||
| page_no: page.value, | |||||
| page_size: pageSize | |||||
| }) | |||||
| page_no: queryPage, | |||||
| page_size: size | |||||
| }, { authSilent: true }) | |||||
| const rows = res.list || [] | const rows = res.list || [] | ||||
| const data = res.data || {} | const data = res.data || {} | ||||
| list.value = reset ? rows : list.value.concat(rows) | |||||
| if (res.authExpired || data.authExpired) { | |||||
| userStore.clear() | |||||
| list.value = [] | |||||
| total.value = 0 | |||||
| counts.value = normalizeCounts({}) | |||||
| clearTimer() | |||||
| return | |||||
| } | |||||
| list.value = replace ? rows : list.value.concat(rows) | |||||
| total.value = Number(data.total || rows.length) | total.value = Number(data.total || rows.length) | ||||
| counts.value = normalizeCounts(data.counts || {}) | counts.value = normalizeCounts(data.counts || {}) | ||||
| scheduleRefresh() | scheduleRefresh() | ||||
| } catch (e) { | } catch (e) { | ||||
| if (Number(e?.code) === 1009) { | |||||
| userStore.clear() | |||||
| list.value = [] | |||||
| total.value = 0 | |||||
| counts.value = normalizeCounts({}) | |||||
| clearTimer() | |||||
| return | |||||
| } | |||||
| if (!silent) toast(e.msg || '作品加载失败') | if (!silent) toast(e.msg || '作品加载失败') | ||||
| } finally { | } finally { | ||||
| fetching = false | |||||
| loading.value = false | loading.value = false | ||||
| refreshing.value = false | refreshing.value = false | ||||
| uni.stopPullDownRefresh() | uni.stopPullDownRefresh() | ||||
| @@ -199,8 +235,14 @@ function normalizeCounts(data = {}) { | |||||
| } | } | ||||
| } | } | ||||
| function refreshSilent(silent = false) { | |||||
| fetchList(true, silent) | |||||
| function refreshLoaded(silent = true) { | |||||
| const currentPage = Math.max(1, Number(page.value) || 1) | |||||
| fetchList({ | |||||
| pageNo: 1, | |||||
| size: currentPage * pageSize, | |||||
| replace: true, | |||||
| silent | |||||
| }) | |||||
| } | } | ||||
| function onRefresh() { | function onRefresh() { | ||||
| @@ -213,11 +255,11 @@ function onRefresh() { | |||||
| }) | }) | ||||
| return | return | ||||
| } | } | ||||
| refreshSilent() | |||||
| fetchList({ reset: true }) | |||||
| } | } | ||||
| function loadMore() { | function loadMore() { | ||||
| if (loading.value || list.value.length >= total.value) return | |||||
| if (fetching || list.value.length >= total.value) return | |||||
| page.value += 1 | page.value += 1 | ||||
| fetchList() | fetchList() | ||||
| } | } | ||||
| @@ -226,7 +268,7 @@ function changeTab(key) { | |||||
| centerTab(key) | centerTab(key) | ||||
| if (activeTab.value === key) return | if (activeTab.value === key) return | ||||
| activeTab.value = key | activeTab.value = key | ||||
| refreshSilent() | |||||
| fetchList({ reset: true }) | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -255,7 +297,7 @@ function centerTab(key) { | |||||
| function scheduleRefresh() { | function scheduleRefresh() { | ||||
| clearTimer() | clearTimer() | ||||
| if (list.value.some((item) => Number(item.status) === 0)) { | if (list.value.some((item) => Number(item.status) === 0)) { | ||||
| timer = setTimeout(() => refreshSilent(true), 3000) | |||||
| timer = setTimeout(() => refreshLoaded(true), 3000) | |||||
| } | } | ||||
| } | } | ||||
| @@ -9,10 +9,14 @@ const DEFAULT_ENV = 'pro' | |||||
| const ENV_MAP = { | const ENV_MAP = { | ||||
| dev: { | dev: { | ||||
| BASE_URL: 'http://192.168.31.168:16888', | |||||
| M_URL: 'https://m.ai.jiefuku.com', | |||||
| PC_URL: 'https://ai.jiefuku.com', | |||||
| ENV_KEY: 'dev' | |||||
| // BASE_URL: 'http://192.168.31.168:16888', | |||||
| // M_URL: 'https://m.ai.jiefuku.com', | |||||
| // PC_URL: 'https://ai.jiefuku.com', | |||||
| // ENV_KEY: 'dev', | |||||
| BASE_URL: 'https://api.aionline.cc', | |||||
| M_URL: 'https://m.aionline.cc', | |||||
| PC_URL: 'https://tool.aionline.cc', | |||||
| ENV_KEY: 'pro' | |||||
| }, | }, | ||||
| test: { | test: { | ||||
| BASE_URL: 'https://api.jiefuku.com', | BASE_URL: 'https://api.jiefuku.com', | ||||
| @@ -1,4 +1,5 @@ | |||||
| import config from './config.js' | import config from './config.js' | ||||
| import { clearAuth } from './auth.js' | |||||
| import { guid, getNonce, getVersion, getClient, isLoginTop } from './util.js' | import { guid, getNonce, getVersion, getClient, isLoginTop } from './util.js' | ||||
| let BASE_URL = config.BASE_URL | let BASE_URL = config.BASE_URL | ||||
| @@ -7,18 +8,38 @@ let BASE_URL = config.BASE_URL | |||||
| BASE_URL = '/api' | BASE_URL = '/api' | ||||
| // #endif | // #endif | ||||
| function clearLoginState() { | |||||
| clearAuth() | |||||
| // 动态引入避免和 Pinia store 形成循环依赖;storage 先同步清掉,页面状态随后刷新。 | |||||
| import('@/store/user.js').then(({ useUserStore }) => { | |||||
| useUserStore().clear() | |||||
| }).catch(() => {}) | |||||
| } | |||||
| function normalizeResponse(response) { | |||||
| // uni-app Promise 形式在部分端可能返回 [err, res],这里保留兼容; | |||||
| // 主流程已改用 success/fail callback,避免小程序端 response.data 解析不稳定。 | |||||
| if (Array.isArray(response)) { | |||||
| const [err, res] = response | |||||
| if (err) throw err | |||||
| return res || {} | |||||
| } | |||||
| return response || {} | |||||
| } | |||||
| /** | /** | ||||
| * 统一请求封装(沿用旧版鉴权约定) | * 统一请求封装(沿用旧版鉴权约定) | ||||
| * - 自动附加 token / uid / mac / client / source / version 等公共参数 | * - 自动附加 token / uid / mac / client / source / version 等公共参数 | ||||
| * - 响应 code===0 视为成功;code===1009 登录失效,清空登录态并跳转登录 | * - 响应 code===0 视为成功;code===1009 登录失效,清空登录态并跳转登录 | ||||
| * @param {{ url: string, method?: string, params?: object, headers?: object, loading?: boolean }} options | |||||
| * @param {{ url: string, method?: string, params?: object, headers?: object, loading?: boolean, authSilent?: boolean }} options | |||||
| */ | */ | ||||
| export default function request({ | export default function request({ | ||||
| url = '', | url = '', | ||||
| method = 'GET', | method = 'GET', | ||||
| params = {}, | params = {}, | ||||
| headers = {}, | headers = {}, | ||||
| loading = false | |||||
| loading = false, | |||||
| authSilent = false | |||||
| } = {}) { | } = {}) { | ||||
| const fullUrl = BASE_URL + url | const fullUrl = BASE_URL + url | ||||
| const timestamp = parseInt(Date.now() / 1000) | const timestamp = parseInt(Date.now() / 1000) | ||||
| @@ -66,40 +87,63 @@ export default function request({ | |||||
| if (loading) uni.showLoading({ mask: true, title: '加载中...' }) | if (loading) uni.showLoading({ mask: true, title: '加载中...' }) | ||||
| return new Promise((resolve, reject) => { | return new Promise((resolve, reject) => { | ||||
| const finish = () => { | |||||
| if (loading) uni.hideLoading() | |||||
| } | |||||
| const handleResponse = (response) => { | |||||
| const normalized = normalizeResponse(response) | |||||
| const res = (normalized && normalized.data) || {} | |||||
| if (res.code === 0) { | |||||
| resolve(res) | |||||
| return | |||||
| } | |||||
| if (res.code === 1009) { | |||||
| clearLoginState() | |||||
| if (authSilent) { | |||||
| resolve({ | |||||
| code: 0, | |||||
| data: { authExpired: true }, | |||||
| list: [], | |||||
| msg: '', | |||||
| authExpired: true, | |||||
| raw: res | |||||
| }) | |||||
| return | |||||
| } | |||||
| if (!isLoginTop()) { | |||||
| let loginUrl = '/pages/login/login' | |||||
| // #ifdef H5 | |||||
| loginUrl += `?redirect=${encodeURIComponent(location.pathname + location.search)}` | |||||
| // #endif | |||||
| uni.navigateTo({ url: loginUrl }) | |||||
| } | |||||
| reject(Object.assign({}, res, { authExpired: true })) | |||||
| return | |||||
| } | |||||
| reject(res) | |||||
| } | |||||
| uni.request({ | uni.request({ | ||||
| url: fullUrl, | url: fullUrl, | ||||
| method, | method, | ||||
| header, | header, | ||||
| data | |||||
| }) | |||||
| .then((response) => { | |||||
| const res = (response && response.data) || {} | |||||
| if (res.code === 0) { | |||||
| resolve(res) | |||||
| } else if (res.code === 1009) { | |||||
| // 登录失效:清空登录态并跳转登录页 | |||||
| // 动态引入避免循环依赖 | |||||
| import('@/store/user.js').then(({ useUserStore }) => { | |||||
| useUserStore().clear() | |||||
| }) | |||||
| if (!isLoginTop()) { | |||||
| let loginUrl = '/pages/login/login' | |||||
| // #ifdef H5 | |||||
| loginUrl += `?redirect=${encodeURIComponent(location.pathname + location.search)}` | |||||
| // #endif | |||||
| uni.navigateTo({ url: loginUrl }) | |||||
| } | |||||
| reject(res) | |||||
| } else { | |||||
| reject(res) | |||||
| data, | |||||
| success: (response) => { | |||||
| try { | |||||
| handleResponse(response) | |||||
| } catch (e) { | |||||
| const status = e && e.statusCode | |||||
| reject({ code: 1, msg: status ? `网络异常 (${status})` : '网络异常,请重试!' }) | |||||
| } finally { | |||||
| finish() | |||||
| } | } | ||||
| }) | |||||
| .catch((e) => { | |||||
| }, | |||||
| fail: (e) => { | |||||
| const status = e && e.statusCode | const status = e && e.statusCode | ||||
| reject({ code: 1, msg: status ? `网络异常 (${status})` : '网络异常,请重试!' }) | reject({ code: 1, msg: status ? `网络异常 (${status})` : '网络异常,请重试!' }) | ||||
| }) | |||||
| .finally(() => { | |||||
| if (loading) uni.hideLoading() | |||||
| }) | |||||
| finish() | |||||
| } | |||||
| }) | |||||
| }) | }) | ||||
| } | } | ||||
| @@ -0,0 +1,99 @@ | |||||
| import { inviteApi } from '@/api/index.js' | |||||
| import { useUserStore } from '@/store/user.js' | |||||
| const DEFAULT_SHARE = { | |||||
| title: 'AI在线|开箱即用的 AI 工具箱', | |||||
| imageUrl: '', | |||||
| path: '/pages/toolbox/toolbox' | |||||
| } | |||||
| const shareState = { | |||||
| loaded: false, | |||||
| loading: null, | |||||
| token: '', | |||||
| share: {}, | |||||
| config: {} | |||||
| } | |||||
| function appendQuery(path, query = {}) { | |||||
| let nextPath = path || DEFAULT_SHARE.path | |||||
| const pairs = Object.keys(query) | |||||
| .filter((key) => query[key] !== undefined && query[key] !== null && query[key] !== '') | |||||
| .map((key) => `${encodeURIComponent(key)}=${encodeURIComponent(query[key])}`) | |||||
| if (!pairs.length) return nextPath | |||||
| nextPath += nextPath.includes('?') ? '&' : '?' | |||||
| return nextPath + pairs.join('&') | |||||
| } | |||||
| function getInviteCode() { | |||||
| const userStore = useUserStore() | |||||
| userStore.restore() | |||||
| const user = userStore.userInfo || {} | |||||
| return user.open_id || user.user_open_id || user.invite_code || uni.getStorageSync('open_id') || '' | |||||
| } | |||||
| export function getInviteSharePath(options = {}) { | |||||
| const inviteCode = getInviteCode() | |||||
| // 与邀请好友页保持同一落地逻辑:统一进入邀请落地页。 | |||||
| // 未登录时 invite_code 为空,好友仍能进入活动说明;登录用户会携带自己的 open_id。 | |||||
| const path = `/pages/invite/landing?invite_code=${encodeURIComponent(inviteCode)}` | |||||
| return appendQuery(path, { source: options.source }) | |||||
| } | |||||
| export async function preloadInviteShare() { | |||||
| const userStore = useUserStore() | |||||
| userStore.restore() | |||||
| const currentToken = userStore.token || '' | |||||
| if (!currentToken) { | |||||
| shareState.loaded = true | |||||
| shareState.token = '' | |||||
| shareState.share = {} | |||||
| shareState.config = {} | |||||
| return shareState | |||||
| } | |||||
| if (shareState.loaded && shareState.token === currentToken) return shareState | |||||
| if (shareState.loading) return shareState.loading | |||||
| shareState.loading = inviteApi.getMy({}, { authSilent: true }) | |||||
| .then((res) => { | |||||
| const data = res.data || {} | |||||
| shareState.share = data.share || {} | |||||
| shareState.config = data.config || {} | |||||
| shareState.token = currentToken | |||||
| shareState.loaded = true | |||||
| return shareState | |||||
| }) | |||||
| .catch(() => { | |||||
| shareState.loaded = true | |||||
| return shareState | |||||
| }) | |||||
| .finally(() => { | |||||
| shareState.loading = null | |||||
| }) | |||||
| return shareState.loading | |||||
| } | |||||
| export function buildShareOptions(options = {}) { | |||||
| const share = shareState.share || {} | |||||
| const config = shareState.config || {} | |||||
| const path = appendQuery( | |||||
| options.path || share.path || getInviteSharePath(), | |||||
| { source: options.source } | |||||
| ) | |||||
| return { | |||||
| title: options.title || share.title || config.share_title || DEFAULT_SHARE.title, | |||||
| path, | |||||
| imageUrl: options.imageUrl || share.image || config.share_image || DEFAULT_SHARE.imageUrl | |||||
| } | |||||
| } | |||||
| export function showPageShareMenu() { | |||||
| preloadInviteShare() | |||||
| try { | |||||
| uni.showShareMenu({ withShareTicket: true }) | |||||
| } catch (e) {} | |||||
| } | |||||