22
CI/CD / build (push) Successful in 4m40s
Details
CI/CD / build (push) Successful in 4m40s
Details
This commit is contained in:
parent
b4008adb5f
commit
779b6d15dd
|
|
@ -29,13 +29,7 @@
|
||||||
移动端兼容 iOS 13+、Android 8+;桌面端兼容 Chrome 80+、Firefox 75+、Edge 80+;平板端覆盖主流设备(iPadOS、Android 平板)。
|
移动端兼容 iOS 13+、Android 8+;桌面端兼容 Chrome 80+、Firefox 75+、Edge 80+;平板端覆盖主流设备(iPadOS、Android 平板)。
|
||||||
避免使用 ES6 + 以上高级语法(或通过 Babel 转译),确保低版本浏览器兼容性。
|
避免使用 ES6 + 以上高级语法(或通过 Babel 转译),确保低版本浏览器兼容性。
|
||||||
12.设计风格
|
12.设计风格
|
||||||
- 主色调:深紫色(#6B46C1)用于主要操作,浅紫色(#A78BFA)用于强调
|
- 粘土拟态风格
|
||||||
- 辅助色:深灰色(#1F2937)用于文本,浅灰色(#F3F4F6)用于背景
|
|
||||||
- 按钮样式:圆角设计(8px半径),微妙阴影,悬停效果
|
|
||||||
- 字体排版:Inter字体系列,16px基础大小,响应式缩放
|
|
||||||
- 布局风格:基于卡片的设计,统一间距(8px网格系统)
|
|
||||||
- 图标风格:Feather图标库,UI元素统一24px大小
|
|
||||||
- 动画效果:平滑过渡(200ms缓入缓出),加载骨架屏
|
|
||||||
13.交付标准
|
13.交付标准
|
||||||
代码结构清晰,遵循 Vue3 最佳实践(如组件拆分粒度合理、逻辑与 UI 分离)。
|
代码结构清晰,遵循 Vue3 最佳实践(如组件拆分粒度合理、逻辑与 UI 分离)。
|
||||||
提供完整的多端测试报告(说明在不同设备 / 尺寸下的测试结果及适配方案)。
|
提供完整的多端测试报告(说明在不同设备 / 尺寸下的测试结果及适配方案)。
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
deotaland-ai-monorepo/
|
deotaland-ai-monorepo/
|
||||||
├── apps/
|
├── apps/
|
||||||
│ └── frontend/ # 主前端应用
|
│ └── frontend/ # 主前端应用
|
||||||
|
│ └── FrontendDesigner/ # 管理端应用
|
||||||
├── packages/ # 共享包目录
|
├── packages/ # 共享包目录
|
||||||
├── .eslintrc.base.json # 基础ESLint配置
|
├── .eslintrc.base.json # 基础ESLint配置
|
||||||
├── .prettierrc # Prettier配置
|
├── .prettierrc # Prettier配置
|
||||||
|
|
|
||||||
|
|
@ -270,6 +270,7 @@ export default {
|
||||||
promptManagement: 'Prompt Management',
|
promptManagement: 'Prompt Management',
|
||||||
productManagement: 'Product Management',
|
productManagement: 'Product Management',
|
||||||
voucherManagement: 'Voucher Management',
|
voucherManagement: 'Voucher Management',
|
||||||
|
operationLog: 'Operation Log',
|
||||||
logout: 'Logout',
|
logout: 'Logout',
|
||||||
profile: 'Profile',
|
profile: 'Profile',
|
||||||
settings: 'Settings'
|
settings: 'Settings'
|
||||||
|
|
@ -929,6 +930,21 @@ export default {
|
||||||
hint: {
|
hint: {
|
||||||
userIds: 'Multiple user IDs separated by commas, e.g.: 1,2,3'
|
userIds: 'Multiple user IDs separated by commas, e.g.: 1,2,3'
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
operationLog: {
|
||||||
|
title: 'Operation Log',
|
||||||
|
adminUserId: 'Admin User ID',
|
||||||
|
adminUsername: 'Admin Username',
|
||||||
|
operationType: 'Operation Type',
|
||||||
|
all: 'All',
|
||||||
|
resourceType: 'Resource Type',
|
||||||
|
resourceId: 'Resource ID',
|
||||||
|
description: 'Description',
|
||||||
|
ipAddress: 'IP Address',
|
||||||
|
userAgent: 'User Agent',
|
||||||
|
createdAt: 'Created At',
|
||||||
|
detailTitle: 'Operation Log Detail',
|
||||||
|
getListFailed: 'Failed to get operation log list'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
modelUpload: {
|
modelUpload: {
|
||||||
|
|
|
||||||
|
|
@ -284,6 +284,7 @@ orderManagement: {
|
||||||
promptManagement: '提示词管理',
|
promptManagement: '提示词管理',
|
||||||
productManagement: '产品管理',
|
productManagement: '产品管理',
|
||||||
voucherManagement: '优惠券管理',
|
voucherManagement: '优惠券管理',
|
||||||
|
operationLog: '操作日志',
|
||||||
logout: '退出登录',
|
logout: '退出登录',
|
||||||
profile: '个人资料',
|
profile: '个人资料',
|
||||||
settings: '设置',
|
settings: '设置',
|
||||||
|
|
@ -922,6 +923,21 @@ orderManagement: {
|
||||||
hint: {
|
hint: {
|
||||||
userIds: '多个用户ID用逗号分隔,例如:1,2,3'
|
userIds: '多个用户ID用逗号分隔,例如:1,2,3'
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
operationLog: {
|
||||||
|
title: '操作日志',
|
||||||
|
adminUserId: '管理员ID',
|
||||||
|
adminUsername: '管理员用户名',
|
||||||
|
operationType: '操作类型',
|
||||||
|
all: '全部',
|
||||||
|
resourceType: '资源类型',
|
||||||
|
resourceId: '资源ID',
|
||||||
|
description: '操作描述',
|
||||||
|
ipAddress: 'IP地址',
|
||||||
|
userAgent: '用户代理',
|
||||||
|
createdAt: '创建时间',
|
||||||
|
detailTitle: '操作日志详情',
|
||||||
|
getListFailed: '获取操作日志列表失败'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
modelUpload: {
|
modelUpload: {
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ const AdminCommissionManagement = () => import('@/views/admin/AdminCommissionMan
|
||||||
const AdminPromptManagement = () => import('@/views/admin/AdminPromptManagement/AdminPromptManagement.vue')
|
const AdminPromptManagement = () => import('@/views/admin/AdminPromptManagement/AdminPromptManagement.vue')
|
||||||
const AdminProductManagement = () => import('@/views/admin/ProductManagement/ProductManagement.vue')
|
const AdminProductManagement = () => import('@/views/admin/ProductManagement/ProductManagement.vue')
|
||||||
const AdminVoucherManagement = () => import('@/views/admin/VoucherManagement/VoucherManagement.vue')
|
const AdminVoucherManagement = () => import('@/views/admin/VoucherManagement/VoucherManagement.vue')
|
||||||
|
const AdminOperationLog = () => import('@/views/admin/AdminOperationLog/AdminOperationLog.vue')
|
||||||
//权限路由映射表
|
//权限路由映射表
|
||||||
export const permissionRoutes = [
|
export const permissionRoutes = [
|
||||||
{
|
{
|
||||||
|
|
@ -149,6 +150,17 @@ export const permissionRoutes = [
|
||||||
requiresAuth: true
|
requiresAuth: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'operation-log',
|
||||||
|
name: 'AdminOperationLog',
|
||||||
|
component: AdminOperationLog,
|
||||||
|
meta: {
|
||||||
|
title: 'admin.layout.operationLog',
|
||||||
|
icon: 'Document',
|
||||||
|
menuOrder: 7,
|
||||||
|
requiresAuth: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
path: 'permission',
|
path: 'permission',
|
||||||
|
|
@ -156,7 +168,7 @@ export const permissionRoutes = [
|
||||||
meta: {
|
meta: {
|
||||||
title: 'admin.layout.permission',
|
title: 'admin.layout.permission',
|
||||||
icon: 'Lock',
|
icon: 'Lock',
|
||||||
menuOrder: 7,
|
menuOrder: 8,
|
||||||
isParent: true,
|
isParent: true,
|
||||||
requiresAuth: true
|
requiresAuth: true
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,362 @@
|
||||||
|
<template>
|
||||||
|
<div class="operation-log">
|
||||||
|
<!-- 筛选和搜索 -->
|
||||||
|
<div class="log-filters">
|
||||||
|
<div class="filter-group">
|
||||||
|
<el-input
|
||||||
|
v-model="filters.adminUsername"
|
||||||
|
:placeholder="t('admin.operationLog.adminUsername')"
|
||||||
|
clearable
|
||||||
|
@keyup.enter="handleSearch"
|
||||||
|
@clear="handleSearch"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<!-- <div class="filter-group">
|
||||||
|
<el-select
|
||||||
|
v-model="filters.operationType"
|
||||||
|
:placeholder="t('admin.operationLog.operationType')"
|
||||||
|
clearable
|
||||||
|
@change="handleSearch"
|
||||||
|
>
|
||||||
|
<el-option :label="t('admin.operationLog.all')" value="" />
|
||||||
|
<el-option label="CREATE" value="CREATE" />
|
||||||
|
<el-option label="UPDATE" value="UPDATE" />
|
||||||
|
<el-option label="DELETE" value="DELETE" />
|
||||||
|
<el-option label="QUERY" value="QUERY" />
|
||||||
|
</el-select>
|
||||||
|
</div> -->
|
||||||
|
<div class="filter-group">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="dateRange"
|
||||||
|
type="daterange"
|
||||||
|
range-separator="-"
|
||||||
|
:start-placeholder="t('admin.common.startDate')"
|
||||||
|
:end-placeholder="t('admin.common.endDate')"
|
||||||
|
value-format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
popper-class="date-picker-popper"
|
||||||
|
:teleported="true"
|
||||||
|
@change="handleDateRangeChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="filter-actions">
|
||||||
|
<el-button :icon="Search" type="primary" @click="handleSearch">
|
||||||
|
{{ t('admin.common.search') }}
|
||||||
|
</el-button>
|
||||||
|
<el-button :icon="Refresh" @click="handleReset">
|
||||||
|
{{ t('admin.common.reset') }}
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 操作日志列表 -->
|
||||||
|
<div class="log-table">
|
||||||
|
<el-table
|
||||||
|
:data="logList"
|
||||||
|
style="width: 100%"
|
||||||
|
v-loading="loading"
|
||||||
|
stripe
|
||||||
|
>
|
||||||
|
<el-table-column prop="id" label="ID" width="80" />
|
||||||
|
<el-table-column prop="adminUsername" :label="t('admin.operationLog.adminUsername')" min-width="120" />
|
||||||
|
<el-table-column prop="operationTypeDesc" :label="t('admin.operationLog.operationType')" min-width="100">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tag :type="getOperationTypeTagType(row.operationType)">
|
||||||
|
{{ row.operationTypeDesc || row.operationType }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="resourceType" :label="t('admin.operationLog.resourceType')" min-width="120" />
|
||||||
|
<el-table-column prop="resourceId" :label="t('admin.operationLog.resourceId')" min-width="100" />
|
||||||
|
<el-table-column prop="description" :label="t('admin.operationLog.description')" min-width="200" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="ipAddress" :label="t('admin.operationLog.ipAddress')" min-width="140" />
|
||||||
|
<el-table-column prop="createdAt" :label="t('admin.operationLog.createdAt')" min-width="180">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ formatDateTime(row.createdAt) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column :label="t('admin.common.actions')" min-width="100" fixed="right">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-button size="small" @click="handleViewDetail(row)">
|
||||||
|
{{ t('admin.common.detail') }}
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 分页 -->
|
||||||
|
<div class="pagination">
|
||||||
|
<el-pagination
|
||||||
|
v-model:current-page="currentPage"
|
||||||
|
v-model:page-size="pageSize"
|
||||||
|
:page-sizes="[10, 20, 50, 100]"
|
||||||
|
:total="total"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
@current-change="handleCurrentChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 详情对话框 -->
|
||||||
|
<el-dialog
|
||||||
|
:title="t('admin.operationLog.detailTitle')"
|
||||||
|
v-model="detailDialogVisible"
|
||||||
|
width="700px"
|
||||||
|
top="5vh"
|
||||||
|
>
|
||||||
|
<div class="log-detail-container">
|
||||||
|
<el-descriptions :column="1" border>
|
||||||
|
<el-descriptions-item label="ID">
|
||||||
|
{{ selectedLog?.id }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item :label="t('admin.operationLog.adminUserId')">
|
||||||
|
{{ selectedLog?.adminUserId }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item :label="t('admin.operationLog.adminUsername')">
|
||||||
|
{{ selectedLog?.adminUsername }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item :label="t('admin.operationLog.operationType')">
|
||||||
|
<el-tag :type="getOperationTypeTagType(selectedLog?.operationType)">
|
||||||
|
{{ selectedLog?.operationTypeDesc || selectedLog?.operationType }}
|
||||||
|
</el-tag>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item :label="t('admin.operationLog.resourceType')">
|
||||||
|
{{ selectedLog?.resourceType }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item :label="t('admin.operationLog.resourceId')">
|
||||||
|
{{ selectedLog?.resourceId }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item :label="t('admin.operationLog.description')">
|
||||||
|
{{ selectedLog?.description }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item :label="t('admin.operationLog.ipAddress')">
|
||||||
|
{{ selectedLog?.ipAddress }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item :label="t('admin.operationLog.userAgent')">
|
||||||
|
<div class="user-agent">{{ selectedLog?.userAgent }}</div>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item :label="t('admin.operationLog.createdAt')">
|
||||||
|
{{ formatDateTime(selectedLog?.createdAt) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="detailDialogVisible = false">{{ t('admin.common.close') }}</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, reactive, onMounted } from 'vue'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { Search, Refresh } from '@element-plus/icons-vue'
|
||||||
|
import { AdminOperationLog } from './index.js'
|
||||||
|
|
||||||
|
const adminOperationLog = new AdminOperationLog()
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
|
const currentPage = ref(1)
|
||||||
|
const pageSize = ref(10)
|
||||||
|
const total = ref(0)
|
||||||
|
const dateRange = ref([])
|
||||||
|
const detailDialogVisible = ref(false)
|
||||||
|
const selectedLog = ref(null)
|
||||||
|
|
||||||
|
const filters = reactive({
|
||||||
|
adminUserId: '',
|
||||||
|
adminUsername: '',
|
||||||
|
operationType: '',
|
||||||
|
startTime: '',
|
||||||
|
endTime: '',
|
||||||
|
orderByColumn: 'createdAt',
|
||||||
|
isAsc: 'desc'
|
||||||
|
})
|
||||||
|
|
||||||
|
const logList = ref([])
|
||||||
|
|
||||||
|
const getOperationLogList = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
...filters,
|
||||||
|
pageSize: pageSize.value,
|
||||||
|
pageNum: currentPage.value
|
||||||
|
}
|
||||||
|
const res = await adminOperationLog.getOperationLogList(params)
|
||||||
|
if (res.code === 0 && res.data) {
|
||||||
|
logList.value = res.data.rows || []
|
||||||
|
total.value = res.data.total || 0
|
||||||
|
} else {
|
||||||
|
ElMessage.error(res.message || t('admin.operationLog.getListFailed'))
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取操作日志失败:', error)
|
||||||
|
ElMessage.error(t('admin.operationLog.getListFailed'))
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSearch = () => {
|
||||||
|
currentPage.value = 1
|
||||||
|
getOperationLogList()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleReset = () => {
|
||||||
|
filters.adminUserId = ''
|
||||||
|
filters.adminUsername = ''
|
||||||
|
filters.operationType = ''
|
||||||
|
filters.startTime = ''
|
||||||
|
filters.endTime = ''
|
||||||
|
dateRange.value = []
|
||||||
|
currentPage.value = 1
|
||||||
|
getOperationLogList()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDateRangeChange = (value) => {
|
||||||
|
if (value && value.length === 2) {
|
||||||
|
filters.startTime = value[0]
|
||||||
|
filters.endTime = value[1]
|
||||||
|
} else {
|
||||||
|
filters.startTime = ''
|
||||||
|
filters.endTime = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSizeChange = (val) => {
|
||||||
|
pageSize.value = val
|
||||||
|
currentPage.value = 1
|
||||||
|
getOperationLogList()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCurrentChange = (val) => {
|
||||||
|
currentPage.value = val
|
||||||
|
getOperationLogList()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleViewDetail = (row) => {
|
||||||
|
selectedLog.value = row
|
||||||
|
detailDialogVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const getOperationTypeTagType = (type) => {
|
||||||
|
const typeMap = {
|
||||||
|
'CREATE': 'success',
|
||||||
|
'UPDATE': 'warning',
|
||||||
|
'DELETE': 'danger',
|
||||||
|
'QUERY': 'info'
|
||||||
|
}
|
||||||
|
return typeMap[type] || 'info'
|
||||||
|
}
|
||||||
|
|
||||||
|
const formatDateTime = (dateTime) => {
|
||||||
|
if (!dateTime) return '-'
|
||||||
|
const date = new Date(dateTime)
|
||||||
|
const year = date.getFullYear()
|
||||||
|
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||||
|
const day = String(date.getDate()).padStart(2, '0')
|
||||||
|
const hours = String(date.getHours()).padStart(2, '0')
|
||||||
|
const minutes = String(date.getMinutes()).padStart(2, '0')
|
||||||
|
const seconds = String(date.getSeconds()).padStart(2, '0')
|
||||||
|
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getOperationLogList()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.operation-log {
|
||||||
|
padding: 20px;
|
||||||
|
background: #f5f7fa;
|
||||||
|
min-height: calc(100vh - 100px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-filters {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-group {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 300px;
|
||||||
|
max-width: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-table {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 16px;
|
||||||
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-top: 20px;
|
||||||
|
padding: 0 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-detail-container {
|
||||||
|
padding: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-agent {
|
||||||
|
word-break: break-all;
|
||||||
|
max-width: 600px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.operation-log {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-filters {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-group {
|
||||||
|
max-width: 100%;
|
||||||
|
min-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-actions {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-actions .el-button {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-table {
|
||||||
|
padding: 8px;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.date-picker-popper {
|
||||||
|
z-index: 9999 !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
import { adminApi,requestUtils } from "@deotaland/utils";
|
||||||
|
export class AdminOperationLog {
|
||||||
|
constructor() {
|
||||||
|
}
|
||||||
|
// 分页查询操作日志列表
|
||||||
|
async getOperationLogList(data) {
|
||||||
|
let params = {
|
||||||
|
adminUserId: data.adminUserId ?? '',
|
||||||
|
adminUsername: data.adminUsername ?? '',
|
||||||
|
operationType: data.operationType ?? '',
|
||||||
|
startTime: data.startTime ?? '',
|
||||||
|
endTime: data.endTime ?? '',
|
||||||
|
pageSize: data.pageSize ?? 10,
|
||||||
|
pageNum: data.pageNum ?? 1,
|
||||||
|
orderByColumn: data.orderByColumn ?? '',
|
||||||
|
isAsc: data.isAsc ?? ''//排序的方向desc或者asc
|
||||||
|
}
|
||||||
|
return requestUtils.common(adminApi.default.getOperationLogList, params);
|
||||||
|
/*
|
||||||
|
返回示例:
|
||||||
|
{
|
||||||
|
"code": 0,
|
||||||
|
"success": true,
|
||||||
|
"data": {
|
||||||
|
"total": 9007199254740991,
|
||||||
|
"rows": [
|
||||||
|
{
|
||||||
|
"id": 1073741824,
|
||||||
|
"adminUserId": 1073741824,
|
||||||
|
"adminUsername": "string",
|
||||||
|
"operationType": "string",
|
||||||
|
"operationTypeDesc": "string",
|
||||||
|
"resourceType": "string",
|
||||||
|
"resourceId": "string",
|
||||||
|
"description": "string",
|
||||||
|
"ipAddress": "string",
|
||||||
|
"userAgent": "string",
|
||||||
|
"createdAt": "2026-01-09T09:10:39.219Z"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"code": 1073741824,
|
||||||
|
"msg": "string"
|
||||||
|
},
|
||||||
|
"message": "操作成功"
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" href="https://draft-user.s3.us-east-2.amazonaws.com/images/2f8e057e-a677-44cd-b709-38245bdec423.svg" />
|
<link rel="icon" href="https://draft-user.s3.us-east-2.amazonaws.com/images/353c3066-f894-4937-8eb7-cd2eb634c4d7.webp" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, minimum-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, minimum-scale=1.0" />
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
|
|
||||||
|
|
@ -166,7 +166,7 @@ const fileInput = ref(null);
|
||||||
const needHook = ref(true);
|
const needHook = ref(true);
|
||||||
|
|
||||||
const isGenerateDisabled = computed(() => {
|
const isGenerateDisabled = computed(() => {
|
||||||
return !formData.value.prompt.trim();
|
return !formData.value.prompt.trim() && !formData.value.previewImage;
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleIpTypeSelect = (type) => {
|
const handleIpTypeSelect = (type) => {
|
||||||
|
|
@ -272,10 +272,9 @@ const handleOptimizePrompt = async () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleGenerate = () => {
|
const handleGenerate = () => {
|
||||||
if (!formData.value.prompt.trim()) {
|
if(!formData.value.prompt.trim() && !formData.value.previewImage){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
emit('generate', {
|
emit('generate', {
|
||||||
ipType: ipType.value,
|
ipType: ipType.value,
|
||||||
prompt: formData.value.prompt,
|
prompt: formData.value.prompt,
|
||||||
|
|
|
||||||
|
|
@ -118,6 +118,7 @@ const formData = ref({
|
||||||
const isTouching = ref(false);
|
const isTouching = ref(false);
|
||||||
const isControlsVisible = ref(false);
|
const isControlsVisible = ref(false);
|
||||||
const cjimg = 'https://draft-user.s3.us-east-2.amazonaws.com/images/14f98f33-06a7-4629-a42e-d7cfbced786f';
|
const cjimg = 'https://draft-user.s3.us-east-2.amazonaws.com/images/14f98f33-06a7-4629-a42e-d7cfbced786f';
|
||||||
|
const e1cjimg='https://draft-user.s3.us-east-2.amazonaws.com/images/23130608-283e-41c3-bb28-8f2492f5e233.png'
|
||||||
const anTypeImg = 'https://draft-user.s3.us-east-2.amazonaws.com/images/1e82b2b6-0e5d-4a62-b65f-098952eb2f67';
|
const anTypeImg = 'https://draft-user.s3.us-east-2.amazonaws.com/images/1e82b2b6-0e5d-4a62-b65f-098952eb2f67';
|
||||||
// const humanTypeImg = 'https://draft-user.s3.us-east-2.amazonaws.com/images/e3e60cc7-9777-41ba-9d1e-f5ffc92e4fac.webp';
|
// const humanTypeImg = 'https://draft-user.s3.us-east-2.amazonaws.com/images/e3e60cc7-9777-41ba-9d1e-f5ffc92e4fac.webp';
|
||||||
// const humanTypeImg = 'https://draft-user.s3.us-east-2.amazonaws.com/images/61770f50-4b87-40a0-9297-cabda0ec6317.webp'
|
// const humanTypeImg = 'https://draft-user.s3.us-east-2.amazonaws.com/images/61770f50-4b87-40a0-9297-cabda0ec6317.webp'
|
||||||
|
|
@ -234,6 +235,10 @@ const props = defineProps({
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
},
|
},
|
||||||
|
series:{//产品类型
|
||||||
|
type: String,
|
||||||
|
default: 'D1'
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// 定义事件
|
// 定义事件
|
||||||
|
|
@ -261,7 +266,10 @@ const handleGenerateImage = async () => {
|
||||||
if(iscjt){
|
if(iscjt){
|
||||||
props.cardData.imgyt&&referenceImages.push(props.cardData.imgyt);
|
props.cardData.imgyt&&referenceImages.push(props.cardData.imgyt);
|
||||||
referenceImages.push(props.cardData.diyPromptImg);
|
referenceImages.push(props.cardData.diyPromptImg);
|
||||||
referenceImages.push(cjimg);
|
referenceImages.push({
|
||||||
|
D1:cjimg,
|
||||||
|
E1:e1cjimg,
|
||||||
|
}[props.series]);
|
||||||
}
|
}
|
||||||
if(props.cardData.diyPromptText||props.cardData.addDiyPromptImg){
|
if(props.cardData.diyPromptText||props.cardData.addDiyPromptImg){
|
||||||
referenceImages.push(props.cardData.diyPromptImg);
|
referenceImages.push(props.cardData.diyPromptImg);
|
||||||
|
|
|
||||||
|
|
@ -23,10 +23,17 @@
|
||||||
<!-- 缩略图显示 -->
|
<!-- 缩略图显示 -->
|
||||||
<div v-if="step.hasThumbnail" class="step-thumbnail">
|
<div v-if="step.hasThumbnail" class="step-thumbnail">
|
||||||
<img
|
<img
|
||||||
|
v-if="series === 'D1'"
|
||||||
src="https://draft-user.s3.us-east-2.amazonaws.com/images/4abf3da6-6abe-4604-adb9-554cf49d9743.webp"
|
src="https://draft-user.s3.us-east-2.amazonaws.com/images/4abf3da6-6abe-4604-adb9-554cf49d9743.webp"
|
||||||
alt="产品零件示例图"
|
alt="产品零件示例图"
|
||||||
class="thumbnail-image"
|
class="thumbnail-image"
|
||||||
/>
|
/>
|
||||||
|
<img
|
||||||
|
v-else-if="series === 'E1'"
|
||||||
|
src="https://draft-user.s3.us-east-2.amazonaws.com/images/1a359f21-1dcd-4167-90ca-7e5eb86a45db.png"
|
||||||
|
alt="产品零件示例图"
|
||||||
|
class="thumbnail-image"
|
||||||
|
/>
|
||||||
<p class="thumbnail-caption">{{ t('orderProcess.steps.inspection.thumbnailCaption') }}</p>
|
<p class="thumbnail-caption">{{ t('orderProcess.steps.inspection.thumbnailCaption') }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -59,10 +66,12 @@ import {
|
||||||
Picture,
|
Picture,
|
||||||
Van
|
Van
|
||||||
} from '@element-plus/icons-vue'
|
} from '@element-plus/icons-vue'
|
||||||
|
import service from '@deotaland/utils/src/utils/request'
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
show: { type: Boolean, default: false },
|
show: { type: Boolean, default: false },
|
||||||
modelData: { type: Object, default: null }
|
modelData: { type: Object, default: null },
|
||||||
|
series: { type: String, default: '' }
|
||||||
})
|
})
|
||||||
const emit = defineEmits(['close', 'acknowledge'])
|
const emit = defineEmits(['close', 'acknowledge'])
|
||||||
const handleOverlayClick = () => onClose()
|
const handleOverlayClick = () => onClose()
|
||||||
|
|
@ -91,12 +100,15 @@ const processSteps = computed(() => [
|
||||||
description: t('orderProcess.steps.scheduling.description'),
|
description: t('orderProcess.steps.scheduling.description'),
|
||||||
time: t('orderProcess.steps.scheduling.time')
|
time: t('orderProcess.steps.scheduling.time')
|
||||||
},
|
},
|
||||||
// {
|
{
|
||||||
// icon: Setting,
|
icon: Setting,
|
||||||
// title: t('orderProcess.steps.production.title'),
|
title: t('orderProcess.steps.production.title'),
|
||||||
// description: t('orderProcess.steps.production.description'),
|
description: t('orderProcess.steps.production.description'),
|
||||||
// time: t('orderProcess.steps.production.time')
|
time: {
|
||||||
// },
|
D1: t('orderProcess.steps.production.time'),
|
||||||
|
E1: t('orderProcess.steps.production.time2'),
|
||||||
|
}[props.series]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
icon: Picture,
|
icon: Picture,
|
||||||
title: t('orderProcess.steps.inspection.title'),
|
title: t('orderProcess.steps.inspection.title'),
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
<router-link to="/" class="brand-link">
|
<router-link to="/" class="brand-link">
|
||||||
<div class="brand-logo">
|
<div class="brand-logo">
|
||||||
<div class="logo-icon">
|
<div class="logo-icon">
|
||||||
<img src="@/assets/logo.webp" alt="Logo" class="logo-image" />
|
<img src="https://draft-user.s3.us-east-2.amazonaws.com/images/353c3066-f894-4937-8eb7-cd2eb634c4d7.webp" alt="Logo" class="logo-image" />
|
||||||
</div>
|
</div>
|
||||||
<span class="brand-name">{{ t('app.title') }}</span>
|
<span class="brand-name">{{ t('app.title') }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -204,7 +204,8 @@ export default {
|
||||||
production: {
|
production: {
|
||||||
title: '模型制作',
|
title: '模型制作',
|
||||||
description: '专业团队使用高精度3D打印机制作您的定制模型,确保每个细节都完美呈现。',
|
description: '专业团队使用高精度3D打印机制作您的定制模型,确保每个细节都完美呈现。',
|
||||||
time: '7-10个工作日'
|
time: '7-10个工作日',
|
||||||
|
time2: '1个工作日',
|
||||||
},
|
},
|
||||||
inspection: {
|
inspection: {
|
||||||
title: '产品检测包装',
|
title: '产品检测包装',
|
||||||
|
|
@ -1755,7 +1756,8 @@ export default {
|
||||||
production: {
|
production: {
|
||||||
title: 'Model Production',
|
title: 'Model Production',
|
||||||
description: 'Professional team uses high-precision 3D printers to create your custom model, ensuring every detail is perfectly presented.',
|
description: 'Professional team uses high-precision 3D printers to create your custom model, ensuring every detail is perfectly presented.',
|
||||||
time: '7-10 working days'
|
time: '7-10 working days',
|
||||||
|
time2: '1-2 working days',
|
||||||
},
|
},
|
||||||
inspection: {
|
inspection: {
|
||||||
title: 'Product Inspection & Packaging',
|
title: 'Product Inspection & Packaging',
|
||||||
|
|
|
||||||
|
|
@ -23,11 +23,14 @@ import 'nprogress/nprogress.css'
|
||||||
import {ElMessage,ElLoading } from 'element-plus'
|
import {ElMessage,ElLoading } from 'element-plus'
|
||||||
import dtUI from '@deotaland/ui'
|
import dtUI from '@deotaland/ui'
|
||||||
import '@deotaland/ui/style.css'
|
import '@deotaland/ui/style.css'
|
||||||
import { environmentUtils } from '@deotaland/utils';
|
import { environmentUtils,WechatBus,isWeChatBrowser } from '@deotaland/utils';
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
window.setElMessage = (options={})=>{
|
window.setElMessage = (options={})=>{
|
||||||
ElMessage[options.type || 'info'](options.message || '请求失败')
|
ElMessage[options.type || 'info'](options.message || '请求失败')
|
||||||
}
|
}
|
||||||
|
if(isWeChatBrowser()){
|
||||||
|
new WechatBus()
|
||||||
|
}
|
||||||
// environmentUtils.detectEnvironment().then((env)=>{
|
// environmentUtils.detectEnvironment().then((env)=>{
|
||||||
// console.log('当前环境:', env)
|
// console.log('当前环境:', env)
|
||||||
// })
|
// })
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@
|
||||||
<!-- 忘记密码和注册链接 -->
|
<!-- 忘记密码和注册链接 -->
|
||||||
<div class="auth-links">
|
<div class="auth-links">
|
||||||
<div class="auth-links-row">
|
<div class="auth-links-row">
|
||||||
<button
|
<!-- <button
|
||||||
v-if="showPhoneLogin"
|
v-if="showPhoneLogin"
|
||||||
type="button"
|
type="button"
|
||||||
class="auth-link phone-login-link"
|
class="auth-link phone-login-link"
|
||||||
|
|
@ -56,7 +56,7 @@
|
||||||
>
|
>
|
||||||
<el-icon class="link-icon"><Phone /></el-icon>
|
<el-icon class="link-icon"><Phone /></el-icon>
|
||||||
<span>{{ t('login.phone_login_link') }}</span>
|
<span>{{ t('login.phone_login_link') }}</span>
|
||||||
</button>
|
</button> -->
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="auth-link forgot-password-link"
|
class="auth-link forgot-password-link"
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
<div class="welcome-content">
|
<div class="welcome-content">
|
||||||
<div class="welcome-left">
|
<div class="welcome-left">
|
||||||
<div class="welcome-logo">
|
<div class="welcome-logo">
|
||||||
<img src="@/assets/logo.webp" alt="Logo" class="welcome-logo-image" />
|
<img src="https://draft-user.s3.us-east-2.amazonaws.com/images/353c3066-f894-4937-8eb7-cd2eb634c4d7.webp" alt="Logo" class="welcome-logo-image" />
|
||||||
<div class="logo-glow"></div>
|
<div class="logo-glow"></div>
|
||||||
</div>
|
</div>
|
||||||
<h1 class="welcome-title">
|
<h1 class="welcome-title">
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,7 @@
|
||||||
@delete="handleDeleteCard(index)"
|
@delete="handleDeleteCard(index)"
|
||||||
@delete-card="handleDeleteCard(index)"
|
@delete-card="handleDeleteCard(index)"
|
||||||
:combinedPromptJson="combinedPromptJson"
|
:combinedPromptJson="combinedPromptJson"
|
||||||
|
:series="series"
|
||||||
@handlePartialEdit="(imageUrl) => handlePartialEdit(imageUrl, index)"
|
@handlePartialEdit="(imageUrl) => handlePartialEdit(imageUrl, index)"
|
||||||
@customize-to-home="handleCustomizeToHome(index)"
|
@customize-to-home="handleCustomizeToHome(index)"
|
||||||
@preview-image="handlePreviewImage"
|
@preview-image="handlePreviewImage"
|
||||||
|
|
@ -136,6 +137,7 @@
|
||||||
/>
|
/>
|
||||||
<!-- 定制到家弹窗 -->
|
<!-- 定制到家弹窗 -->
|
||||||
<OrderProcessModal
|
<OrderProcessModal
|
||||||
|
:series="series"
|
||||||
:show="showOrderProcessModal"
|
:show="showOrderProcessModal"
|
||||||
:modelData="CustomizeModalData"
|
:modelData="CustomizeModalData"
|
||||||
@close="showOrderProcessModal=false"
|
@close="showOrderProcessModal=false"
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,7 @@
|
||||||
:key="card.id"
|
:key="card.id"
|
||||||
:image-url="card.imageUrl"
|
:image-url="card.imageUrl"
|
||||||
:card-data="card"
|
:card-data="card"
|
||||||
|
:series="series"
|
||||||
:combinedPromptJson="combinedPromptJson"
|
:combinedPromptJson="combinedPromptJson"
|
||||||
@save-project="(item)=>{handleSaveProject(index,item,'image')}"
|
@save-project="(item)=>{handleSaveProject(index,item,'image')}"
|
||||||
@customize-to-home="handleCustomizeToHome(index)"
|
@customize-to-home="handleCustomizeToHome(index)"
|
||||||
|
|
@ -124,6 +125,7 @@
|
||||||
@close="showPurchaseModal=false" />
|
@close="showPurchaseModal=false" />
|
||||||
<!-- 定制到家弹窗 -->
|
<!-- 定制到家弹窗 -->
|
||||||
<OrderProcessModal
|
<OrderProcessModal
|
||||||
|
:series="series"
|
||||||
:show="showOrderProcessModal"
|
:show="showOrderProcessModal"
|
||||||
:modelData="CustomizeModalData"
|
:modelData="CustomizeModalData"
|
||||||
@close="showOrderProcessModal=false"
|
@close="showOrderProcessModal=false"
|
||||||
|
|
@ -182,7 +184,7 @@ import ThemeToggle from '@/components/ui/ThemeToggle.vue';
|
||||||
import AddModal from '@/components/AddModal/index.vue';
|
import AddModal from '@/components/AddModal/index.vue';
|
||||||
import PurchaseModal from '@/components/PurchaseModal/index.vue';
|
import PurchaseModal from '@/components/PurchaseModal/index.vue';
|
||||||
import OrderProcessModal from '@/components/OrderProcessModal/index.vue';
|
import OrderProcessModal from '@/components/OrderProcessModal/index.vue';
|
||||||
|
import {WechatBus,isWeChatBrowser} from '@deotaland/utils';
|
||||||
// 引入竖屏卡片组件
|
// 引入竖屏卡片组件
|
||||||
import ShuCard from '@/components/IPCard/shu.vue';
|
import ShuCard from '@/components/IPCard/shu.vue';
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
@ -259,6 +261,13 @@ const getGenerateCount = async ()=>{
|
||||||
}
|
}
|
||||||
//下载图片
|
//下载图片
|
||||||
const downloadImage = async (url) => {
|
const downloadImage = async (url) => {
|
||||||
|
if(isWeChatBrowser()){
|
||||||
|
WechatBus.BusWechartForNavigate('/pages/bus/index',{
|
||||||
|
method: 'download',
|
||||||
|
url: url,
|
||||||
|
})
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const blobUrl = await fileServer.fetchImage(url);
|
const blobUrl = await fileServer.fetchImage(url);
|
||||||
const link = document.createElement('a');
|
const link = document.createElement('a');
|
||||||
|
|
|
||||||
|
|
@ -385,7 +385,8 @@ import { ElMessage } from 'element-plus'
|
||||||
import { useAuthStore } from '@/stores/auth'
|
import { useAuthStore } from '@/stores/auth'
|
||||||
import { UserController } from './index.js'
|
import { UserController } from './index.js'
|
||||||
import {ModernHome} from '../ModernHome/index.js'
|
import {ModernHome} from '../ModernHome/index.js'
|
||||||
import { FileServer } from '@deotaland/utils'
|
import { FileServer,WechatBus } from '@deotaland/utils'
|
||||||
|
const wechatBus = new WechatBus();
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const modernHome = new ModernHome()
|
const modernHome = new ModernHome()
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
|
||||||
|
|
@ -66,8 +66,8 @@ export default defineConfig({
|
||||||
// 配置代理解决CORS问题
|
// 配置代理解决CORS问题
|
||||||
proxy: {
|
proxy: {
|
||||||
'/api': {
|
'/api': {
|
||||||
// target: 'https://api.deotaland.ai',
|
target: 'https://api.deotaland.ai',
|
||||||
target: 'http://api.deotaland.local',
|
// target: 'http://api.deotaland.local',
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
rewrite: (path) => path.replace(/^\/api/, '')
|
rewrite: (path) => path.replace(/^\/api/, '')
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,5 +8,6 @@ const order = {
|
||||||
getInviteCodeList:{url:'/api-base/admin/invite-code/list',method:'GET'},//分页查询邀请码列表
|
getInviteCodeList:{url:'/api-base/admin/invite-code/list',method:'GET'},//分页查询邀请码列表
|
||||||
generateInviteCode:{url:'/api-base/admin/invite-code/generate',method:'POST',isLoading:true},//为用户生成邀请码(支持批量生成)
|
generateInviteCode:{url:'/api-base/admin/invite-code/generate',method:'POST',isLoading:true},//为用户生成邀请码(支持批量生成)
|
||||||
deleteInviteCode:{url:'/api-base/admin/invite-code/CODEID',method:'DELETE',isLoading:true},//根据邀请码ID删除邀请码
|
deleteInviteCode:{url:'/api-base/admin/invite-code/CODEID',method:'DELETE',isLoading:true},//根据邀请码ID删除邀请码
|
||||||
|
getOperationLogList:{url:'/api-base/admin/operation-log/list',method:'GET'},//分页查询操作日志列表
|
||||||
}
|
}
|
||||||
export default order;
|
export default order;
|
||||||
|
|
@ -23,6 +23,7 @@ import prompt from './servers/prompt.js';
|
||||||
import { PayServer } from './servers/payserver.js';
|
import { PayServer } from './servers/payserver.js';
|
||||||
import { LogistIcsService } from './servers/logisticsservice.js';
|
import { LogistIcsService } from './servers/logisticsservice.js';
|
||||||
import * as orderStatus from './utils/orderStatus.js';
|
import * as orderStatus from './utils/orderStatus.js';
|
||||||
|
import { WechatBus } from './utils/wechaBus.js';
|
||||||
// 合并所有工具函数
|
// 合并所有工具函数
|
||||||
const deotalandUtils = {
|
const deotalandUtils = {
|
||||||
string: stringUtils,
|
string: stringUtils,
|
||||||
|
|
@ -44,6 +45,8 @@ const deotalandUtils = {
|
||||||
PayServer,
|
PayServer,
|
||||||
LogistIcsService,
|
LogistIcsService,
|
||||||
orderStatus,
|
orderStatus,
|
||||||
|
WechatBus,
|
||||||
|
isWeChatBrowser,
|
||||||
// 全局常用方法
|
// 全局常用方法
|
||||||
debounce: stringUtils.debounce || createDebounce(),
|
debounce: stringUtils.debounce || createDebounce(),
|
||||||
throttle: stringUtils.throttle || createThrottle(),
|
throttle: stringUtils.throttle || createThrottle(),
|
||||||
|
|
@ -77,8 +80,13 @@ export {
|
||||||
PayServer,
|
PayServer,
|
||||||
LogistIcsService,
|
LogistIcsService,
|
||||||
orderStatus,
|
orderStatus,
|
||||||
|
WechatBus,
|
||||||
|
isWeChatBrowser,
|
||||||
|
}
|
||||||
|
function isWeChatBrowser() {
|
||||||
|
const ua = navigator.userAgent.toLowerCase()
|
||||||
|
return ua.indexOf('micromessenger') !== -1
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建防抖函数
|
* 创建防抖函数
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -1,45 +1,25 @@
|
||||||
export class WechatBus {
|
export class WechatBus {
|
||||||
|
static wx = window.wx || window.jWeixin
|
||||||
constructor() {
|
constructor() {
|
||||||
this.wx = null
|
if (!WechatBus.wx) {
|
||||||
this.isReady = false
|
this.loadJSSDK()
|
||||||
this.init()
|
|
||||||
}
|
|
||||||
|
|
||||||
init() {
|
|
||||||
if (typeof window !== 'undefined') {
|
|
||||||
this.wx = window.wx || window.jWeixin
|
|
||||||
if (this.wx && this.wx.miniProgram) {
|
|
||||||
this.isReady = true
|
|
||||||
console.log('WechatBus initialized successfully')
|
|
||||||
} else {
|
|
||||||
console.warn('Wechat JSSDK not loaded or miniProgram not available')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 加载微信JSSDK
|
||||||
checkReady() {
|
|
||||||
if (!this.isReady || !this.wx || !this.wx.miniProgram) {
|
|
||||||
throw new Error('Wechat JSSDK is not ready. Please ensure JSSDK is loaded.')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loadJSSDK(src = 'https://res.wx.qq.com/open/js/jweixin-1.6.0.js') {
|
loadJSSDK(src = 'https://res.wx.qq.com/open/js/jweixin-1.6.0.js') {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (typeof window === 'undefined') {
|
if (typeof window === 'undefined') {
|
||||||
reject(new Error('Window object not available'))
|
reject(new Error('Window object not available'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (window.wx || window.jWeixin) {
|
if (window.wx || window.jWeixin) {
|
||||||
this.init()
|
|
||||||
resolve(true)
|
resolve(true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const script = document.createElement('script')
|
const script = document.createElement('script')
|
||||||
script.src = src
|
script.src = src
|
||||||
script.onload = () => {
|
script.onload = () => {
|
||||||
this.init()
|
WechatBus.wx = window.wx || window.jWeixin
|
||||||
resolve(true)
|
resolve(true)
|
||||||
}
|
}
|
||||||
script.onerror = () => {
|
script.onerror = () => {
|
||||||
|
|
@ -48,47 +28,20 @@ export class WechatBus {
|
||||||
document.head.appendChild(script)
|
document.head.appendChild(script)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
static objectToUrlParams(obj, prefix = '') {
|
||||||
config(config) {
|
|
||||||
this.checkReady()
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
this.wx.config({
|
|
||||||
debug: config.debug || false,
|
|
||||||
appId: config.appId,
|
|
||||||
timestamp: config.timestamp,
|
|
||||||
nonceStr: config.nonceStr,
|
|
||||||
signature: config.signature,
|
|
||||||
jsApiList: config.jsApiList || []
|
|
||||||
})
|
|
||||||
|
|
||||||
this.wx.ready(() => {
|
|
||||||
console.log('Wechat JSSDK config ready')
|
|
||||||
resolve(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
this.wx.error((res) => {
|
|
||||||
console.error('Wechat JSSDK config error:', res)
|
|
||||||
reject(res)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
objectToUrlParams(obj, prefix = '') {
|
|
||||||
const params = []
|
const params = []
|
||||||
|
|
||||||
for (const key in obj) {
|
for (const key in obj) {
|
||||||
if (obj.hasOwnProperty(key)) {
|
if (obj.hasOwnProperty(key)) {
|
||||||
const value = obj[key]
|
const value = obj[key]
|
||||||
const paramKey = prefix ? `${prefix}[${key}]` : key
|
const paramKey = prefix ? `${prefix}[${key}]` : key
|
||||||
|
|
||||||
if (value !== null && value !== undefined) {
|
if (value !== null && value !== undefined) {
|
||||||
if (typeof value === 'object' && !Array.isArray(value)) {
|
if (typeof value === 'object' && !Array.isArray(value)) {
|
||||||
const nestedParams = this.objectToUrlParams(value, paramKey)
|
const nestedParams = WechatBus.objectToUrlParams(value, paramKey)
|
||||||
params.push(...nestedParams)
|
params.push(...nestedParams)
|
||||||
} else if (Array.isArray(value)) {
|
} else if (Array.isArray(value)) {
|
||||||
value.forEach((item, index) => {
|
value.forEach((item, index) => {
|
||||||
if (typeof item === 'object') {
|
if (typeof item === 'object') {
|
||||||
const nestedParams = this.objectToUrlParams(item, `${paramKey}[${index}]`)
|
const nestedParams = WechatBus.objectToUrlParams(item, `${paramKey}[${index}]`)
|
||||||
params.push(...nestedParams)
|
params.push(...nestedParams)
|
||||||
} else {
|
} else {
|
||||||
params.push(`${encodeURIComponent(paramKey)}[${index}]=${encodeURIComponent(item)}`)
|
params.push(`${encodeURIComponent(paramKey)}[${index}]=${encodeURIComponent(item)}`)
|
||||||
|
|
@ -103,303 +56,18 @@ export class WechatBus {
|
||||||
|
|
||||||
return params
|
return params
|
||||||
}
|
}
|
||||||
|
// 导航到微信小程序页面并且携带通信参数
|
||||||
navigateTo(url, data = {}) {
|
static BusWechartForNavigate(url, data = {}) {
|
||||||
this.checkReady()
|
|
||||||
let finalUrl = url
|
let finalUrl = url
|
||||||
|
|
||||||
if (data && typeof data === 'object' && Object.keys(data).length > 0) {
|
if (data && typeof data === 'object' && Object.keys(data).length > 0) {
|
||||||
const params = this.objectToUrlParams(data)
|
const params = WechatBus.objectToUrlParams(data)
|
||||||
if (params.length > 0) {
|
if (params.length > 0) {
|
||||||
const separator = url.includes('?') ? '&' : '?'
|
const separator = url.includes('?') ? '&' : '?'
|
||||||
finalUrl = url + separator + params.join('&')
|
finalUrl = url + separator + params.join('&')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
WechatBus.wx.miniProgram.navigateTo({
|
||||||
return new Promise((resolve, reject) => {
|
url: finalUrl
|
||||||
this.wx.miniProgram.navigateTo({
|
|
||||||
url: finalUrl,
|
|
||||||
success: (res) => resolve(res),
|
|
||||||
fail: (error) => reject(error)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
navigateBack(delta = 1) {
|
|
||||||
this.checkReady()
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
this.wx.miniProgram.navigateBack({
|
|
||||||
delta,
|
|
||||||
success: (res) => resolve(res),
|
|
||||||
fail: (error) => reject(error)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
redirectTo(url, data = {}) {
|
|
||||||
this.checkReady()
|
|
||||||
let finalUrl = url
|
|
||||||
|
|
||||||
if (data && typeof data === 'object' && Object.keys(data).length > 0) {
|
|
||||||
const params = this.objectToUrlParams(data)
|
|
||||||
if (params.length > 0) {
|
|
||||||
const separator = url.includes('?') ? '&' : '?'
|
|
||||||
finalUrl = url + separator + params.join('&')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
this.wx.miniProgram.redirectTo({
|
|
||||||
url: finalUrl,
|
|
||||||
success: (res) => resolve(res),
|
|
||||||
fail: (error) => reject(error)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
switchTab(url) {
|
|
||||||
this.checkReady()
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
this.wx.miniProgram.switchTab({
|
|
||||||
url,
|
|
||||||
success: (res) => resolve(res),
|
|
||||||
fail: (error) => reject(error)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
reLaunch(url, data = {}) {
|
|
||||||
this.checkReady()
|
|
||||||
let finalUrl = url
|
|
||||||
|
|
||||||
if (data && typeof data === 'object' && Object.keys(data).length > 0) {
|
|
||||||
const params = this.objectToUrlParams(data)
|
|
||||||
if (params.length > 0) {
|
|
||||||
const separator = url.includes('?') ? '&' : '?'
|
|
||||||
finalUrl = url + separator + params.join('&')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
this.wx.miniProgram.reLaunch({
|
|
||||||
url: finalUrl,
|
|
||||||
success: (res) => resolve(res),
|
|
||||||
fail: (error) => reject(error)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
postMessage(data) {
|
|
||||||
this.checkReady()
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
try {
|
|
||||||
this.wx.miniProgram.postMessage({
|
|
||||||
data
|
|
||||||
})
|
|
||||||
resolve(true)
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Post message error:', error)
|
|
||||||
reject(error)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
getEnv() {
|
|
||||||
this.checkReady()
|
|
||||||
return this.wx.miniProgram.getEnv()
|
|
||||||
}
|
|
||||||
|
|
||||||
getAccountInfoSync() {
|
|
||||||
this.checkReady()
|
|
||||||
return this.wx.getAccountInfoSync()
|
|
||||||
}
|
|
||||||
|
|
||||||
setStorageSync(key, data) {
|
|
||||||
this.checkReady()
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
try {
|
|
||||||
this.wx.miniProgram.setStorageSync({
|
|
||||||
key,
|
|
||||||
data
|
|
||||||
})
|
|
||||||
resolve(true)
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Set storage error:', error)
|
|
||||||
reject(error)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
getStorageSync(key) {
|
|
||||||
this.checkReady()
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
try {
|
|
||||||
const data = this.wx.miniProgram.getStorageSync({
|
|
||||||
key
|
|
||||||
})
|
|
||||||
resolve(data)
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Get storage error:', error)
|
|
||||||
reject(error)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
removeStorageSync(key) {
|
|
||||||
this.checkReady()
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
try {
|
|
||||||
this.wx.miniProgram.removeStorageSync({
|
|
||||||
key
|
|
||||||
})
|
|
||||||
resolve(true)
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Remove storage error:', error)
|
|
||||||
reject(error)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
clearStorageSync() {
|
|
||||||
this.checkReady()
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
try {
|
|
||||||
this.wx.miniProgram.clearStorageSync()
|
|
||||||
resolve(true)
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Clear storage error:', error)
|
|
||||||
reject(error)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
chooseImage(options = {}) {
|
|
||||||
this.checkReady()
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const defaultOptions = {
|
|
||||||
count: 1,
|
|
||||||
sizeType: ['original', 'compressed'],
|
|
||||||
sourceType: ['album', 'camera'],
|
|
||||||
...options
|
|
||||||
}
|
|
||||||
|
|
||||||
this.wx.chooseImage({
|
|
||||||
...defaultOptions,
|
|
||||||
success: (res) => resolve(res),
|
|
||||||
fail: (error) => reject(error)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
previewImage(options) {
|
|
||||||
this.checkReady()
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
this.wx.previewImage({
|
|
||||||
...options,
|
|
||||||
success: (res) => resolve(res),
|
|
||||||
fail: (error) => reject(error)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
getLocation(options = {}) {
|
|
||||||
this.checkReady()
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const defaultOptions = {
|
|
||||||
type: 'wgs84',
|
|
||||||
...options
|
|
||||||
}
|
|
||||||
|
|
||||||
this.wx.getLocation({
|
|
||||||
...defaultOptions,
|
|
||||||
success: (res) => resolve(res),
|
|
||||||
fail: (error) => reject(error)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
openLocation(options) {
|
|
||||||
this.checkReady()
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
this.wx.openLocation({
|
|
||||||
...options,
|
|
||||||
success: (res) => resolve(res),
|
|
||||||
fail: (error) => reject(error)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
scanQRCode(options = {}) {
|
|
||||||
this.checkReady()
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const defaultOptions = {
|
|
||||||
needResult: 1,
|
|
||||||
scanType: ['qrCode', 'barCode'],
|
|
||||||
...options
|
|
||||||
}
|
|
||||||
|
|
||||||
this.wx.scanQRCode({
|
|
||||||
...defaultOptions,
|
|
||||||
success: (res) => resolve(res),
|
|
||||||
fail: (error) => reject(error)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
chooseWXPay(options) {
|
|
||||||
this.checkReady()
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
this.wx.chooseWXPay({
|
|
||||||
...options,
|
|
||||||
success: (res) => resolve(res),
|
|
||||||
fail: (error) => reject(error)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
updateAppMessageShareData(options) {
|
|
||||||
this.checkReady()
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
this.wx.updateAppMessageShareData({
|
|
||||||
...options,
|
|
||||||
success: (res) => resolve(res),
|
|
||||||
fail: (error) => reject(error)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
updateTimelineShareData(options) {
|
|
||||||
this.checkReady()
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
this.wx.updateTimelineShareData({
|
|
||||||
...options,
|
|
||||||
success: (res) => resolve(res),
|
|
||||||
fail: (error) => reject(error)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
closeWindow() {
|
|
||||||
this.checkReady()
|
|
||||||
this.wx.closeWindow()
|
|
||||||
}
|
|
||||||
|
|
||||||
hideMenuItems(menuList = []) {
|
|
||||||
this.checkReady()
|
|
||||||
this.wx.hideMenuItems({
|
|
||||||
menuList
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
showMenuItems(menuList = []) {
|
|
||||||
this.checkReady()
|
|
||||||
this.wx.showMenuItems({
|
|
||||||
menuList
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const wechatBus = new WechatBus()
|
|
||||||
|
|
||||||
export default wechatBus
|
|
||||||
export { WechatBus }
|
|
||||||
Loading…
Reference in New Issue