瀏覽代碼

修复BUG

main
leiyun 1 月之前
父節點
當前提交
e5875fb659
共有 19 個文件被更改,包括 580 次插入146 次删除
  1. +5
    -5
      api/index.js
  2. +4
    -2
      pages/invite/invite.vue
  3. +4
    -2
      pages/invite/landing.vue
  4. +10
    -1
      pages/point/earn.vue
  5. +11
    -1
      pages/tool/ai-name.vue
  6. +10
    -1
      pages/tool/anime-avatar.vue
  7. +54
    -17
      pages/tool/anime-history.vue
  8. +43
    -15
      pages/tool/id-photo-history.vue
  9. +10
    -1
      pages/tool/id-photo.vue
  10. +55
    -17
      pages/tool/name-history.vue
  11. +53
    -16
      pages/tool/old-photo-history.vue
  12. +10
    -1
      pages/tool/old-photo.vue
  13. +53
    -16
      pages/tool/portrait-history.vue
  14. +10
    -1
      pages/tool/portrait-photo.vue
  15. +10
    -1
      pages/toolbox/toolbox.vue
  16. +57
    -15
      pages/works/works.vue
  17. +8
    -4
      utils/config.js
  18. +74
    -30
      utils/request.js
  19. +99
    -0
      utils/share.js

+ 5
- 5
api/index.js 查看文件

@@ -36,10 +36,10 @@ export const userApi = {
}

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 = {
@@ -112,7 +112,7 @@ export const oldPhotoApi = {
}

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 }),
getAppDetail: (params) => request({ url: API.INTERFACE_APP_GET_DETAIL, method: 'POST', params }),
getWorkList: (params) => request({ url: API.INTERFACE_ART_GET_LIST, method: 'POST', params }),


+ 4
- 2
pages/invite/invite.vue 查看文件

@@ -92,6 +92,7 @@ import { onShareAppMessage, onShow } from '@dcloudio/uni-app'
import { inviteApi } from '@/api/index.js'
import { useUserStore } from '@/store/user.js'
import { useApp } from '@/utils/useApp.js'
import { buildShareOptions } from '@/utils/share.js'

const userStore = useUserStore()
const { toast } = useApp()
@@ -113,10 +114,11 @@ onShow(() => {
loadData()
})

onShareAppMessage(() => ({
onShareAppMessage(() => buildShareOptions({
title: (share.value && share.value.title) || config.value.share_title || '邀请你体验 AI ONLINE',
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() {


+ 4
- 2
pages/invite/landing.vue 查看文件

@@ -32,6 +32,7 @@ import { onLoad, onShareAppMessage } from '@dcloudio/uni-app'
import { inviteApi } from '@/api/index.js'
import { useUserStore } from '@/store/user.js'
import { useApp } from '@/utils/useApp.js'
import { buildShareOptions } from '@/utils/share.js'

const userStore = useUserStore()
const { toast } = useApp()
@@ -47,10 +48,11 @@ onLoad((options = {}) => {
loadConfig(options.scene || '')
})

onShareAppMessage(() => ({
onShareAppMessage(() => buildShareOptions({
title: config.value.share_title || '邀请你体验 AI ONLINE',
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) {


+ 10
- 1
pages/point/earn.vue 查看文件

@@ -107,14 +107,23 @@

<script setup>
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 { useUserStore } from '@/store/user.js'
import { useApp } from '@/utils/useApp.js'
import { buildShareOptions, showPageShareMenu } from '@/utils/share.js'

const userStore = useUserStore()
const { toast } = useApp()

const pageShare = {
title: '获得点数|签到、邀请、看视频都能拿点数',
source: 'share_earn_points'
}

onShow(() => showPageShareMenu())
onShareAppMessage(() => buildShareOptions(pageShare))

const loading = ref(true)
const balance = ref(0)
const rewarding = ref(false)


+ 11
- 1
pages/tool/ai-name.vue 查看文件

@@ -237,15 +237,25 @@

<script setup>
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 { billingApi, toolApi, userApi } from '@/api/index.js'
import { useUserStore } from '@/store/user.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'

const userStore = useUserStore()
const { toast } = useApp()

const pageShare = {
title: 'AI取名|给宝宝取一个有出处的好名字',
source: 'share_ai_name'
}

onShow(() => showPageShareMenu())
onShareAppMessage(() => buildShareOptions(pageShare))

const appDetail = ref({})
const robotInfo = ref({})
const todayDate = dayjs().format('YYYY-MM-DD')


+ 10
- 1
pages/tool/anime-avatar.vue 查看文件

@@ -181,16 +181,25 @@

<script setup>
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 { useUserStore } from '@/store/user.js'
import { useApp } from '@/utils/useApp.js'
import { uploadImage } from '@/utils/upload.js'
import { buildShareOptions, showPageShareMenu } from '@/utils/share.js'

const userStore = useUserStore()
const { toast } = useApp()
const ANIME_BILLING_MODEL = 'tencent_anime'

const pageShare = {
title: 'AI动漫头像|上传自拍生成你的专属头像',
source: 'share_anime_avatar'
}

onShow(() => showPageShareMenu())
onShareAppMessage(() => buildShareOptions(pageShare))

const appDetail = ref({})
const demoList = ref([])
const styleList = ref([{ value: '201', name: '默认', model: 2, pic: 'https://cdn.aionline.cc/ai/toolbox/home_card_avatar.png' }])


+ 54
- 17
pages/tool/anime-history.vue 查看文件

@@ -79,7 +79,7 @@

<script setup>
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 { toolApi } from '@/api/index.js'
import { useApp } from '@/utils/useApp.js'
@@ -87,15 +87,25 @@ import { useApp } from '@/utils/useApp.js'
const { toast } = useApp()
const list = ref([])
const page = ref(1)
const pageSize = 10
const total = ref(0)
const loading = ref(false)
const refreshing = ref(false)
let fetching = false
let firstShow = true
let timer = 0

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)

onLoad(() => refresh())
onLoad(() => fetchList({ reset: true }))
onShow(() => {
if (firstShow) {
firstShow = false
return
}
refreshLoaded(true)
})
onUnload(() => clearTimeout(timer))

function formatTime(value) {
@@ -103,46 +113,73 @@ function formatTime(value) {
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 {
const res = await toolApi.getAnimeList({
page_no: page.value,
page_size: 10,
page_no: queryPage,
page_size: size,
role: 2
})
const rows = res.list || []
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()
} catch (e) {
toast(e.msg || '历史记录加载失败')
if (!silent) toast(e.msg || '历史记录加载失败')
} finally {
fetching = false
loading.value = false
refreshing.value = false
finishRefresh()
}
}

function scheduleRefresh() {
clearTimeout(timer)
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() {
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() {
if (loading.value) return
if (fetching) return
if (list.value.length >= total.value) return
page.value += 1
fetchList()
@@ -190,7 +227,7 @@ function deleteItem(item) {
try {
await toolApi.deleteAnime({ chat_open_id: item.chat_open_id })
toast('删除成功')
refresh()
refreshLoaded(true)
} catch (e) {
toast(e.msg || '删除失败')
}


+ 43
- 15
pages/tool/id-photo-history.vue 查看文件

@@ -91,15 +91,25 @@ import { useApp } from '@/utils/useApp.js'
const { toast } = useApp()
const list = ref([])
const page = ref(1)
const pageSize = 10
const total = ref(0)
const loading = ref(false)
const refreshing = ref(false)
let fetching = false
let firstShow = true
let timer = 0

const pendingCount = computed(() => list.value.filter((item) => isPending(item)).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())
onUnload(() => clearTimer())

@@ -112,22 +122,35 @@ function formatTime(value) {
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()
return
}
loading.value = true
if (reset) page.value = 1
fetching = true
if (!silent) loading.value = true
if (reset) {
page.value = 1
queryPage = 1
}
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 || []
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()
} catch (e) {
toast((e && e.msg) || '记录加载失败')
if (!silent) toast((e && e.msg) || '记录加载失败')
} finally {
fetching = false
loading.value = false
finishRefresh()
}
@@ -136,7 +159,7 @@ async function fetchList(reset = false) {
function scheduleRefresh() {
clearTimer()
if (list.value.some((item) => isPending(item))) {
timer = setTimeout(() => refreshSilent(), 3000)
timer = setTimeout(() => refreshLoaded(true), 3000)
}
}

@@ -152,16 +175,21 @@ function finishRefresh() {

function refresh() {
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() {
if (loading.value || list.value.length >= total.value) return
if (fetching || list.value.length >= total.value) return
page.value += 1
fetchList()
}
@@ -211,7 +239,7 @@ function deleteItem(item) {
try {
await idPhotoApi.delete({ photo_open_id: id })
toast('删除成功')
refreshSilent()
refreshLoaded(true)
} catch (e) {
toast((e && e.msg) || '删除失败')
}


+ 10
- 1
pages/tool/id-photo.vue 查看文件

@@ -284,12 +284,21 @@

<script setup>
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 { createIdPhoto } from '@/utils/upload.js'
import { guid } from '@/utils/util.js'
import { useUserStore } from '@/store/user.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 = [
{ key: 'blue', name: '常用蓝', color: '#438edb' },


+ 55
- 17
pages/tool/name-history.vue 查看文件

@@ -38,7 +38,7 @@

<script setup>
import { ref } from 'vue'
import { onLoad, onUnload } from '@dcloudio/uni-app'
import { onLoad, onShow, onUnload } from '@dcloudio/uni-app'
import dayjs from 'dayjs'
import { toolApi } from '@/api/index.js'
import { useApp } from '@/utils/useApp.js'
@@ -47,14 +47,25 @@ const { toast } = useApp()
const robotId = ref('')
const list = ref([])
const page = ref(1)
const pageSize = 10
const total = ref(0)
const loading = ref(false)
const refreshing = ref(false)
let fetching = false
let firstShow = true
let timer = 0

onLoad((options) => {
robotId.value = options.robot_id || ''
refresh()
fetchList({ reset: true })
})

onShow(() => {
if (firstShow) {
firstShow = false
return
}
refreshLoaded(true)
})

onUnload(() => clearTimeout(timer))
@@ -64,47 +75,74 @@ function formatTime(value) {
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 {
const res = await toolApi.getNameList({
page_no: page.value,
page_size: 10,
page_no: queryPage,
page_size: size,
robot_open_id: robotId.value,
role: 2
})
const rows = res.list || []
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()
} catch (e) {
toast(e.msg || '历史记录加载失败')
if (!silent) toast(e.msg || '历史记录加载失败')
} finally {
fetching = false
loading.value = false
refreshing.value = false
finishRefresh()
}
}

function scheduleRefresh() {
clearTimeout(timer)
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() {
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() {
if (loading.value || list.value.length >= total.value) return
if (fetching || list.value.length >= total.value) return
page.value += 1
fetchList()
}
@@ -224,7 +262,7 @@ function deleteItem(item) {
try {
await toolApi.deleteName({ chat_open_id: item.chat_open_id })
toast('删除成功')
refresh()
refreshLoaded(true)
} catch (e) {
toast(e.msg || '删除失败')
}


+ 53
- 16
pages/tool/old-photo-history.vue 查看文件

@@ -78,7 +78,7 @@

<script setup>
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 { oldPhotoApi } from '@/api/index.js'
import { useApp } from '@/utils/useApp.js'
@@ -86,15 +86,25 @@ import { useApp } from '@/utils/useApp.js'
const { toast } = useApp()
const list = ref([])
const page = ref(1)
const pageSize = 10
const total = ref(0)
const loading = ref(false)
const refreshing = ref(false)
let fetching = false
let firstShow = true
let timer = 0

const pendingCount = computed(() => list.value.filter((item) => isPending(item)).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))

function isPending(item) {
@@ -106,20 +116,37 @@ function formatTime(value) {
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 {
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 || []
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()
} catch (e) {
toast((e && e.msg) || '历史记录加载失败')
if (!silent) toast((e && e.msg) || '历史记录加载失败')
} finally {
fetching = false
loading.value = false
refreshing.value = false
finishRefresh()
}
}

@@ -127,22 +154,32 @@ async function fetchList(reset = false) {
function scheduleRefresh() {
clearTimeout(timer)
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() {
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() {
if (loading.value) return
if (fetching) return
if (list.value.length >= total.value) return
page.value += 1
fetchList()
@@ -185,7 +222,7 @@ function deleteItem(item) {
try {
await oldPhotoApi.delete({ photo_open_id: item.photo_open_id })
toast('删除成功')
refresh()
refreshLoaded(true)
} catch (e) {
toast((e && e.msg) || '删除失败')
}


+ 10
- 1
pages/tool/old-photo.vue 查看文件

@@ -171,16 +171,25 @@

<script setup>
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 { createOldPhoto } from '@/utils/upload.js'
import { guid } from '@/utils/util.js'
import { useUserStore } from '@/store/user.js'
import { useApp } from '@/utils/useApp.js'
import { buildShareOptions, showPageShareMenu } from '@/utils/share.js'

const userStore = useUserStore()
const { toast } = useApp()

const pageShare = {
title: '老照片修复上色|让旧照片焕然一新',
source: 'share_old_photo'
}

onShow(() => showPageShareMenu())
onShareAppMessage(() => buildShareOptions(pageShare))

// 轮询:混元未买并发,排队 + 生成通常几十秒,最长按后端 20 分钟超时来兜
const POLL_INTERVAL = 3000
const POLL_LIMIT = 5 * 60 * 1000


+ 53
- 16
pages/tool/portrait-history.vue 查看文件

@@ -79,7 +79,7 @@

<script setup>
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 { portraitApi } from '@/api/index.js'
import { useApp } from '@/utils/useApp.js'
@@ -87,9 +87,12 @@ import { useApp } from '@/utils/useApp.js'
const { toast } = useApp()
const list = ref([])
const page = ref(1)
const pageSize = 10
const total = ref(0)
const loading = ref(false)
const refreshing = ref(false)
let fetching = false
let firstShow = true
let timer = 0

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)
)

onLoad(() => refresh())
onLoad(() => fetchList({ reset: true }))
onShow(() => {
if (firstShow) {
firstShow = false
return
}
refreshLoaded(true)
})
onUnload(() => clearTimeout(timer))

function isPending(item) {
@@ -109,20 +119,37 @@ function formatTime(value) {
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 {
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 || []
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()
} catch (e) {
toast((e && e.msg) || '记录加载失败')
if (!silent) toast((e && e.msg) || '记录加载失败')
} finally {
fetching = false
loading.value = false
refreshing.value = false
finishRefresh()
}
}

@@ -130,22 +157,32 @@ async function fetchList(reset = false) {
function scheduleRefresh() {
clearTimeout(timer)
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() {
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() {
if (loading.value) return
if (fetching) return
if (list.value.length >= total.value) return
page.value += 1
fetchList()
@@ -187,7 +224,7 @@ function deleteItem(item) {
try {
await portraitApi.delete({ photo_open_id: item.photo_open_id })
toast('删除成功')
refresh()
refreshLoaded(true)
} catch (e) {
toast((e && e.msg) || '删除失败')
}


+ 10
- 1
pages/tool/portrait-photo.vue 查看文件

@@ -270,16 +270,25 @@

<script setup>
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 { createPortrait } from '@/utils/upload.js'
import { guid } from '@/utils/util.js'
import { useUserStore } from '@/store/user.js'
import { useApp } from '@/utils/useApp.js'
import { buildShareOptions, showPageShareMenu } from '@/utils/share.js'

const userStore = useUserStore()
const { toast } = useApp()

const pageShare = {
title: 'AI形象照|职业照、艺术写真一键生成',
source: 'share_portrait_photo'
}

onShow(() => showPageShareMenu())
onShareAppMessage(() => buildShareOptions(pageShare))

const POLL_INTERVAL = 3000
const POLL_LIMIT = 8 * 60 * 1000



+ 10
- 1
pages/toolbox/toolbox.vue 查看文件

@@ -49,15 +49,24 @@

<script setup>
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 { useApp } from '@/utils/useApp.js'
import { buildShareOptions, showPageShareMenu } from '@/utils/share.js'

const { toast } = useApp()
const refreshing = ref(false)
const bannerIndex = ref(0)
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 defaultRecommends = [


+ 57
- 15
pages/works/works.vue 查看文件

@@ -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 tabsScrollLeft = ref(0)
let fetching = false
let timer = 0

const tabs = computed(() => [
@@ -135,11 +136,17 @@ const tabs = computed(() => [

onShow(() => {
const preferredTab = uni.getStorageSync('works_active_tab')
let needReset = false
if (tabs.value.some((item) => item.key === preferredTab)) {
needReset = activeTab.value !== preferredTab
activeTab.value = preferredTab
uni.removeStorageSync('works_active_tab')
}
refreshSilent()
if (needReset) {
fetchList({ reset: true })
} else {
refreshLoaded(list.value.length > 0)
}
// 从其它页面带着类型进来时,把该类型滚到可视区中间(等渲染完成再量位置)
nextTick(() => centerTab(activeTab.value))
})
@@ -152,7 +159,15 @@ function formatTime(value) {
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) {
list.value = []
total.value = 0
@@ -160,27 +175,48 @@ async function fetchList(reset = false, silent = false) {
uni.stopPullDownRefresh()
return
}
if (loading.value) {
if (fetching) {
if (refreshing.value) uni.stopPullDownRefresh()
return
}
loading.value = true
if (reset) page.value = 1
fetching = true
if (!silent) loading.value = true
if (reset) {
page.value = 1
queryPage = 1
}
try {
const res = await toolApi.getMyWorkList({
type: activeTab.value,
page_no: page.value,
page_size: pageSize
})
page_no: queryPage,
page_size: size
}, { authSilent: true })
const rows = res.list || []
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)
counts.value = normalizeCounts(data.counts || {})
scheduleRefresh()
} catch (e) {
if (Number(e?.code) === 1009) {
userStore.clear()
list.value = []
total.value = 0
counts.value = normalizeCounts({})
clearTimer()
return
}
if (!silent) toast(e.msg || '作品加载失败')
} finally {
fetching = false
loading.value = false
refreshing.value = false
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() {
@@ -213,11 +255,11 @@ function onRefresh() {
})
return
}
refreshSilent()
fetchList({ reset: true })
}

function loadMore() {
if (loading.value || list.value.length >= total.value) return
if (fetching || list.value.length >= total.value) return
page.value += 1
fetchList()
}
@@ -226,7 +268,7 @@ function changeTab(key) {
centerTab(key)
if (activeTab.value === key) return
activeTab.value = key
refreshSilent()
fetchList({ reset: true })
}

/**
@@ -255,7 +297,7 @@ function centerTab(key) {
function scheduleRefresh() {
clearTimer()
if (list.value.some((item) => Number(item.status) === 0)) {
timer = setTimeout(() => refreshSilent(true), 3000)
timer = setTimeout(() => refreshLoaded(true), 3000)
}
}



+ 8
- 4
utils/config.js 查看文件

@@ -9,10 +9,14 @@ const DEFAULT_ENV = 'pro'

const ENV_MAP = {
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: {
BASE_URL: 'https://api.jiefuku.com',


+ 74
- 30
utils/request.js 查看文件

@@ -1,4 +1,5 @@
import config from './config.js'
import { clearAuth } from './auth.js'
import { guid, getNonce, getVersion, getClient, isLoginTop } from './util.js'

let BASE_URL = config.BASE_URL
@@ -7,18 +8,38 @@ let BASE_URL = config.BASE_URL
BASE_URL = '/api'
// #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 等公共参数
* - 响应 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({
url = '',
method = 'GET',
params = {},
headers = {},
loading = false
loading = false,
authSilent = false
} = {}) {
const fullUrl = BASE_URL + url
const timestamp = parseInt(Date.now() / 1000)
@@ -66,40 +87,63 @@ export default function request({
if (loading) uni.showLoading({ mask: true, title: '加载中...' })

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({
url: fullUrl,
method,
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
reject({ code: 1, msg: status ? `网络异常 (${status})` : '网络异常,请重试!' })
})
.finally(() => {
if (loading) uni.hideLoading()
})
finish()
}
})
})
}

+ 99
- 0
utils/share.js 查看文件

@@ -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) {}
}

Loading…
取消
儲存