1.修复 平台 搜索租户接口bug

2.平台页面 添加生成租户系统URL,和二维码!
This commit is contained in:
Wang Chen Chen
2023-11-15 18:36:14 +08:00
parent 2110637372
commit 62a84236cb
13 changed files with 131 additions and 10 deletions

View File

@@ -2,3 +2,6 @@
## 项目标题
VITE_APP_TITLE = "Molly平台管理系统"
## 租户系统域名链接。如: molly.xaaef.com 或者 .xaaef.com
VITE_TENANT_LINK = "molly.xaaef.com:8555/tenant/"

View File

@@ -1,7 +1,7 @@
# 开发环境自定义的环境变量(命名必须以 VITE_ 开头)
## 后端接口公共路径(如果解决跨域问题采用反向代理就只需写公共路径)
VITE_BASE_API = 'http://localhost:18891'
VITE_BASE_API = 'http://127.0.0.1:18891'
## 路由模式 hash 或 html5
VITE_ROUTER_HISTORY = 'hash'

View File

@@ -38,6 +38,7 @@
"path-browserify": "1.0.1",
"path-to-regexp": "6.2.1",
"pinia": "2.1.7",
"qrcode-vue3": "^1.6.8",
"screenfull": "6.0.2",
"sockjs-client": "^1.6.1",
"uuid": "^9.0.1",

View File

@@ -50,6 +50,9 @@ dependencies:
pinia:
specifier: 2.1.7
version: 2.1.7(typescript@5.2.2)(vue@3.3.5)
qrcode-vue3:
specifier: ^1.6.8
version: 1.6.8
screenfull:
specifier: 6.0.2
version: 6.0.2
@@ -4179,6 +4182,16 @@ packages:
engines: {node: '>=6'}
dev: true
/qrcode-generator@1.4.4:
resolution: {integrity: sha512-HM7yY8O2ilqhmULxGMpcHSF1EhJJ9yBj8gvDEuZ6M+KGJ0YY2hKpnXvRD+hZPLrDVck3ExIGhmPtSdcjC+guuw==}
dev: false
/qrcode-vue3@1.6.8:
resolution: {integrity: sha512-LtMnwKWi58ZqHbXBcsTF/VxDYhI6RrBIrDQw8fbDVlO8p5tJBZa7TaIaVYLY937vKO2WCEBmOKksGlpm5ccEIg==}
dependencies:
qrcode-generator: 1.4.4
dev: false
/query-string@4.3.4:
resolution: {integrity: sha512-O2XLNDBIg1DnTOa+2XrIwSiXEV8h2KImXUnjhhn2+UsvZ+Es2uyd5CCRTNQlDGbzUQOW3aYCBx9rVA6dzsiY7Q==}
engines: {node: '>=0.10.0'}

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1700031003005" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6004" width="32" height="32" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M384 64l-249.6 0c-51.2 0-89.6 41.6-89.6 89.6l0 227.2c0 51.2 41.6 89.6 89.6 89.6l249.6 0c51.2 0 89.6-41.6 89.6-89.6l0-227.2C473.6 105.6 435.2 64 384 64zM428.8 380.8c0 25.6-19.2 44.8-44.8 44.8l-249.6 0c-25.6 0-44.8-19.2-44.8-44.8l0-227.2c0-25.6 19.2-44.8 44.8-44.8l249.6 0c25.6 0 44.8 19.2 44.8 44.8L428.8 380.8z" p-id="6005"></path><path d="M192 192l134.4 0 0 134.4-134.4 0 0-134.4Z" p-id="6006"></path><path d="M377.6 544l-243.2 0c-48 0-86.4 38.4-86.4 89.6l0 220.8c0 48 38.4 89.6 86.4 89.6l243.2 0c48 0 86.4-38.4 86.4-89.6l0-220.8C467.2 582.4 425.6 544 377.6 544zM422.4 851.2c0 25.6-19.2 44.8-44.8 44.8l-243.2 0c-25.6 0-44.8-19.2-44.8-44.8l0-220.8c0-25.6 19.2-44.8 44.8-44.8l243.2 0c25.6 0 44.8 19.2 44.8 44.8L422.4 851.2z" p-id="6007"></path><path d="M192 668.8l131.2 0 0 131.2-131.2 0 0-131.2Z" p-id="6008"></path><path d="M633.6 470.4l249.6 0c51.2 0 89.6-41.6 89.6-89.6l0-227.2c0-51.2-41.6-89.6-89.6-89.6l-249.6 0c-51.2 0-89.6 41.6-89.6 89.6l0 227.2C544 432 585.6 470.4 633.6 470.4zM588.8 153.6c0-25.6 19.2-44.8 44.8-44.8l249.6 0c25.6 0 44.8 19.2 44.8 44.8l0 227.2c0 25.6-19.2 44.8-44.8 44.8l-249.6 0c-25.6 0-44.8-19.2-44.8-44.8L588.8 153.6z" p-id="6009"></path><path d="M700.8 192l134.4 0 0 134.4-134.4 0 0-134.4Z" p-id="6010"></path><path d="M572.8 716.8l137.6 0c12.8 0 22.4-9.6 22.4-22.4l0-137.6c0-12.8-9.6-22.4-22.4-22.4l-137.6 0c-12.8 0-22.4 9.6-22.4 22.4l0 137.6C550.4 707.2 560 716.8 572.8 716.8z" p-id="6011"></path><path d="M886.4 563.2l0 38.4c0 12.8 12.8 25.6 25.6 25.6l38.4 0c12.8 0 25.6-12.8 25.6-25.6l0-38.4c0-12.8-12.8-25.6-25.6-25.6l-38.4 0C899.2 537.6 886.4 547.2 886.4 563.2z" p-id="6012"></path><path d="M582.4 944l48 0c12.8 0 22.4-9.6 22.4-22.4l0-48c0-12.8-9.6-22.4-22.4-22.4l-48 0c-12.8 0-22.4 9.6-22.4 22.4l0 48C560 934.4 569.6 944 582.4 944z" p-id="6013"></path><path d="M944 704l-99.2 0c-16 0-28.8 12.8-28.8 28.8l0 44.8-48 0c-19.2 0-32 12.8-32 32l0 99.2c0 16 12.8 28.8 28.8 28.8l179.2 3.2c16 0 28.8-12.8 28.8-28.8l0-179.2C972.8 716.8 960 704 944 704z" p-id="6014"></path></svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -2,6 +2,8 @@ import dayjs from "dayjs"
import { removeConfigLayout } from "@/utils/cache/local-storage"
import chinaAreaJson from "@/assets/ChinaArea.json"
import { ISimpleProject, ISimpleTenant } from "@/types/base"
import { getFileBlob, httpGet } from "./service"
import { AxiosResponse } from "axios"
//#region 格式化日期时间
export const DEFAULT_DATE_TIME_PATTERN = "YYYY-MM-DD HH:mm:ss"

View File

@@ -8,6 +8,7 @@ import { useProjectStoreHook } from "@/store/modules/project"
import { getEnvBaseURLPrefix } from "."
import { ISimpleProject, ISimpleTenant } from "@/types/base"
import { defaultTenant } from "@/utils"
import { v4 as uuidv4 } from "uuid"
/** 创建请求实例 */
function createService() {
@@ -26,7 +27,7 @@ function createService() {
const apiData = response.data
// 二进制数据则直接返回
const responseType = response.request?.responseType
if (responseType === "blob" || responseType === "arraybuffer") return apiData
if (responseType === "blob" || responseType === "arraybuffer") return response
// 这个 code 是和后端约定的业务 code
const code = apiData.status
// 如果没有 code, 代表这不是项目后端开发的 api
@@ -217,4 +218,41 @@ function httpDelete<T, P>(url: string, params?: T): Promise<P> {
return httpRequest<P>({ method: "delete", url, params })
}
export { httpRequest, httpGet, httpPost, httpPut, httpDelete, axiosRequest }
/** 单独抽离的 文件下载 工具函数 */
async function downloadFile(url: string, fileName?: string): Promise<void> {
const { data, headers } = await axios.get(url, { responseType: "blob" })
if (!fileName) {
const fn1 = headers["content-disposition"]
if (fn1) {
fileName = fn1.replace(/\w+;filename=(.*)/, "$1")
} else {
fileName = uuidv4().replace(/-/g, "")
}
}
const urlObject = window.URL || window.webkitURL
const downloadUrl = urlObject.createObjectURL(data)
const link = document.createElement("a")
link.href = downloadUrl
link.download = fileName!
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
urlObject.revokeObjectURL(downloadUrl)
}
/** 单独抽离的 文件下载 工具函数 */
function getFileBlob(url: string): Promise<Blob> {
// 单独处理自定义请求/响应回掉
return new Promise((resolve, reject) => {
axios
.get(url, { responseType: "blob" })
.then((resp) => {
const { data, headers } = resp
const blob = new Blob([data], { type: headers["content-type"] })
resolve(blob)
})
.catch((error) => reject(error))
})
}
export { httpRequest, httpGet, httpPost, httpPut, httpDelete, axiosRequest, downloadFile, getFileBlob }

View File

@@ -91,7 +91,11 @@
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item v-if="!isDefaultTenantId(scope.row.tenantId)" :icon="Link" command="ResetData">
<el-dropdown-item :icon="Link" command="Jump">跳转客户系统</el-dropdown-item>
<el-dropdown-item command="QRcode">
<template #default><SvgIcon name="qrcode" />&nbsp;生成二维码</template>
</el-dropdown-item>
<el-dropdown-item v-if="!isDefaultTenantId(scope.row.tenantId)" :icon="Refresh" command="ResetData">
重置数据
</el-dropdown-item>
<el-dropdown-item v-if="!isDefaultTenantId(scope.row.tenantId)" :icon="Delete" command="Delete">
@@ -118,6 +122,24 @@
</div>
</el-card>
<!-- 生成二维码弹窗 -->
<el-dialog v-model="qrCodeDialog.visible" :title="qrCodeDialog.title" width="500px">
<div style="text-align: center">
<el-link type="primary" :href="qrCodeDialog.link" target="_blank">
{{ qrCodeDialog.link }}
</el-link>
<br />
<br />
<QRCodeVue3
:key="qrCodeDialog.tenantId"
:width="300"
:value="qrCodeDialog.link"
:imageOptions="{ hideBackgroundDots: true, imageSize: 0.4, margin: 0 }"
:image="qrCodeDialog.logo"
/>
</div>
</el-dialog>
<!-- 新增和修改的弹窗 -->
<el-dialog v-model="dialogVisible" :title="dialogTitle" width="50%" :close-on-click-modal="false">
<template v-if="saveFlag">
@@ -251,7 +273,7 @@
<script setup lang="ts">
import { ref, reactive, onMounted, computed, watch } from "vue"
import { Plus, Edit, Delete, Search, UserFilled, Loading, Link } from "@element-plus/icons-vue"
import { Plus, Edit, Delete, Search, UserFilled, Loading, Link, Refresh } from "@element-plus/icons-vue"
import { ElMessage, ElMessageBox, FormInstance, FormRules } from "element-plus"
import { queryTenantApi, updateTenantApi, deleteTenantApi, resetDataTenantApi } from "@/api/tenant"
import { listTemplateApi } from "@/api/template"
@@ -265,6 +287,9 @@ import { showExpiredDateAgo, showStringOverflow, showChinaArea, isDefaultTenantI
import { isEmail, isTelphone } from "@/utils/validate"
import { futureShortcuts } from "@/utils"
import { useNoticeStoreHook } from "@/store/modules/notice"
import QRCodeVue3 from "qrcode-vue3"
import logoPng from "@/assets/layouts/logo.png?url"
import { downloadFile, getFileBlob } from "@/utils/service"
const dictStore = useDictStoreHook()
@@ -470,11 +495,47 @@ const handleCommand = (command: string, data: ISysTenant) => {
case "Delete":
handleDelete(data)
break
case "Jump":
window.open(genTenantLink(data.tenantId), "_blank")
break
case "QRcode":
getFileBlob(data.logo)
.then((blob) => {
const reader = new FileReader()
reader.readAsDataURL(blob)
reader.onload = () => {
qrCodeDialog.value.title = `扫码进入 ${data.name} 客户管理系统`
qrCodeDialog.value.link = genTenantLink(data.tenantId)
qrCodeDialog.value.tenantId = data.tenantId
const base64 = reader.result as string
qrCodeDialog.value.logo = base64
qrCodeDialog.value.visible = true
}
reader.onerror = (err) => console.log("err :>> ", err)
})
.catch((err) => {
console.log("QRcode error :>> ", err)
})
break
default:
console.log("command :>> ", command)
break
}
}
const qrCodeDialog = ref({
visible: false,
title: "",
tenantId: "",
link: "",
logo: ""
})
// 生成租户二级域名跳转链接
const genTenantLink = (tenantId: string) => {
return `http://${tenantId}.${import.meta.env.VITE_TENANT_LINK}`
}
// 删除
const handleDelete = (data: ISysTenant) => {
ElMessageBox.prompt(`请在下方的输入框中填写租户ID`, `确要删除 ${data.name} 吗?`, {

View File

@@ -2,6 +2,7 @@
declare interface ImportMetaEnv {
readonly VITE_APP_TITLE: string
readonly VITE_BASE_API: string
readonly VITE_TENANT_LINK: string
readonly VITE_ROUTER_HISTORY: "hash" | "html5"
readonly VITE_PUBLIC_PATH: string
}

View File

@@ -26,7 +26,7 @@ public class JwtConst {
String.format("%s/**", STOMP_ENDPOINT),
"/actuator/**",
// 判断租户是否存在
"/sys/tenant/simple/*",
"/sys/tenant/simple",
"/v2/api-docs",
"/v3/api-docs/**",
"/doc.html",

View File

@@ -39,8 +39,8 @@ public class SysTenantController {
@Operation(summary = "简单Id查询", description = "根据Id查询 最基础的字段")
@GetMapping("/simple/{id}")
public JsonResult<SysTenant> findSimpleById(@PathVariable("id") String id) {
@GetMapping("/simple")
public JsonResult<SysTenant> findSimpleById(@RequestParam("tenantId") String id) {
return JsonResult.success(baseService.getSimpleById(id));
}

View File

@@ -44,7 +44,7 @@
<hutool.version>5.8.22</hutool.version>
<knife4j.version>4.3.0</knife4j.version>
<mybatis-plus.version>3.5.4</mybatis-plus.version>
<mybatis-plus.version>3.5.4.1</mybatis-plus.version>
<spring-boot.version>3.1.5</spring-boot.version>
</properties>

View File

@@ -19,7 +19,8 @@ export const existTenantApi = (id: string) => {
/** 根据Id简单查询 */
export const getSimpleTenantApi = (id: string) => {
return httpGet<string, IJsonResult<ISimpleTenant>>(`/sys/tenant/simple/${id}`)
const params = { tenantId: id }
return httpGet<any, IJsonResult<ISimpleTenant>>("/sys/tenant/simple", params)
}
/** 分页查询所有 */