zrd пре 4 месеци
родитељ
комит
4894052d56

+ 2 - 2
.env.dev

@@ -4,7 +4,7 @@ NODE_ENV=production
 VITE_DEV=true
 
 # 请求路径
-VITE_BASE_URL='http://api-dashboard.yudao.iocoder.cn'
+VITE_BASE_URL='http://42.194.163.46:9095'
 
 # 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持S3服务
 VITE_UPLOAD_TYPE=server
@@ -22,7 +22,7 @@ VITE_DROP_CONSOLE=false
 VITE_SOURCEMAP=true
 
 # 打包路径
-VITE_BASE_PATH=/
+VITE_BASE_PATH=/admin/
 
 # 输出路径
 VITE_OUT_DIR=dist

+ 2 - 2
.env.local

@@ -4,7 +4,7 @@ NODE_ENV=development
 VITE_DEV=true
 
 # 请求路径
-VITE_BASE_URL='http://localhost:48080'
+VITE_BASE_URL='http://42.194.163.46:9095'
 
 # 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持 S3 服务
 VITE_UPLOAD_TYPE=server
@@ -22,7 +22,7 @@ VITE_DROP_CONSOLE=false
 VITE_SOURCEMAP=false
 
 # 打包路径
-VITE_BASE_PATH=/
+VITE_BASE_PATH=/admin/
 
 # 商城H5会员端域名
 VITE_MALL_H5_DOMAIN='http://localhost:3000'

+ 48 - 0
src/api/game/characterinfo/index.ts

@@ -0,0 +1,48 @@
+import request from '@/config/axios'
+
+// 游戏角色信息 VO
+export interface CharacterInfoVO {
+  id: number // 角色 ID
+  playerName: string // 玩家名称
+  level: string // 角色等级
+  combatPower: number // 角色战力
+  profession: string // 角色职业
+}
+
+// 游戏角色信息 API
+export const CharacterInfoApi = {
+  // 查询游戏角色信息分页
+  getCharacterInfoPage: async (params: any) => {
+    return await request.get({ url: `/game/character-info/page`, params })
+  },
+
+  // 查询游戏角色信息详情
+  getCharacterInfo: async (id: number) => {
+    return await request.get({ url: `/game/character-info/get?id=` + id })
+  },
+
+  // 新增游戏角色信息
+  createCharacterInfo: async (data: CharacterInfoVO) => {
+    return await request.post({ url: `/game/character-info/create`, data })
+  },
+
+  // 修改游戏角色信息
+  updateCharacterInfo: async (data: CharacterInfoVO) => {
+    return await request.put({ url: `/game/character-info/update`, data })
+  },
+
+  // 删除游戏角色信息
+  deleteCharacterInfo: async (id: number) => {
+    return await request.delete({ url: `/game/character-info/delete?id=` + id })
+  },
+
+  // 导出游戏角色信息 Excel
+  exportCharacterInfo: async (params) => {
+    return await request.download({ url: `/game/character-info/export-excel`, params })
+  },
+
+  // 导入客户
+  handleImport: async (formData) => {
+    return await request.upload({ url: `/game/character-info/import`, data: formData })
+  }
+}

+ 2 - 0
src/layout/Layout.vue

@@ -3,6 +3,7 @@ import { computed, defineComponent, unref } from 'vue'
 import { useAppStore } from '@/store/modules/app'
 import { Backtop } from '@/components/Backtop'
 import { Setting } from '@/layout/components/Setting'
+import AI from '@/layout/components/AI/index.vue'
 import { useRenderLayout } from './components/useRenderLayout'
 import { useDesign } from '@/hooks/web/useDesign'
 
@@ -60,6 +61,7 @@ export default defineComponent({
         <Backtop></Backtop>
 
         <Setting></Setting>
+        <AI></AI>
       </section>
     )
   }

+ 22 - 0
src/layout/components/AI/index.vue

@@ -0,0 +1,22 @@
+<script lang="ts" setup>
+import { ref } from 'vue'
+const drawer = ref(false)
+</script>
+
+<template>
+  <div
+    class="fixed right-0 top-[55%] h-40px w-40px cursor-pointer bg-[var(--el-color-primary)] text-center leading-40px"
+    @click="drawer = true"
+  >
+    <!-- <Icon color="#fff" icon="ep:setting" /> -->
+    ai
+  </div>
+  <el-dialog fullscreen v-model="drawer" title="AI助手">
+    <iframe
+      style="width: 100%; height: 90vh"
+      src="http://192.168.0.43:29012/chatbot/A5eXkRACh5WtfS87"
+    ></iframe>
+  </el-dialog>
+</template>
+
+<style lang="scss" scoped></style>

+ 2 - 1
src/layout/components/Setting/index.ts

@@ -1,3 +1,4 @@
 import Setting from './src/Setting.vue'
+import Setting1 from './src/Setting1.vue'
 
-export { Setting }
+export { Setting, Setting1 }

+ 41 - 0
src/layout/components/Setting/src/Setting1.vue

@@ -0,0 +1,41 @@
+<template>
+  <div>
+    <!-- 这里可以放置其他内容 -->
+  </div>
+</template>
+
+<script setup>
+import { onMounted } from 'vue'
+
+onMounted(() => {
+  // 配置 Dify 聊天机器人
+  window.difyChatbotConfig = {
+    token: 'A5eXkRACh5WtfS87',
+    baseUrl: 'http://192.168.0.43:29012'
+  }
+
+  // 创建 script 元素
+  const script = document.createElement('script')
+  script.src = 'http://192.168.0.43:29012/embed.min.js'
+  script.id = 'A5eXkRACh5WtfS87'
+  script.defer = true
+
+  // 将 script 元素添加到文档中
+  document.head.appendChild(script)
+
+  // 创建 style 元素
+  const style = document.createElement('style')
+  style.textContent = `
+    #dify-chatbot-bubble-button {
+      background-color: #1C64F2 !important;
+    }
+    #dify-chatbot-bubble-window {
+      width: 24rem !important;
+      height: 40rem !important;
+    }
+  `
+
+  // 将 style 元素添加到文档中
+  document.head.appendChild(style)
+})
+</script>

+ 1 - 0
src/permission.ts

@@ -49,6 +49,7 @@ const parseURL = (
 // 路由不重定向白名单
 const whiteList = [
   '/login',
+  '/login1',
   '/social-login',
   '/auth-redirect',
   '/bind',

+ 110 - 0
src/views/game/characterinfo/CharacterInfoForm.vue

@@ -0,0 +1,110 @@
+<template>
+  <Dialog :title="dialogTitle" v-model="dialogVisible">
+    <el-form
+      ref="formRef"
+      :model="formData"
+      :rules="formRules"
+      label-width="100px"
+      v-loading="formLoading"
+    >
+      <el-form-item label="玩家名称" prop="playerName">
+        <el-input v-model="formData.playerName" placeholder="请输入玩家名称" />
+      </el-form-item>
+      <el-form-item label="角色等级" prop="level">
+        <el-input v-model="formData.level" placeholder="请输入角色等级" />
+      </el-form-item>
+      <el-form-item label="角色战力" prop="combatPower">
+        <el-input v-model="formData.combatPower" placeholder="请输入角色战力" />
+      </el-form-item>
+      <el-form-item label="角色职业" prop="profession">
+        <el-input v-model="formData.profession" placeholder="请输入角色职业" />
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
+      <el-button @click="dialogVisible = false">取 消</el-button>
+    </template>
+  </Dialog>
+</template>
+<script setup lang="ts">
+import { CharacterInfoApi, CharacterInfoVO } from '@/api/game/characterinfo'
+
+/** 游戏角色信息 表单 */
+defineOptions({ name: 'CharacterInfoForm' })
+
+const { t } = useI18n() // 国际化
+const message = useMessage() // 消息弹窗
+
+const dialogVisible = ref(false) // 弹窗的是否展示
+const dialogTitle = ref('') // 弹窗的标题
+const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
+const formType = ref('') // 表单的类型:create - 新增;update - 修改
+const formData = ref({
+  id: undefined,
+  playerName: undefined,
+  level: undefined,
+  combatPower: undefined,
+  profession: undefined,
+})
+const formRules = reactive({
+  playerName: [{ required: true, message: '玩家名称不能为空', trigger: 'blur' }],
+  level: [{ required: true, message: '角色等级不能为空', trigger: 'blur' }],
+  combatPower: [{ required: true, message: '角色战力不能为空', trigger: 'blur' }],
+  profession: [{ required: true, message: '角色职业不能为空', trigger: 'blur' }],
+})
+const formRef = ref() // 表单 Ref
+
+/** 打开弹窗 */
+const open = async (type: string, id?: number) => {
+  dialogVisible.value = true
+  dialogTitle.value = t('action.' + type)
+  formType.value = type
+  resetForm()
+  // 修改时,设置数据
+  if (id) {
+    formLoading.value = true
+    try {
+      formData.value = await CharacterInfoApi.getCharacterInfo(id)
+    } finally {
+      formLoading.value = false
+    }
+  }
+}
+defineExpose({ open }) // 提供 open 方法,用于打开弹窗
+
+/** 提交表单 */
+const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
+const submitForm = async () => {
+  // 校验表单
+  await formRef.value.validate()
+  // 提交请求
+  formLoading.value = true
+  try {
+    const data = formData.value as unknown as CharacterInfoVO
+    if (formType.value === 'create') {
+      await CharacterInfoApi.createCharacterInfo(data)
+      message.success(t('common.createSuccess'))
+    } else {
+      await CharacterInfoApi.updateCharacterInfo(data)
+      message.success(t('common.updateSuccess'))
+    }
+    dialogVisible.value = false
+    // 发送操作成功的事件
+    emit('success')
+  } finally {
+    formLoading.value = false
+  }
+}
+
+/** 重置表单 */
+const resetForm = () => {
+  formData.value = {
+    id: undefined,
+    playerName: undefined,
+    level: undefined,
+    combatPower: undefined,
+    profession: undefined,
+  }
+  formRef.value?.resetFields()
+}
+</script>

+ 130 - 0
src/views/game/characterinfo/ImageImportForm.vue

@@ -0,0 +1,130 @@
+<template>
+  <Dialog v-model="dialogVisible" title="图片上传" width="400">
+    <el-upload
+      ref="uploadRef"
+      v-model:file-list="fileList"
+      :action="importUrl + '?updateSupport=' + updateSupport"
+      :auto-upload="false"
+      :disabled="formLoading"
+      :headers="uploadHeaders"
+      :limit="1"
+      :on-error="submitFormError"
+      :on-exceed="handleExceed"
+      :on-success="submitFormSuccess"
+      accept=".jpg, .png"
+      drag
+    >
+      <Icon icon="ep:upload" />
+      <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
+      <template #tip>
+        <div class="el-upload__tip text-center">
+          <div class="el-upload__tip">
+            <el-checkbox v-model="updateSupport" />
+            是否更新已经存在的用户数据
+          </div>
+          <span>仅允许导入 jpg、png 格式文件。</span>
+        </div>
+      </template>
+    </el-upload>
+    <template #footer>
+      <el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
+      <el-button @click="dialogVisible = false">取 消</el-button>
+    </template>
+  </Dialog>
+</template>
+<script lang="ts" setup>
+import * as UserApi from '@/api/system/user'
+import { getAccessToken, getTenantId } from '@/utils/auth'
+import download from '@/utils/download'
+
+defineOptions({ name: 'SystemUserImportForm' })
+
+const message = useMessage() // 消息弹窗
+
+const dialogVisible = ref(false) // 弹窗的是否展示
+const formLoading = ref(false) // 表单的加载中
+const uploadRef = ref()
+const importUrl =
+  import.meta.env.VITE_BASE_URL + import.meta.env.VITE_API_URL + '/game/character-info/import'
+const uploadHeaders = ref() // 上传 Header 头
+const fileList = ref([]) // 文件列表
+const updateSupport = ref(0) // 是否更新已经存在的用户数据
+
+/** 打开弹窗 */
+const open = () => {
+  dialogVisible.value = true
+  updateSupport.value = 0
+  fileList.value = []
+  resetForm()
+}
+defineExpose({ open }) // 提供 open 方法,用于打开弹窗
+
+/** 提交表单 */
+const submitForm = async () => {
+  if (fileList.value.length == 0) {
+    message.error('请上传文件')
+    return
+  }
+  // 提交请求
+  uploadHeaders.value = {
+    Authorization: 'Bearer ' + getAccessToken(),
+    'tenant-id': getTenantId()
+  }
+  formLoading.value = true
+  uploadRef.value!.submit()
+}
+
+/** 文件上传成功 */
+const emits = defineEmits(['success'])
+const submitFormSuccess = (response: any) => {
+  if (response.code !== 0) {
+    message.error(response.msg)
+    formLoading.value = false
+    return
+  }
+  // 拼接提示语
+  const data = response.data
+  let text = '上传成功数量:' + data.createUsernames.length + ';'
+  for (let username of data.createUsernames) {
+    text += '< ' + username + ' >'
+  }
+  text += '更新成功数量:' + data.updateUsernames.length + ';'
+  for (const username of data.updateUsernames) {
+    text += '< ' + username + ' >'
+  }
+  text += '更新失败数量:' + Object.keys(data.failureUsernames).length + ';'
+  for (const username in data.failureUsernames) {
+    text += '< ' + username + ': ' + data.failureUsernames[username] + ' >'
+  }
+  message.alert(text)
+  formLoading.value = false
+  dialogVisible.value = false
+  // 发送操作成功的事件
+  emits('success')
+}
+
+/** 上传错误提示 */
+const submitFormError = (): void => {
+  message.error('上传失败,请您重新上传!')
+  formLoading.value = false
+}
+
+/** 重置表单 */
+const resetForm = async (): Promise<void> => {
+  // 重置上传状态和文件
+  formLoading.value = false
+  await nextTick()
+  uploadRef.value?.clearFiles()
+}
+
+/** 文件数超出提示 */
+const handleExceed = (): void => {
+  message.error('最多只能上传一个文件!')
+}
+
+/** 下载模板操作 */
+const importTemplate = async () => {
+  const res = await UserApi.importUserTemplate()
+  download.excel(res, '用户导入模版.xls')
+}
+</script>

+ 244 - 0
src/views/game/characterinfo/index.vue

@@ -0,0 +1,244 @@
+<template>
+  <ContentWrap>
+    <!-- 搜索工作栏 -->
+    <el-form
+      ref="queryFormRef"
+      :inline="true"
+      :model="queryParams"
+      class="-mb-15px"
+      label-width="68px"
+    >
+      <el-form-item label="玩家名称" prop="playerName">
+        <el-input
+          v-model="queryParams.playerName"
+          class="!w-240px"
+          clearable
+          placeholder="请输入玩家名称"
+          @keyup.enter="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="角色等级" prop="level">
+        <el-input
+          v-model="queryParams.level"
+          class="!w-240px"
+          clearable
+          placeholder="请输入角色等级"
+          @keyup.enter="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="角色战力" prop="combatPower">
+        <el-input
+          v-model="queryParams.combatPower"
+          class="!w-240px"
+          clearable
+          placeholder="请输入角色战力"
+          @keyup.enter="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="角色职业" prop="profession">
+        <el-input
+          v-model="queryParams.profession"
+          class="!w-240px"
+          clearable
+          placeholder="请输入角色职业"
+          @keyup.enter="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="创建时间" prop="createTime">
+        <el-date-picker
+          v-model="queryParams.createTime"
+          :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
+          class="!w-220px"
+          end-placeholder="结束日期"
+          start-placeholder="开始日期"
+          type="daterange"
+          value-format="YYYY-MM-DD HH:mm:ss"
+        />
+      </el-form-item>
+      <el-form-item>
+        <el-button @click="handleQuery">
+          <Icon class="mr-5px" icon="ep:search" />
+          搜索
+        </el-button>
+        <el-button @click="resetQuery">
+          <Icon class="mr-5px" icon="ep:refresh" />
+          重置
+        </el-button>
+        <el-button
+          v-hasPermi="['game:character-info:create']"
+          plain
+          type="primary"
+          @click="openForm('create')"
+        >
+          <Icon class="mr-5px" icon="ep:plus" />
+          新增
+        </el-button>
+        <el-button
+          v-hasPermi="['game:character-info:export']"
+          :loading="exportLoading"
+          plain
+          type="success"
+          @click="handleExport"
+        >
+          <Icon class="mr-5px" icon="ep:download" />
+          导出
+        </el-button>
+        <el-button
+          v-hasPermi="['game:character-info:export']"
+          :loading="exportLoading"
+          plain
+          type="success"
+          @click="handleImport"
+        >
+          <Icon class="mr-5px" icon="ep:download" />
+          AI识别
+        </el-button>
+      </el-form-item>
+    </el-form>
+  </ContentWrap>
+
+  <!-- 列表 -->
+  <ContentWrap>
+    <el-table v-loading="loading" :data="list" :show-overflow-tooltip="true" :stripe="true">
+      <el-table-column align="center" label="角色 ID" prop="id" />
+      <el-table-column align="center" label="玩家名称" prop="playerName" />
+      <el-table-column align="center" label="角色等级" prop="level" />
+      <el-table-column align="center" label="角色战力" prop="combatPower" />
+      <el-table-column align="center" label="角色职业" prop="profession" />
+      <el-table-column
+        :formatter="dateFormatter"
+        align="center"
+        label="创建时间"
+        prop="createTime"
+        width="180px"
+      />
+      <el-table-column align="center" label="操作" min-width="120px">
+        <template #default="scope">
+          <el-button
+            v-hasPermi="['game:character-info:update']"
+            link
+            type="primary"
+            @click="openForm('update', scope.row.id)"
+          >
+            编辑
+          </el-button>
+          <el-button
+            v-hasPermi="['game:character-info:delete']"
+            link
+            type="danger"
+            @click="handleDelete(scope.row.id)"
+          >
+            删除
+          </el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <!-- 分页 -->
+    <Pagination
+      v-model:limit="queryParams.pageSize"
+      v-model:page="queryParams.pageNo"
+      :total="total"
+      @pagination="getList"
+    />
+  </ContentWrap>
+
+  <!-- 表单弹窗:添加/修改 -->
+  <CharacterInfoForm ref="formRef" @success="getList" />
+  <ImageImportForm ref="formImageRef" @success="getList" />
+</template>
+
+<script lang="ts" setup>
+import { dateFormatter } from '@/utils/formatTime'
+import download from '@/utils/download'
+import { CharacterInfoApi, CharacterInfoVO } from '@/api/game/characterinfo'
+import CharacterInfoForm from './CharacterInfoForm.vue'
+import ImageImportForm from './ImageImportForm.vue'
+
+/** 游戏角色信息 列表 */
+defineOptions({ name: 'CharacterInfo' })
+
+const message = useMessage() // 消息弹窗
+const { t } = useI18n() // 国际化
+
+const loading = ref(true) // 列表的加载中
+const list = ref<CharacterInfoVO[]>([]) // 列表的数据
+const total = ref(0) // 列表的总页数
+const queryParams = reactive({
+  pageNo: 1,
+  pageSize: 10,
+  playerName: undefined,
+  level: undefined,
+  combatPower: undefined,
+  profession: undefined,
+  createTime: []
+})
+const queryFormRef = ref() // 搜索的表单
+const exportLoading = ref(false) // 导出的加载中
+
+/** 查询列表 */
+const getList = async () => {
+  loading.value = true
+  try {
+    const data = await CharacterInfoApi.getCharacterInfoPage(queryParams)
+    list.value = data.list
+    total.value = data.total
+  } finally {
+    loading.value = false
+  }
+}
+/** 用户导入 */
+const formImageRef = ref()
+const handleImport = () => {
+  formImageRef.value.open()
+}
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.pageNo = 1
+  getList()
+}
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value.resetFields()
+  handleQuery()
+}
+
+/** 添加/修改操作 */
+const formRef = ref()
+const openForm = (type: string, id?: number) => {
+  formRef.value.open(type, id)
+}
+
+/** 删除按钮操作 */
+const handleDelete = async (id: number) => {
+  try {
+    // 删除的二次确认
+    await message.delConfirm()
+    // 发起删除
+    await CharacterInfoApi.deleteCharacterInfo(id)
+    message.success(t('common.delSuccess'))
+    // 刷新列表
+    await getList()
+  } catch {}
+}
+
+/** 导出按钮操作 */
+const handleExport = async () => {
+  try {
+    // 导出的二次确认
+    await message.exportConfirm()
+    // 发起导出
+    exportLoading.value = true
+    const data = await CharacterInfoApi.exportCharacterInfo(queryParams)
+    download.excel(data, '游戏角色信息.xls')
+  } catch {
+  } finally {
+    exportLoading.value = false
+  }
+}
+
+/** 初始化 **/
+onMounted(() => {
+  getList()
+})
+</script>