-
-
+
+
+
+
-
-
-
-
-
-
-
-
-
+
+ {{ data.name }}
+
+
+
+
+
+ 添加分类
+ 编辑分类
+ 删除分类
+
+
+
-
-
-
-
+
+
+
+
+
+
+ 确认所选
+
+
+
+ 查询
-
- {{ item.name }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.name }}
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 取消
+ 确定
+
+
+
diff --git a/web/src/components/upload/common.vue b/web/src/components/upload/common.vue
index 1505024b9b..1f4b78418a 100644
--- a/web/src/components/upload/common.vue
+++ b/web/src/components/upload/common.vue
@@ -6,10 +6,11 @@
:on-error="uploadError"
:on-success="uploadSuccess"
:show-file-list="false"
+ :data="{'classId': props.classId}"
multiple
class="upload-btn"
>
-
普通上传
+
普通上传
@@ -19,11 +20,19 @@
import { ElMessage } from 'element-plus'
import { isVideoMime, isImageMime } from '@/utils/image'
import { getBaseUrl } from '@/utils/format'
+ import {Upload} from "@element-plus/icons-vue";
defineOptions({
name: 'UploadCommon'
})
+ const props = defineProps({
+ classId: {
+ type: Number,
+ default: 0
+ }
+ })
+
const emit = defineEmits(['on-success'])
const fullscreenLoading = ref(false)
diff --git a/web/src/components/upload/image.vue b/web/src/components/upload/image.vue
index 7423cc7def..60d226f492 100644
--- a/web/src/components/upload/image.vue
+++ b/web/src/components/upload/image.vue
@@ -6,8 +6,9 @@
:on-success="handleImageSuccess"
:before-upload="beforeImageUpload"
:multiple="false"
+ :data="{'classId': props.classId}"
>
- 压缩上传
+ 压缩上传
@@ -16,6 +17,7 @@
import ImageCompress from '@/utils/image'
import { ElMessage } from 'element-plus'
import { getBaseUrl } from '@/utils/format'
+ import {Upload} from "@element-plus/icons-vue";
defineOptions({
name: 'UploadImage'
@@ -34,6 +36,10 @@
maxWH: {
type: Number,
default: 1920 // 图片长宽上限
+ },
+ classId: {
+ type: Number,
+ default: 0
}
})
diff --git a/web/src/pathInfo.json b/web/src/pathInfo.json
index e08798c046..bf0f039458 100644
--- a/web/src/pathInfo.json
+++ b/web/src/pathInfo.json
@@ -27,6 +27,7 @@
"/src/view/layout/aside/normalMode.vue": "GvaAside",
"/src/view/layout/header/index.vue": "Index",
"/src/view/layout/header/tools.vue": "Tools",
+ "/src/view/layout/iframe.vue": "GvaLayoutIframe",
"/src/view/layout/index.vue": "GvaLayout",
"/src/view/layout/screenfull/index.vue": "Screenfull",
"/src/view/layout/search/search.vue": "BtnBox",
diff --git a/web/src/permission.js b/web/src/permission.js
index 70c21bee89..78d6ae858c 100644
--- a/web/src/permission.js
+++ b/web/src/permission.js
@@ -4,134 +4,143 @@ import getPageTitle from '@/utils/page'
import router from '@/router'
import Nprogress from 'nprogress'
import 'nprogress/nprogress.css'
-Nprogress.configure({ showSpinner: false, ease: 'ease', speed: 500 })
-const whiteList = ['Login', 'Init']
+// 配置 NProgress
+Nprogress.configure({
+ showSpinner: false,
+ ease: 'ease',
+ speed: 500
+})
-const getRouter = async (userStore) => {
- const routerStore = useRouterStore()
- await routerStore.SetAsyncRouter()
- await userStore.GetUserInfo()
- const asyncRouters = routerStore.asyncRouters
- asyncRouters.forEach((asyncRouter) => {
- router.addRoute(asyncRouter)
- })
+// 白名单路由
+const WHITE_LIST = ['Login', 'Init']
+
+// 处理路由加载
+const setupRouter = async (userStore) => {
+ try {
+ const routerStore = useRouterStore()
+ await Promise.all([routerStore.SetAsyncRouter(), userStore.GetUserInfo()])
+
+ routerStore.asyncRouters.forEach((route) => router.addRoute(route))
+ return true
+ } catch (error) {
+ console.error('Setup router failed:', error)
+ return false
+ }
}
+// 移除加载动画
const removeLoading = () => {
const element = document.getElementById('gva-loading-box')
- if (element) {
- element.remove()
- }
+ element?.remove()
}
-async function handleKeepAlive(to) {
- if (to.matched.some((item) => item.meta.keepAlive)) {
- if (to.matched && to.matched.length > 2) {
- for (let i = 1; i < to.matched.length; i++) {
- const element = to.matched[i - 1]
- if (element.name === 'layout') {
- to.matched.splice(i, 1)
- await handleKeepAlive(to)
- }
- // 如果没有按需加载完成则等待加载
- if (typeof element.components.default === 'function') {
- await element.components.default()
- await handleKeepAlive(to)
- }
+// 处理组件缓存
+const handleKeepAlive = async (to) => {
+ if (!to.matched.some((item) => item.meta.keepAlive)) return
+
+ if (to.matched?.length > 2) {
+ for (let i = 1; i < to.matched.length; i++) {
+ const element = to.matched[i - 1]
+
+ if (element.name === 'layout') {
+ to.matched.splice(i, 1)
+ await handleKeepAlive(to)
+ continue
+ }
+
+ if (typeof element.components.default === 'function') {
+ await element.components.default()
+ await handleKeepAlive(to)
}
}
}
}
+// 处理路由重定向
+const handleRedirect = (to, userStore) => {
+ if (router.hasRoute(userStore.userInfo.authority.defaultRouter)) {
+ return { ...to, replace: true }
+ }
+ return { path: '/layout/404' }
+}
+
+// 路由守卫
router.beforeEach(async (to, from) => {
+ const userStore = useUserStore()
const routerStore = useRouterStore()
+ const token = userStore.token
+
Nprogress.start()
- const userStore = useUserStore()
+
+ // 处理元数据和缓存
to.meta.matched = [...to.matched]
- handleKeepAlive(to)
- const token = userStore.token
- // 在白名单中的判断情况
+ await handleKeepAlive(to)
+
+ // 设置页面标题
document.title = getPageTitle(to.meta.title, to)
+
if (to.meta.client) {
return true
}
- if (whiteList.indexOf(to.name) > -1) {
- if (token) {
- if (!routerStore.asyncRouterFlag && whiteList.indexOf(from.name) < 0) {
- await getRouter(userStore)
- }
- // token 可以解析但是却是不存在的用户 id 或角色 id 会导致无限调用
- if (userStore.userInfo?.authority?.defaultRouter != null) {
- if (router.hasRoute(userStore.userInfo.authority.defaultRouter)) {
- return { name: userStore.userInfo.authority.defaultRouter }
- } else {
- return { path: '/layout/404' }
- }
- } else {
- // 强制退出账号
- userStore.ClearStorage()
- return {
- name: 'Login',
- query: {
- redirect: document.location.hash
- }
- }
- }
- } else {
- return true
+
+ // 白名单路由处理
+ if (WHITE_LIST.includes(to.name)) {
+ if (
+ token &&
+ !routerStore.asyncRouterFlag &&
+ !WHITE_LIST.includes(from.name)
+ ) {
+ await setupRouter(userStore)
}
- } else {
- // 不在白名单中并且已经登录的时候
- if (token) {
- if (sessionStorage.getItem('needToHome') === 'true') {
- sessionStorage.removeItem('needToHome')
- return { path: '/' }
- }
- // 添加flag防止多次获取动态路由和栈溢出
- if (!routerStore.asyncRouterFlag && whiteList.indexOf(from.name) < 0) {
- await getRouter(userStore)
- if (userStore.token) {
- if (router.hasRoute(userStore.userInfo.authority.defaultRouter)) {
- return { ...to, replace: true }
- } else {
- return { path: '/layout/404' }
- }
- } else {
- return {
- name: 'Login',
- query: { redirect: to.href }
- }
- }
- } else {
- if (to.matched.length) {
- return true
- } else {
- return { path: '/layout/404' }
- }
- }
+ return true
+ }
+
+ // 需要登录的路由处理
+ if (token) {
+ // 处理需要跳转到首页的情况
+ if (sessionStorage.getItem('needToHome') === 'true') {
+ sessionStorage.removeItem('needToHome')
+ return { path: '/' }
}
- // 不在白名单中并且未登录的时候
- if (!token) {
+
+ // 处理异步路由
+ if (!routerStore.asyncRouterFlag && !WHITE_LIST.includes(from.name)) {
+ const setupSuccess = await setupRouter(userStore)
+
+ if (setupSuccess && userStore.token) {
+ return handleRedirect(to, userStore)
+ }
+
return {
name: 'Login',
- query: {
- redirect: document.location.hash
- }
+ query: { redirect: to.href }
}
}
+
+ return to.matched.length ? true : { path: '/layout/404' }
+ }
+
+ // 未登录跳转登录页
+ return {
+ name: 'Login',
+ query: {
+ redirect: document.location.hash
+ }
}
})
+// 路由加载完成
router.afterEach(() => {
- // 路由加载完成后关闭进度条
- document.getElementsByClassName('main-cont main-right')[0]?.scrollTo(0, 0)
+ document.querySelector('.main-cont.main-right')?.scrollTo(0, 0)
Nprogress.done()
})
-router.onError(() => {
- // 路由发生错误后销毁进度条
+// 路由错误处理
+router.onError((error) => {
+ console.error('Router error:', error)
Nprogress.remove()
})
+// 移除初始加载动画
removeLoading()
diff --git a/web/src/pinia/modules/params.js b/web/src/pinia/modules/params.js
new file mode 100644
index 0000000000..54cdbf97a8
--- /dev/null
+++ b/web/src/pinia/modules/params.js
@@ -0,0 +1,31 @@
+import { getSysParam } from '@/api/sysParams'
+import { defineStore } from 'pinia'
+import { ref } from 'vue'
+
+export const useParamsStore = defineStore('params', () => {
+ const paramsMap = ref({})
+
+ const setParamsMap = (paramsRes) => {
+ paramsMap.value = { ...paramsMap.value, ...paramsRes }
+ }
+
+ const getParams = async(key) => {
+ if (paramsMap.value[key] && paramsMap.value[key].length) {
+ return paramsMap.value[key]
+ } else {
+ const res = await getSysParam({ key })
+ if (res.code === 0) {
+ const paramsRes = {}
+ paramsRes[key] = res.data.value
+ setParamsMap(paramsRes)
+ return paramsMap.value[key]
+ }
+ }
+ }
+
+ return {
+ paramsMap,
+ setParamsMap,
+ getParams
+ }
+})
diff --git a/web/src/router/index.js b/web/src/router/index.js
index d5203b2f87..96ffbe5027 100644
--- a/web/src/router/index.js
+++ b/web/src/router/index.js
@@ -21,7 +21,7 @@ const routes = [
closeTab: true
},
component: () => import('@/view/error/index.vue')
- }
+ },
]
const router = createRouter({
diff --git a/web/src/utils/dictionary.js b/web/src/utils/dictionary.js
index 679a6b9886..89ec656e43 100644
--- a/web/src/utils/dictionary.js
+++ b/web/src/utils/dictionary.js
@@ -1,5 +1,4 @@
import { useDictionaryStore } from '@/pinia/modules/dictionary'
-import { getSysParam } from '@/api/sysParams'
// 获取字典方法 使用示例 getDict('sex').then(res) 或者 async函数下 const res = await getDict('sex')
export const getDict = async (type) => {
const dictionaryStore = useDictionaryStore()
@@ -25,10 +24,3 @@ export const showDictLabel = (
})
return Reflect.has(dictMap, code) ? dictMap[code] : ''
}
-
-export const getParams = async (key) => {
- const res = await getSysParam({ key })
- if (res.code === 0) {
- return res.data.value
- }
-}
diff --git a/web/src/utils/params.js b/web/src/utils/params.js
new file mode 100644
index 0000000000..b03d539a39
--- /dev/null
+++ b/web/src/utils/params.js
@@ -0,0 +1,14 @@
+import { useParamsStore } from '@/pinia/modules/params'
+/*
+ * 获取参数方法 使用示例 getParams('key').then(res) 或者 async函数下 const res = await getParams('key')
+ * const res = ref('')
+ * const fun = async () => {
+ * res.value = await getParams('test')
+ * }
+ * fun()
+ */
+export const getParams = async(key) => {
+ const paramsStore = useParamsStore()
+ await paramsStore.getParams(key)
+ return paramsStore.paramsMap[key]
+}
diff --git a/web/src/view/about/index.vue b/web/src/view/about/index.vue
index 1ac60e1a6b..bf02becbe5 100644
--- a/web/src/view/about/index.vue
+++ b/web/src/view/about/index.vue
@@ -1,34 +1,29 @@
-
-
-
+
+
+
gin-vue-admin
-
+
flipped-aurora团队
-
-
+
-
-
-
+
@@ -155,10 +144,6 @@
+}
+.list-enter-active,
+.list-leave-active {
+ transition: all 1s;
+}
+.list-enter, .list-leave-to
+ /* .list-leave-active for below version 2.1.8 */ {
+ opacity: 0;
+ transform: translateY(-30px);
+}
+
\ No newline at end of file
diff --git a/web/src/view/example/upload/upload.vue b/web/src/view/example/upload/upload.vue
index 42a4c9161d..1e34948007 100644
--- a/web/src/view/example/upload/upload.vue
+++ b/web/src/view/example/upload/upload.vue
@@ -1,161 +1,233 @@
-
-
-
-
-
-
- 导入URL
-
-
-
查询
+
+
+
+
+
+ {{ data.name }}
+
+
+
+
+
+
+ 添加分类
+ 编辑分类
+ 删除分类
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
- {{ formatDate(scope.row.UpdatedAt) }}
-
-
-
-
-
- {{ scope.row.name }}
-
-
-
-
-
-
- {{ scope.row.tag }}
-
-
-
-
-
- 下载
+
+
+
+
+
+
+ 导入URL
+
+
+
查询
+
-
删除
+
+
+
+
+
+
+
+
+
+ {{ formatDate(scope.row.UpdatedAt) }}
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+ 取消
+ 确定
+
+
+
+ if (res.code === 0) {
+ categories.value = res.data || []
+ categories.value.unshift(data)
+ }
+}
+
+const handleNodeClick = (node) => {
+ search.value.keyword = null
+ search.value.classId = node.ID
+ page.value = 1
+ getTableData()
+}
+
+const categoryDialogVisible = ref(false)
+const categoryFormData = ref({
+ ID: 0,
+ pid: 0,
+ name: ''
+})
+
+const categoryForm = ref(null)
+const rules = ref({
+ name: [
+ {required: true, message: '请输入分类名称', trigger: 'blur'},
+ {max: 20, message: '最多20位字符', trigger: 'blur'}
+ ]
+})
-
+}
+
+fetchCategories()
+
diff --git a/web/src/view/layout/aside/combinationMode.vue b/web/src/view/layout/aside/combinationMode.vue
index e9f82ecc74..3e588ce60f 100644
--- a/web/src/view/layout/aside/combinationMode.vue
+++ b/web/src/view/layout/aside/combinationMode.vue
@@ -95,7 +95,6 @@
return config.value.layout_side_collapsed_width
}
})
-
watchEffect(() => {
active.value = route.meta.activeName || route.name
})
@@ -123,8 +122,10 @@
})
if (index === route.name) return
if (index.indexOf('http://') > -1 || index.indexOf('https://') > -1) {
- window.open(index)
- } else {
+ window.open(index, '_blank')
+ return
+ }
+
if (!top) {
router.push({ name: index, query, params })
return
@@ -136,7 +137,7 @@
}
const firstMenu = leftMenu.find((item) => !item.hidden && item.path.indexOf("http://") === -1 && item.path.indexOf("https://") === -1)
router.push({ name: firstMenu.name, query, params })
- }
+
}
const toggleCollapse = () => {
diff --git a/web/src/view/layout/aside/headMode.vue b/web/src/view/layout/aside/headMode.vue
index 9157c59261..dd8295f754 100644
--- a/web/src/view/layout/aside/headMode.vue
+++ b/web/src/view/layout/aside/headMode.vue
@@ -40,6 +40,10 @@
const isCollapse = ref(false)
const active = ref('')
watchEffect(() => {
+ if (route.name === 'Iframe') {
+ active.value = decodeURIComponent(route.query.url)
+ return
+ }
active.value = route.meta.activeName || route.name
})
@@ -66,7 +70,18 @@
})
if (index === route.name) return
if (index.indexOf('http://') > -1 || index.indexOf('https://') > -1) {
- window.open(index)
+ if (index === 'Iframe') {
+ query.url = decodeURIComponent(index)
+ router.push({
+ name: 'Iframe',
+ query,
+ params
+ })
+ return
+ } else {
+ window.open(index, '_blank')
+ return
+ }
} else {
router.push({ name: index, query, params })
}
diff --git a/web/src/view/layout/aside/normalMode.vue b/web/src/view/layout/aside/normalMode.vue
index a3bc7f4241..8c0fae1081 100644
--- a/web/src/view/layout/aside/normalMode.vue
+++ b/web/src/view/layout/aside/normalMode.vue
@@ -15,7 +15,7 @@
unique-opened
@select="selectMenuItem"
>
-
+
{
+ if (route.name === 'Iframe') {
+ active.value = decodeURIComponent(route.query.url)
+ return
+ }
active.value = route.meta.activeName || route.name
})
@@ -91,7 +95,18 @@
})
if (index === route.name) return
if (index.indexOf('http://') > -1 || index.indexOf('https://') > -1) {
- window.open(index)
+ if (index === 'Iframe') {
+ query.url = decodeURIComponent(index)
+ router.push({
+ name: 'Iframe',
+ query,
+ params
+ })
+ return
+ } else {
+ window.open(index, '_blank')
+ return
+ }
} else {
router.push({ name: index, query, params })
}
diff --git a/web/src/view/layout/iframe.vue b/web/src/view/layout/iframe.vue
new file mode 100644
index 0000000000..57fc5d25db
--- /dev/null
+++ b/web/src/view/layout/iframe.vue
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+
+
diff --git a/web/src/view/layout/index.vue b/web/src/view/layout/index.vue
index c5d26fc8f2..da2009a1a6 100644
--- a/web/src/view/layout/index.vue
+++ b/web/src/view/layout/index.vue
@@ -74,8 +74,7 @@
})
watchEffect(() => {
- font.color =
- isDark.value ? 'rgba(255,255,255, .15)' : 'rgba(0, 0, 0, .15)'
+ font.color = isDark.value ? 'rgba(255,255,255, .15)' : 'rgba(0, 0, 0, .15)'
})
const router = useRouter()
diff --git a/web/src/view/person/person.vue b/web/src/view/person/person.vue
index f146483905..9920aae8ff 100644
--- a/web/src/view/person/person.vue
+++ b/web/src/view/person/person.vue
@@ -12,18 +12,24 @@
-
-
-
+
+
+
-
+
{{ userStore.userInfo.nickName }}
-
-
-
-
+
+
+
+ 确认
-
-
+
+ 取消
-
+
中国·北京市·朝阳区
- 北京反转极光科技有限公司
+ 北京翻转极光科技有限公司
@@ -63,15 +67,11 @@
-
-
-
+
+
发送消息
-
-
- 分享主页
-
+ 分享主页
@@ -83,13 +83,17 @@
-
+
基本信息
-
+
手机号码:
{{ userStore.userInfo.phone || '未设置' }}
@@ -102,9 +106,11 @@
修改
-
+
- 邮箱地址:
+ 邮箱地址:
{{ userStore.userInfo.email || '未设置' }}
-
+
账号密码:
已设置
@@ -162,19 +170,35 @@
@@ -196,7 +220,9 @@
:hollow="true"
class="pb-6"
>
-
{{ activity.title }}
+
+ {{ activity.title }}
+
{{ activity.content }}
@@ -258,7 +284,7 @@
@@ -302,7 +328,7 @@
@@ -332,11 +358,10 @@
diff --git a/web/src/view/systemTools/autoCode/index.vue b/web/src/view/systemTools/autoCode/index.vue
index 83b3d68f74..b63b327963 100644
--- a/web/src/view/systemTools/autoCode/index.vue
+++ b/web/src/view/systemTools/autoCode/index.vue
@@ -329,142 +329,159 @@
-
-
-
-
-
-
- 使用GVA结构
-
-
-
-
-
-
-
-
-
-
-
- 自动创建API
-
-
-
-
-
-
-
-
-
-
-
- 自动创建菜单
-
-
-
-
-
-
-
-
-
-
-
- 同步表结构
-
-
-
-
-
-
-
-
-
-
-
- 创建按钮权限
-
-
-
-
-
-
-
-
-
-
-
- 创建资源标识
-
-
-
-
-
-
-
-
-
-
-
- 基础模板
-
-
-
-
-
-
-
-
-
-
-
- 树型结构
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+ 专家模式
+
+
+
+
+ {{ isActive ? '收起' : '展开' }}
+
+
+
+
+
+
基础设置
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
自动化设置
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
高级设置
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
树形结构设置
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -929,6 +946,10 @@
for (let key in json) {
form.value[key] = json[key]
}
+
+ form.value.generateServer = true
+ form.value.generateWeb = true
+
}
}
@@ -1118,6 +1139,8 @@
autoCreateResource: false,
onlyTemplate: false,
isTree: false,
+ generateWeb:true,
+ generateServer:true,
treeJson: "",
fields: []
})
@@ -1239,6 +1262,13 @@
})
return false
}
+ if(!form.value.generateWeb && !form.value.generateServer){
+ ElMessage({
+ type: 'error',
+ message: '请至少选择一个生成项'
+ })
+ return false
+ }
if (!form.value.onlyTemplate) {
if (form.value.fields.length <= 0) {
ElMessage({
@@ -1403,6 +1433,8 @@
form.value.abbreviation = toLowerCase(tbHump)
form.value.description = tbHump + '表'
form.value.autoCreateApiToSql = true
+ form.value.generateServer = true
+ form.value.generateWeb = true
form.value.fields = []
res.data.columns &&
res.data.columns.forEach((item) => {
@@ -1520,6 +1552,20 @@
}
)
+ watch(()=>form.value.generateServer,()=>{
+ if(!form.value.generateServer){
+ form.value.autoCreateApiToSql = false
+ form.value.autoMigrate = false
+ }
+ })
+
+ watch(()=>form.value.generateWeb,()=>{
+ if(!form.value.generateWeb){
+ form.value.autoCreateMenuToSql = false
+ form.value.autoCreateBtnAuth = false
+ }
+ })
+
const catchData = () => {
window.sessionStorage.setItem('autoCode', JSON.stringify(form.value))
}
@@ -1607,3 +1653,18 @@
}
)
+
+