Explorar o código

增加积分机制,跟租户绑定,图片100、视频500

liyanbo hai 4 semanas
pai
achega
4908e4f71e

+ 6 - 0
src/api/ai/point/index.ts

@@ -0,0 +1,6 @@
+import request from '@/config/axios'
+
+export const getAccountPage = (params: any) => request.get({ url: '/ai/point/account-page', params })
+export const getRecordPage = (params: any) => request.get({ url: '/ai/point/record-page', params })
+export const recharge = (data: { tenantId: number; point: number; remark?: string }) =>
+  request.post({ url: '/ai/point/recharge', data })

+ 0 - 2
src/types/auto-imports.d.ts

@@ -7,8 +7,6 @@ export {}
 declare global {
   const DICT_TYPE: typeof import('@/utils/dict')['DICT_TYPE']
   const EffectScope: typeof import('vue')['EffectScope']
-  const ElMessage: typeof import('element-plus/es')['ElMessage']
-  const ElMessageBox: typeof import('element-plus/es')['ElMessageBox']
   const computed: typeof import('vue')['computed']
   const createApp: typeof import('vue')['createApp']
   const customRef: typeof import('vue')['customRef']

+ 38 - 0
src/views/system/aiPoint/index.vue

@@ -0,0 +1,38 @@
+<template>
+  <ContentWrap>
+    <el-form :inline="true" :model="queryParams">
+      <el-form-item label="租户"><el-select v-model="queryParams.tenantId" clearable filterable class="!w-240px"><el-option v-for="item in tenants" :key="item.id" :label="item.name" :value="item.id" /></el-select></el-form-item>
+      <el-form-item><el-button @click="getList"><Icon icon="ep:search" /> 搜索</el-button><el-button @click="resetQuery"><Icon icon="ep:refresh" /> 重置</el-button></el-form-item>
+    </el-form>
+  </ContentWrap>
+  <ContentWrap>
+    <el-table v-loading="loading" :data="list">
+      <el-table-column label="租户" min-width="180" align="center"><template #default="{ row }">{{ tenantName(row.tenantId) }}</template></el-table-column>
+      <el-table-column label="当前可用积分" prop="balance" align="center" />
+      <el-table-column label="累计充值" prop="totalRecharge" align="center" />
+      <el-table-column label="累计消耗" prop="totalConsume" align="center" />
+      <el-table-column label="状态" align="center"><template #default="{ row }"><el-tag :type="row.status === 1 ? 'success' : 'info'">{{ row.status === 1 ? '启用' : '停用' }}</el-tag></template></el-table-column>
+      <el-table-column label="操作" width="220" fixed="right"><template #default="{ row }"><el-button link type="primary" @click="openRecharge(row)">充值</el-button><el-button link type="primary" @click="openRecords(row)">消耗明细</el-button></template></el-table-column>
+    </el-table>
+    <Pagination :total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize" @pagination="getList" />
+  </ContentWrap>
+  <el-dialog v-model="rechargeVisible" title="租户 AI 积分充值" width="420px">
+    <el-form :model="rechargeForm" label-width="90px"><el-form-item label="租户"><span>{{ tenantName(rechargeForm.tenantId) }}</span></el-form-item><el-form-item label="充值积分"><el-input-number v-model="rechargeForm.point" :min="1" :precision="0" /></el-form-item><el-form-item label="备注"><el-input v-model="rechargeForm.remark" maxlength="500" /></el-form-item></el-form>
+    <template #footer><el-button @click="rechargeVisible=false">取消</el-button><el-button type="primary" @click="submitRecharge">确认充值</el-button></template>
+  </el-dialog>
+</template>
+<script setup lang="ts">
+import * as PointApi from '@/api/ai/point'
+import * as TenantApi from '@/api/system/tenant'
+import { useRouter } from 'vue-router'
+defineOptions({ name: 'SystemAiPoint' })
+const router = useRouter(); const message = useMessage(); const loading = ref(false); const list = ref<any[]>([]); const total = ref(0); const tenants = ref<any[]>([])
+const queryParams = reactive<any>({ pageNo: 1, pageSize: 10, tenantId: undefined }); const rechargeVisible = ref(false); const rechargeForm = reactive<any>({ tenantId: undefined, point: 100000, remark: '' })
+const tenantName = (id: number) => tenants.value.find((item: any) => item.id === id)?.name || `租户#${id}`
+const getList = async () => { loading.value = true; try { const data = await PointApi.getAccountPage(queryParams); list.value = data.list; total.value = data.total } finally { loading.value = false } }
+const resetQuery = () => { queryParams.tenantId = undefined; queryParams.pageNo = 1; getList() }
+const openRecharge = (row: any) => { Object.assign(rechargeForm, { tenantId: row.tenantId, point: 100000, remark: '' }); rechargeVisible.value = true }
+const submitRecharge = async () => { await PointApi.recharge(rechargeForm); message.success('充值成功'); rechargeVisible.value = false; getList() }
+const openRecords = (row: any) => router.push({ path: '/system/ai-point-management/consume-history', query: { tenantId: row.tenantId } })
+onMounted(async () => { tenants.value = await TenantApi.getTenantList(); await getList() })
+</script>

+ 28 - 0
src/views/system/aiPointRecord/index.vue

@@ -0,0 +1,28 @@
+<template>
+  <ContentWrap><el-form :inline="true" :model="queryParams"><el-form-item label="租户"><el-select v-model="queryParams.tenantId" clearable filterable class="!w-240px"><el-option v-for="item in tenants" :key="item.id" :label="item.name" :value="item.id" /></el-select></el-form-item><el-form-item label="类型"><el-select v-model="queryParams.bizType" clearable class="!w-160px"><el-option label="图片生成" value="IMAGE" /><el-option label="视频生成" value="VIDEO" /><el-option label="人工充值" value="MANUAL_RECHARGE" /></el-select></el-form-item><el-form-item><el-button @click="getList"><Icon icon="ep:search" /> 搜索</el-button><el-button @click="resetQuery"><Icon icon="ep:refresh" /> 重置</el-button></el-form-item></el-form></ContentWrap>
+  <ContentWrap><el-table v-loading="loading" :data="list"><el-table-column label="流水号" prop="requestNo" min-width="180" /><el-table-column label="租户" min-width="180" align="center"><template #default="{ row }">{{ tenantName(row.tenantId) }}</template></el-table-column><el-table-column label="用户" prop="userNickname" /><el-table-column label="类型" min-width="120"><template #default="{ row }"><el-tag>{{ bizTypeMap[row.bizType] || row.bizType }}</el-tag></template></el-table-column><el-table-column label="消耗积分" prop="consumePoint" /><el-table-column label="扣后余额" prop="balanceAfter" /><el-table-column label="状态" min-width="100" align="center"><template #default="{ row }"><el-tag :type="statusTagType(row.status)">{{ statusMap[row.status] || row.status }}</el-tag></template></el-table-column><el-table-column label="结果" min-width="160"><template #default="{ row }"><el-link v-if="row.resultUrl" :href="row.resultUrl" target="_blank">查看生成结果</el-link><span v-else>{{ row.failureReason || '-' }}</span></template></el-table-column><el-table-column label="时间" prop="createTime" width="180" :formatter="dateFormatter" /></el-table><Pagination :total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize" @pagination="getList" /></ContentWrap>
+</template>
+<script setup lang="ts">
+import * as PointApi from '@/api/ai/point'
+import * as TenantApi from '@/api/system/tenant'
+import { useRoute } from 'vue-router'
+import { dateFormatter } from '@/utils/formatTime'
+defineOptions({ name: 'SystemAiPointRecord' })
+const route = useRoute(); const loading = ref(false); const list = ref<any[]>([]); const total = ref(0); const tenants = ref<any[]>([])
+
+// 类型映射
+const bizTypeMap: Record<string, string> = { IMAGE: '图片生成', VIDEO: '视频生成', MANUAL_RECHARGE: '人工充值' }
+// 积分流水状态:0进行中、1成功、2失败已退款、3人工调整
+const statusMap: Record<number | string, string> = { 0: '进行中', 1: '成功', 2: '失败已退款', 3: '人工调整' }
+const statusTagType = (status: number | string) => {
+  if (status === 1 || status === 3 || status === 'SUCCESS') return 'success'
+  if (status === 2 || status === 'FAIL') return 'danger'
+  return 'warning'
+}
+
+const queryParams = reactive<any>({ pageNo: 1, pageSize: 10, tenantId: route.query.tenantId ? Number(route.query.tenantId) : undefined, bizType: undefined })
+const tenantName = (id: number) => tenants.value.find((item: any) => item.id === id)?.name || `租户#${id}`
+const getList = async () => { loading.value = true; try { const data = await PointApi.getRecordPage(queryParams); list.value = data.list; total.value = data.total } finally { loading.value = false } }
+const resetQuery = () => { queryParams.tenantId = undefined; queryParams.bizType = undefined; queryParams.pageNo = 1; getList() }
+onMounted(async () => { tenants.value = await TenantApi.getTenantList(); await getList() })
+</script>