!317 优化 vue3 配置,修复部分 bug

Merge pull request !317 from xingyu/dev
This commit is contained in:
芋道源码 2022-12-07 13:40:42 +00:00 committed by Gitee
commit 7f0c011123
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
132 changed files with 2106 additions and 2810 deletions

View File

@ -28,15 +28,15 @@
<dynamic-datasource.version>3.5.2</dynamic-datasource.version> <dynamic-datasource.version>3.5.2</dynamic-datasource.version>
<redisson.version>3.18.0</redisson.version> <redisson.version>3.18.0</redisson.version>
<!-- 服务保障相关 --> <!-- 服务保障相关 -->
<lock4j.version>2.2.2</lock4j.version> <lock4j.version>2.2.3</lock4j.version>
<resilience4j.version>1.7.1</resilience4j.version> <resilience4j.version>1.7.1</resilience4j.version>
<!-- 监控相关 --> <!-- 监控相关 -->
<skywalking.version>8.12.0</skywalking.version> <skywalking.version>8.12.0</skywalking.version>
<spring-boot-admin.version>2.7.7</spring-boot-admin.version> <spring-boot-admin.version>2.7.7</spring-boot-admin.version>
<opentracing.version>0.33.0</opentracing.version> <opentracing.version>0.33.0</opentracing.version>
<!-- Test 测试相关 --> <!-- Test 测试相关 -->
<podam.version>7.2.9.RELEASE</podam.version> <podam.version>7.2.11.RELEASE</podam.version>
<jedis-mock.version>1.0.4</jedis-mock.version> <jedis-mock.version>1.0.5</jedis-mock.version>
<mockito-inline.version>4.8.0</mockito-inline.version> <mockito-inline.version>4.8.0</mockito-inline.version>
<!-- Bpm 工作流相关 --> <!-- Bpm 工作流相关 -->
<flowable.version>6.7.2</flowable.version> <flowable.version>6.7.2</flowable.version>
@ -50,19 +50,19 @@
<fastjson.version>1.2.83</fastjson.version> <fastjson.version>1.2.83</fastjson.version>
<guava.version>31.1-jre</guava.version> <guava.version>31.1-jre</guava.version>
<guice.version>5.1.0</guice.version> <guice.version>5.1.0</guice.version>
<transmittable-thread-local.version>2.14.0</transmittable-thread-local.version> <transmittable-thread-local.version>2.14.2</transmittable-thread-local.version>
<commons-net.version>3.8.0</commons-net.version> <commons-net.version>3.8.0</commons-net.version>
<jsch.version>0.1.55</jsch.version> <jsch.version>0.1.55</jsch.version>
<tika-core.version>2.5.0</tika-core.version> <tika-core.version>2.5.0</tika-core.version>
<aj-captcha.version>1.3.0</aj-captcha.version> <aj-captcha.version>1.3.0</aj-captcha.version>
<netty-all.version>4.1.82.Final</netty-all.version> <netty-all.version>4.1.85.Final</netty-all.version>
<!-- 三方云服务相关 --> <!-- 三方云服务相关 -->
<okio.version>3.0.0</okio.version> <okio.version>3.0.0</okio.version>
<okhttp3.version>4.10.0</okhttp3.version> <okhttp3.version>4.10.0</okhttp3.version>
<minio.version>8.4.6</minio.version> <minio.version>8.4.6</minio.version>
<aliyun-java-sdk-core.version>4.6.2</aliyun-java-sdk-core.version> <aliyun-java-sdk-core.version>4.6.3</aliyun-java-sdk-core.version>
<aliyun-java-sdk-dysmsapi.version>2.2.1</aliyun-java-sdk-dysmsapi.version> <aliyun-java-sdk-dysmsapi.version>2.2.1</aliyun-java-sdk-dysmsapi.version>
<tencentcloud-sdk-java.version>3.1.635</tencentcloud-sdk-java.version> <tencentcloud-sdk-java.version>3.1.637</tencentcloud-sdk-java.version>
<justauth.version>1.4.0</justauth.version> <justauth.version>1.4.0</justauth.version>
<jimureport.version>1.5.4</jimureport.version> <jimureport.version>1.5.4</jimureport.version>
<xercesImpl.version>2.12.2</xercesImpl.version> <xercesImpl.version>2.12.2</xercesImpl.version>

View File

@ -1,12 +1,10 @@
package cn.iocoder.yudao.framework.common.util.object; package cn.iocoder.yudao.framework.common.util.object;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil; import cn.hutool.core.util.ReflectUtil;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.Arrays; import java.util.Arrays;
import java.util.Objects;
import java.util.function.Consumer; import java.util.function.Consumer;
/** /**
@ -47,6 +45,7 @@ public class ObjectUtils {
return obj1.compareTo(obj2) > 0 ? obj1 : obj2; return obj1.compareTo(obj2) > 0 ? obj1 : obj2;
} }
@SafeVarargs
public static <T> T defaultIfNull(T... array) { public static <T> T defaultIfNull(T... array) {
for (T item : array) { for (T item : array) {
if (item != null) { if (item != null) {
@ -56,6 +55,7 @@ public class ObjectUtils {
return null; return null;
} }
@SafeVarargs
public static <T> boolean equalsAny(T obj, T... array) { public static <T> boolean equalsAny(T obj, T... array) {
return Arrays.asList(array).contains(obj); return Arrays.asList(array).contains(obj);
} }

View File

@ -52,7 +52,7 @@
<dependency> <dependency>
<groupId>com.alipay.sdk</groupId> <groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId> <artifactId>alipay-sdk-java</artifactId>
<version>4.33.44.ALL</version> <version>4.34.71.ALL</version>
<exclusions> <exclusions>
<exclusion> <exclusion>
<groupId>org.bouncycastle</groupId> <groupId>org.bouncycastle</groupId>

View File

@ -46,7 +46,7 @@
<dependency> <dependency>
<groupId>com.fasterxml.jackson.core</groupId> <groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId> <artifactId>jackson-databind</artifactId>
<version>2.13.2.2</version> <version>2.13.4</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.fasterxml.jackson.core</groupId> <groupId>com.fasterxml.jackson.core</groupId>

View File

@ -48,7 +48,7 @@ public interface BpmTaskConvert {
return null; return null;
} }
try { try {
T newInstance = target.newInstance(); T newInstance = target.getDeclaredConstructor().newInstance();
BeanUtils.copyProperties(source, newInstance); BeanUtils.copyProperties(source, newInstance);
return newInstance; return newInstance;
} catch (Exception e) { } catch (Exception e) {

View File

@ -5,7 +5,7 @@ export interface ${simpleClassName}VO {
#if ($column.createOperation || $column.updateOperation) #if ($column.createOperation || $column.updateOperation)
#if(${column.javaType.toLowerCase()} == "long" || ${column.javaType.toLowerCase()} == "integer" || ${column.javaType.toLowerCase()} == "double") #if(${column.javaType.toLowerCase()} == "long" || ${column.javaType.toLowerCase()} == "integer" || ${column.javaType.toLowerCase()} == "double")
${column.javaField}: number ${column.javaField}: number
#elseif(${column.javaType.toLowerCase()} == "date") #elseif(${column.javaType.toLowerCase()} == "date" || ${column.javaType.toLowerCase()} == "localdatetime")
${column.javaField}: Date ${column.javaField}: Date
#else #else
${column.javaField}: ${column.javaType.toLowerCase()} ${column.javaField}: ${column.javaType.toLowerCase()}
@ -19,7 +19,7 @@ export interface ${simpleClassName}PageReqVO extends PageParam {
#if (${column.listOperation})##查询操作 #if (${column.listOperation})##查询操作
#if(${column.javaType.toLowerCase()} == "long" || ${column.javaType.toLowerCase()} == "integer" || ${column.javaType.toLowerCase()} == "double") #if(${column.javaType.toLowerCase()} == "long" || ${column.javaType.toLowerCase()} == "integer" || ${column.javaType.toLowerCase()} == "double")
${column.javaField}?: number ${column.javaField}?: number
#elseif(${column.javaType.toLowerCase()} == "date") #elseif(${column.javaType.toLowerCase()} == "date" || ${column.javaType.toLowerCase()} == "localdatetime")
${column.javaField}?: Date[] ${column.javaField}?: Date[]
#else #else
${column.javaField}?: ${column.javaType.toLowerCase()} ${column.javaField}?: ${column.javaType.toLowerCase()}
@ -33,8 +33,8 @@ export interface ${simpleClassName}ExcelReqVO {
#if (${column.listOperation})##查询操作 #if (${column.listOperation})##查询操作
#if(${column.javaType.toLowerCase()} == "long" || ${column.javaType.toLowerCase()} == "integer" || ${column.javaType.toLowerCase()} == "double") #if(${column.javaType.toLowerCase()} == "long" || ${column.javaType.toLowerCase()} == "integer" || ${column.javaType.toLowerCase()} == "double")
${column.javaField}?: number ${column.javaField}?: number
#elseif(${column.javaType.toLowerCase()} == "date") #elseif(${column.javaType.toLowerCase()} == "date" || ${column.javaType.toLowerCase()} == "localdatetime")
${column.javaField}?: string[] ${column.javaField}?: Date[]
#else #else
${column.javaField}?: ${column.javaType.toLowerCase()} ${column.javaField}?: ${column.javaType.toLowerCase()}
#end #end

View File

@ -28,6 +28,9 @@ const crudSchemas = reactive<VxeCrudSchema>({
{ {
title: '${column.columnComment}', title: '${column.columnComment}',
field: '${column.javaField}', field: '${column.javaField}',
#if (!$column.listOperationResult)
isTable: false,
#end
#if ("" != $dictType)## 有数据字典 #if ("" != $dictType)## 有数据字典
dictType: DICT_TYPE.$dictType.toUpperCase(), dictType: DICT_TYPE.$dictType.toUpperCase(),
#if (${column.javaType.toLowerCase()} == "long" || ${column.javaType.toLowerCase()} == "integer") #if (${column.javaType.toLowerCase()} == "long" || ${column.javaType.toLowerCase()} == "integer")
@ -39,7 +42,8 @@ const crudSchemas = reactive<VxeCrudSchema>({
#if (!$column.createOperation && !$column.updateOperation) #if (!$column.createOperation && !$column.updateOperation)
isForm: false, isForm: false,
#elseif(!("" != $column.dictType)) #elseif(!("" != $column.dictType))
#if ($column.htmlType == "datetime")## 时间框 #if (${column.javaType.toLowerCase()} == "date" || ${column.javaType.toLowerCase()} == "localdatetime")
formatter: 'formatDate',
form: { form: {
component: 'DatePicker', component: 'DatePicker',
componentProps: { componentProps: {
@ -73,9 +77,20 @@ const crudSchemas = reactive<VxeCrudSchema>({
component: 'InputNumber', component: 'InputNumber',
value: 0 value: 0
}, },
#elseif($column.htmlType == "imageUpload")## 图片上传
form: {
component: 'UploadImg',
componentProps: {
limit: 1
}
},
#elseif($column.htmlType == "fileUpload")## 图片上传
form: {
component: 'UploadFile'
},
#end #end
#end #end
#if ($column.listOperationResult) #if ($column.listOperation)
#if($column.htmlType == "input") #if($column.htmlType == "input")
isSearch: true, isSearch: true,
#elseif("" != $dictType) #elseif("" != $dictType)

View File

@ -74,7 +74,7 @@
</template> </template>
</XModal> </XModal>
</template> </template>
<script setup lang="ts" name="${table.moduleName}"> <script setup lang="ts" name="${simpleClassName}">
// 全局相关的 import // 全局相关的 import
import { ref, unref } from 'vue' import { ref, unref } from 'vue'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
@ -91,7 +91,7 @@ const message = useMessage() // 消息弹窗
// 列表相关的变量 // 列表相关的变量
const xGrid = ref<VxeGridInstance>() // 列表 Grid Ref const xGrid = ref<VxeGridInstance>() // 列表 Grid Ref
const { gridOptions, reloadList, deleteData, exportList } = useVxeGrid<${simpleClassName}Api.${simpleClassName}VO>({ const { gridOptions, getList, deleteData, exportList } = useVxeGrid<${simpleClassName}Api.${simpleClassName}VO>({
allSchemas: allSchemas, allSchemas: allSchemas,
getListApi: ${simpleClassName}Api.get${simpleClassName}PageApi, getListApi: ${simpleClassName}Api.get${simpleClassName}PageApi,
deleteApi: ${simpleClassName}Api.delete${simpleClassName}Api, deleteApi: ${simpleClassName}Api.delete${simpleClassName}Api,
@ -169,7 +169,7 @@ const submitForm = async () => {
} finally { } finally {
actionLoading.value = false actionLoading.value = false
// 刷新列表 // 刷新列表
await reloadList(xGrid) await getList(xGrid)
} }
} }
}) })

View File

@ -6,5 +6,4 @@
/vite.config.ts /vite.config.ts
/src/types/env.d.ts /src/types/env.d.ts
/docs/**/* /docs/**/*
/plop/**/*
CHANGELOG CHANGELOG

View File

@ -0,0 +1,76 @@
import { resolve } from 'path'
import Vue from '@vitejs/plugin-vue'
import VueJsx from '@vitejs/plugin-vue-jsx'
import VueI18n from '@intlify/vite-plugin-vue-i18n'
import WindiCSS from 'vite-plugin-windicss'
import progress from 'vite-plugin-progress'
import EslintPlugin from 'vite-plugin-eslint'
import PurgeIcons from 'vite-plugin-purge-icons'
import { ViteEjsPlugin } from 'vite-plugin-ejs'
import viteCompression from 'vite-plugin-compression'
import vueSetupExtend from 'vite-plugin-vue-setup-extend'
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
import {
createStyleImportPlugin,
ElementPlusResolve,
VxeTableResolve
} from 'vite-plugin-style-import'
export function createVitePlugins(VITE_APP_TITLE: string) {
const root = process.cwd()
// 路径查找
function pathResolve(dir: string) {
return resolve(root, '.', dir)
}
return [
Vue(),
VueJsx(),
WindiCSS(),
progress(),
PurgeIcons(),
vueSetupExtend(),
createStyleImportPlugin({
resolves: [ElementPlusResolve(), VxeTableResolve()],
libs: [
{
libraryName: 'element-plus',
esModule: true,
resolveStyle: (name) => {
return `element-plus/es/components/${name.substring(3)}/style/css`
}
},
{
libraryName: 'vxe-table',
esModule: true,
resolveStyle: (name) => {
return `vxe-table/es/${name}/style.css`
}
}
]
}),
EslintPlugin({
cache: false,
include: ['src/**/*.vue', 'src/**/*.ts', 'src/**/*.tsx'] // 检查的文件
}),
VueI18n({
runtimeOnly: true,
compositionOnly: true,
include: [resolve(__dirname, 'src/locales/**')]
}),
createSvgIconsPlugin({
iconDirs: [pathResolve('src/assets/svgs')],
symbolId: 'icon-[dir]-[name]',
svgoOptions: true
}),
viteCompression({
verbose: true, // 是否在控制台输出压缩结果
disable: false, // 是否禁用
threshold: 10240, // 体积大于 threshold 才会被压缩,单位 b
algorithm: 'gzip', // 压缩算法,可选 [ 'gzip' , 'brotliCompress' ,'deflate' , 'deflateRaw']
ext: '.gz', // 生成的压缩包后缀
deleteOriginFile: false //压缩后是否删除源文件
}),
ViteEjsPlugin({
title: VITE_APP_TITLE
})
]
}

View File

@ -0,0 +1,40 @@
const include = [
'qs',
'url',
'vue',
'sass',
'mitt',
'axios',
'pinia',
'dayjs',
'qrcode',
'windicss',
'vue-router',
'vue-types',
'vue-i18n',
'xe-utils',
'crypto-js',
'lodash-es',
'vxe-table',
'nprogress',
'animate.css',
'vxe-table/es/style',
'web-storage-cache',
'element-plus',
'element-plus/es/locale/lang/zh-cn',
'element-plus/es/locale/lang/en',
'@iconify/iconify',
'@vueuse/core',
'@zxcvbn-ts/core',
'echarts/core',
'echarts/charts',
'echarts/components',
'echarts/renderers',
'echarts-wordcloud',
'@wangeditor/editor',
'@wangeditor/editor-for-vue'
]
const exclude = ['@iconify/json']
export { include, exclude }

View File

@ -0,0 +1,18 @@
export const styleImportPlugin = {
libs: [
{
libraryName: 'element-plus',
esModule: true,
resolveStyle: (name) => {
return `element-plus/es/components/${name.substring(3)}/style/css`
}
},
{
libraryName: 'vxe-table',
esModule: true,
resolveStyle: (name) => {
return `vxe-table/es/${name}/style.css`
}
}
]
}

View File

@ -1,12 +1,12 @@
{ {
"name": "ruoyi-vue-pro-vue3", "name": "yudao-ui-admin-vue3",
"version": "1.6.4.1863", "version": "1.6.5.1874",
"description": "基于vue3、vite3、element-plus、typesScript", "description": "基于vue3、vite3、element-plus、typesScript",
"author": "xingyu", "author": "xingyu",
"private": false, "private": false,
"scripts": { "scripts": {
"i": "pnpm install", "i": "pnpm install",
"dev": "vite --mode base --open", "dev": "vite --mode base",
"ts:check": "vue-tsc --noEmit", "ts:check": "vue-tsc --noEmit",
"build:pro": "vite build --mode pro", "build:pro": "vite build --mode pro",
"build:dev": "vite build --mode dev", "build:dev": "vite build --mode dev",
@ -21,8 +21,7 @@
"lint:format": "prettier --write --loglevel warn \"src/**/*.{js,ts,json,tsx,css,less,scss,vue,html,md}\"", "lint:format": "prettier --write --loglevel warn \"src/**/*.{js,ts,json,tsx,css,less,scss,vue,html,md}\"",
"lint:style": "stylelint --fix \"**/*.{vue,less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/", "lint:style": "stylelint --fix \"**/*.{vue,less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/",
"lint:lint-staged": "lint-staged -c ", "lint:lint-staged": "lint-staged -c ",
"lint:pretty": "pretty-quick --staged", "lint:pretty": "pretty-quick --staged"
"p": "plop"
}, },
"dependencies": { "dependencies": {
"@iconify/iconify": "^3.0.1", "@iconify/iconify": "^3.0.1",
@ -31,14 +30,13 @@
"@wangeditor/editor-for-vue": "^5.1.10", "@wangeditor/editor-for-vue": "^5.1.10",
"@zxcvbn-ts/core": "^2.1.0", "@zxcvbn-ts/core": "^2.1.0",
"animate.css": "^4.1.1", "animate.css": "^4.1.1",
"axios": "^1.2.0", "axios": "^1.2.1",
"crypto-js": "^4.1.1", "crypto-js": "^4.1.1",
"dayjs": "^1.11.6", "dayjs": "^1.11.7",
"echarts": "^5.4.0", "echarts": "^5.4.0",
"echarts-wordcloud": "^2.1.0", "echarts-wordcloud": "^2.1.0",
"element-plus": "2.2.25", "element-plus": "2.2.26",
"intro.js": "^6.0.0", "intro.js": "^6.0.0",
"js-cookie": "^3.0.1",
"jsencrypt": "^3.3.1", "jsencrypt": "^3.3.1",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"mitt": "^3.0.0", "mitt": "^3.0.0",
@ -59,52 +57,54 @@
"devDependencies": { "devDependencies": {
"@commitlint/cli": "^17.3.0", "@commitlint/cli": "^17.3.0",
"@commitlint/config-conventional": "^17.3.0", "@commitlint/config-conventional": "^17.3.0",
"@iconify/json": "^2.1.145", "@iconify/json": "^2.1.149",
"@intlify/vite-plugin-vue-i18n": "^6.0.3", "@intlify/vite-plugin-vue-i18n": "^6.0.3",
"@purge-icons/generated": "^0.9.0", "@purge-icons/generated": "^0.9.0",
"@types/intro.js": "^5.1.0", "@types/intro.js": "^5.1.0",
"@types/lodash-es": "^4.17.6", "@types/lodash-es": "^4.17.6",
"@types/node": "^18.11.9", "@types/node": "^18.11.11",
"@types/nprogress": "^0.2.0", "@types/nprogress": "^0.2.0",
"@types/qrcode": "^1.5.0", "@types/qrcode": "^1.5.0",
"@types/qs": "^6.9.7", "@types/qs": "^6.9.7",
"@typescript-eslint/eslint-plugin": "^5.45.0", "@typescript-eslint/eslint-plugin": "^5.45.1",
"@typescript-eslint/parser": "^5.45.0", "@typescript-eslint/parser": "^5.45.1",
"@vitejs/plugin-legacy": "^2.3.1",
"@vitejs/plugin-vue": "^3.2.0", "@vitejs/plugin-vue": "^3.2.0",
"@vitejs/plugin-vue-jsx": "^2.1.1", "@vitejs/plugin-vue-jsx": "^2.1.1",
"autoprefixer": "^10.4.13", "autoprefixer": "^10.4.13",
"eslint": "^8.28.0", "consola": "^2.15.3",
"eslint": "^8.29.0",
"eslint-config-prettier": "^8.5.0", "eslint-config-prettier": "^8.5.0",
"eslint-define-config": "^1.12.0", "eslint-define-config": "^1.12.0",
"eslint-plugin-prettier": "^4.2.1", "eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-vue": "^9.8.0", "eslint-plugin-vue": "^9.8.0",
"lint-staged": "^13.0.4", "lint-staged": "^13.1.0",
"plop": "^3.1.1",
"postcss": "^8.4.19", "postcss": "^8.4.19",
"postcss-html": "^1.5.0", "postcss-html": "^1.5.0",
"postcss-scss": "^4.0.6", "postcss-scss": "^4.0.6",
"prettier": "^2.8.0", "prettier": "^2.8.0",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"rollup": "^3.5.0", "rollup": "^3.6.0",
"sass": "^1.56.1", "sass": "^1.56.1",
"stylelint": "^14.15.0", "stylelint": "^14.16.0",
"stylelint-config-html": "^1.1.0", "stylelint-config-html": "^1.1.0",
"stylelint-config-prettier": "^9.0.4", "stylelint-config-prettier": "^9.0.4",
"stylelint-config-recommended": "^9.0.0", "stylelint-config-recommended": "^9.0.0",
"stylelint-config-standard": "^29.0.0", "stylelint-config-standard": "^29.0.0",
"stylelint-order": "^5.0.0", "stylelint-order": "^5.0.0",
"terser": "^5.16.1",
"typescript": "4.9.3", "typescript": "4.9.3",
"vite": "3.2.4", "vite": "3.2.5",
"vite-plugin-compression": "^0.5.1", "vite-plugin-compression": "^0.5.1",
"vite-plugin-ejs": "^1.6.4",
"vite-plugin-eslint": "^1.8.1", "vite-plugin-eslint": "^1.8.1",
"vite-plugin-html": "^3.2.0",
"vite-plugin-progress": "^0.0.6", "vite-plugin-progress": "^0.0.6",
"vite-plugin-purge-icons": "^0.9.1", "vite-plugin-purge-icons": "^0.9.1",
"vite-plugin-style-import": "2.0.0", "vite-plugin-style-import": "2.0.0",
"vite-plugin-svg-icons": "^2.0.1", "vite-plugin-svg-icons": "^2.0.1",
"vite-plugin-vue-setup-extend": "^0.4.0", "vite-plugin-vue-setup-extend": "^0.4.0",
"vite-plugin-windicss": "^1.8.8", "vite-plugin-windicss": "^1.8.8",
"vue-tsc": "^1.0.10", "vue-tsc": "^1.0.11",
"windicss": "^3.5.6" "windicss": "^3.5.6"
}, },
"engines": { "engines": {
@ -116,7 +116,7 @@
"url": "git+https://gitee.com/zhijiantianya/ruoyi-vue-pro" "url": "git+https://gitee.com/zhijiantianya/ruoyi-vue-pro"
}, },
"bugs": { "bugs": {
"url": "https://gitee.com/zhijiantianya/ruoyi-vue-pro/issues/I57XOQ" "url": "https://gitee.com/zhijiantianya/ruoyi-vue-pro/issues"
}, },
"homepage": "https://gitee.com/zhijiantianya/ruoyi-vue-pro" "homepage": "https://gitee.com/zhijiantianya/ruoyi-vue-pro"
} }

View File

@ -1,11 +0,0 @@
<script setup lang="ts">
import { useDesign } from '@/hooks/web/useDesign'
const { getPrefixCls } = useDesign()
const prefixCls = getPrefixCls('{{ name }}')
</script>
<template>
<div :class="`${prefixCls}-{{ name }}`">{{ upperFirstName }}</div>
</template>

View File

@ -1,3 +0,0 @@
import {{ upperFirstName }} from './src/{{ upperFirstName }}.vue'
export { {{ upperFirstName }} }

View File

@ -1,38 +0,0 @@
const toUpperCase = (str) => str.charAt(0).toUpperCase() + str.slice(1)
module.exports = {
description: 'Create vue component',
prompts: [
{
type: 'input',
name: 'name',
message: '请输入组件名称Please enter the component name'
}
],
actions: (data) => {
const { name } = data
const upperFirstName = toUpperCase(name)
const actions = []
if (name) {
actions.push({
type: 'add',
path: `./src/components/${upperFirstName}/src/${upperFirstName}.vue`,
templateFile: './plop/component/component.hbs',
data: {
name,
upperFirstName
}
}, {
type: 'add',
path: `./src/components/${upperFirstName}/index.ts`,
templateFile: './plop/component/index.hbs',
data: {
upperFirstName
}
})
}
return actions
}
}

View File

@ -1,37 +0,0 @@
const toUpperCase = (str) => str.charAt(0).toUpperCase() + str.slice(1)
module.exports = {
description: 'Create vue view',
prompts: [
{
type: 'input',
name: 'path',
message: '请输入路径Please enter a path',
default: 'views'
},
{
type: 'input',
name: 'name',
message: '请输入模块名称Please enter module name'
}
],
actions: (data) => {
const { name, path } = data
const upperFirstName = toUpperCase(name)
const actions = []
if (name) {
actions.push({
type: 'add',
path: `./src/${path}/${upperFirstName}.vue`,
templateFile: './plop/view/view.hbs',
data: {
name,
upperFirstName
}
})
}
return actions
}
}

View File

@ -1,5 +0,0 @@
<template>
<ContentWrap title="{{ upperFirstName }}"> {{ name }} </ContentWrap>
</template>
<script setup lang="ts" name="{{ name }}">
</script>

View File

@ -1,7 +0,0 @@
const viewGenerator = require('./plop/view/prompt.js')
const componentGenerator = require('./plop/component/prompt.js')
module.exports = function (plop) {
plop.setGenerator('view', viewGenerator)
plop.setGenerator('component', componentGenerator)
}

File diff suppressed because it is too large Load Diff

View File

@ -4,7 +4,7 @@ import { isDark } from '@/utils/is'
import { useAppStore } from '@/store/modules/app' import { useAppStore } from '@/store/modules/app'
import { useDesign } from '@/hooks/web/useDesign' import { useDesign } from '@/hooks/web/useDesign'
import { ConfigGlobal } from '@/components/ConfigGlobal' import { ConfigGlobal } from '@/components/ConfigGlobal'
import { useCache } from '@/hooks/web/useCache' import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
const { getPrefixCls } = useDesign() const { getPrefixCls } = useDesign()
const prefixCls = getPrefixCls('app') const prefixCls = getPrefixCls('app')
@ -15,15 +15,10 @@ const { wsCache } = useCache()
// //
const setDefaultTheme = () => { const setDefaultTheme = () => {
if (wsCache.get('isDark')) { let isDarkTheme = wsCache.get(CACHE_KEY.IS_DARK)
if (wsCache.get('isDark') || wsCache.get('isDark') === 'true') { if (isDarkTheme === null) {
appStore.setIsDark(true) isDarkTheme = isDark()
} else {
appStore.setIsDark(false)
} }
return
}
const isDarkTheme = isDark()
appStore.setIsDark(isDarkTheme) appStore.setIsDark(isDarkTheme)
} }
setDefaultTheme() setDefaultTheme()

View File

@ -42,7 +42,7 @@ export const getFileConfigApi = (id: number) => {
// 更新文件配置为主配置 // 更新文件配置为主配置
export const updateFileConfigMasterApi = (id: number) => { export const updateFileConfigMasterApi = (id: number) => {
return request.get({ url: '/infra/file-config/update-master?id=' + id }) return request.put({ url: '/infra/file-config/update-master?id=' + id })
} }
// 新增文件配置 // 新增文件配置

View File

@ -57,7 +57,7 @@ export const smsLoginApi = (data: SmsLoginVO) => {
} }
// 社交授权的跳转 // 社交授权的跳转
export const socialAuthRedirectApi = (type: string, redirectUri: string) => { export const socialAuthRedirectApi = (type: number, redirectUri: string) => {
return request.get({ return request.get({
url: '/system/auth/social-auth-redirect?type=' + type + '&redirectUri=' + redirectUri url: '/system/auth/social-auth-redirect?type=' + type + '&redirectUri=' + redirectUri
}) })

View File

@ -21,6 +21,7 @@ import {
} from 'element-plus' } from 'element-plus'
import { InputPassword } from '@/components/InputPassword' import { InputPassword } from '@/components/InputPassword'
import { Editor } from '@/components/Editor' import { Editor } from '@/components/Editor'
import { UploadImg, UploadFile } from '@/components/UploadFile'
import { ComponentName } from '@/types/components' import { ComponentName } from '@/types/components'
const componentMap: Recordable<Component, ComponentName> = { const componentMap: Recordable<Component, ComponentName> = {
@ -45,7 +46,9 @@ const componentMap: Recordable<Component, ComponentName> = {
TreeSelect: ElTreeSelect, TreeSelect: ElTreeSelect,
RadioButton: ElRadioGroup, RadioButton: ElRadioGroup,
InputPassword: InputPassword, InputPassword: InputPassword,
Editor: Editor Editor: Editor,
UploadImg: UploadImg,
UploadFile: UploadFile
} }
export { componentMap } export { componentMap }

View File

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

View File

@ -0,0 +1,186 @@
<template>
<div class="upload-file">
<el-upload
ref="uploadRef"
:multiple="props.limit > 1"
name="file"
v-model="valueRef"
v-model:file-list="fileList"
:show-file-list="true"
:auto-upload="autoUpload"
:action="updateUrl"
:headers="uploadHeaders"
:limit="props.limit"
:drag="drag"
:before-upload="beforeUpload"
:on-exceed="handleExceed"
:on-success="handleFileSuccess"
:on-error="excelUploadError"
:on-remove="handleRemove"
:on-preview="handlePreview"
class="upload-file-uploader"
>
<el-button type="primary"><Icon icon="ep:upload-filled" />选取文件</el-button>
<template v-if="isShowTip" #tip>
<div style="font-size: 8px">
大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b>
</div>
<div style="font-size: 8px">
格式为 <b style="color: #f56c6c">{{ fileType.join('/') }}</b> 的文件
</div>
</template>
</el-upload>
</div>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue'
import { useMessage } from '@/hooks/web/useMessage'
import { propTypes } from '@/utils/propTypes'
import { getAccessToken, getTenantId } from '@/utils/auth'
import { ElUpload, UploadInstance, UploadProps, UploadRawFile, UploadUserFile } from 'element-plus'
const message = useMessage() //
const emit = defineEmits(['update:modelValue'])
const props = defineProps({
modelValue: propTypes.oneOfType([String, Object, Array]),
title: propTypes.string.def('文件上传'),
updateUrl: propTypes.string.def(import.meta.env.VITE_UPLOAD_URL),
fileType: propTypes.array.def(['doc', 'xls', 'ppt', 'txt', 'pdf']), // , ['png', 'jpg', 'jpeg']
fileSize: propTypes.number.def(5), // (MB)
limit: propTypes.number.def(5), //
autoUpload: propTypes.bool.def(true), //
drag: propTypes.bool.def(false), //
isShowTip: propTypes.bool.def(true) //
})
// ========== ==========
const valueRef = ref(props.modelValue)
const uploadRef = ref<UploadInstance>()
const uploadList = ref<UploadUserFile[]>([])
const fileList = ref<UploadUserFile[]>([])
const uploadNumber = ref<number>(0)
const uploadHeaders = ref({
Authorization: 'Bearer ' + getAccessToken(),
'tenant-id': getTenantId()
})
watch(
() => props.modelValue,
(val) => {
if (val) {
// , 穿map
const list = Array.isArray(props.modelValue)
? props.modelValue
: Array.isArray(props.modelValue?.split(','))
? props.modelValue?.split(',')
: Array.of(props.modelValue)
//
fileList.value = list.map((item) => {
if (typeof item === 'string') {
// edit by
item = { name: item, url: item }
}
return item
})
} else {
fileList.value = []
return []
}
},
{
deep: true,
immediate: true
}
)
//
const beforeUpload: UploadProps['beforeUpload'] = (file: UploadRawFile) => {
if (fileList.value.length >= props.limit) {
message.error(`上传文件数量不能超过${props.limit}个!`)
return false
}
let fileExtension = ''
if (file.name.lastIndexOf('.') > -1) {
fileExtension = file.name.slice(file.name.lastIndexOf('.') + 1)
}
const isImg = props.fileType.some((type: string) => {
if (file.type.indexOf(type) > -1) return true
return !!(fileExtension && fileExtension.indexOf(type) > -1)
})
const isLimit = file.size < props.fileSize * 1024 * 1024
if (!isImg) {
message.error(`文件格式不正确, 请上传${props.fileType.join('/')}格式!`)
return false
}
if (!isLimit) {
message.error(`上传文件大小不能超过${props.fileSize}MB!`)
return false
}
message.success('正在上传文件,请稍候...')
uploadNumber.value++
}
//
// const handleFileChange = (uploadFile: UploadFile): void => {
// uploadRef.value.data.path = uploadFile.name
// }
//
const handleFileSuccess: UploadProps['onSuccess'] = (res: any): void => {
message.success('上传成功')
uploadList.value.push({ name: res.data, url: res.data })
if (uploadList.value.length == uploadNumber.value) {
fileList.value = fileList.value.concat(uploadList.value)
uploadList.value = []
uploadNumber.value = 0
emit('update:modelValue', listToString(fileList.value))
}
}
//
const handleExceed: UploadProps['onExceed'] = (): void => {
message.error(`上传文件数量不能超过${props.limit}个!`)
}
//
const excelUploadError: UploadProps['onError'] = (): void => {
message.error('导入数据失败,请您重新上传!')
}
//
const handleRemove = (file) => {
const findex = fileList.value.map((f) => f.name).indexOf(file.name)
if (findex > -1) {
fileList.value.splice(findex, 1)
emit('update:modelValue', listToString(fileList.value))
}
}
const handlePreview: UploadProps['onPreview'] = (uploadFile) => {
console.log(uploadFile)
}
//
const listToString = (list: UploadUserFile[], separator?: string) => {
let strs = ''
separator = separator || ','
for (let i in list) {
strs += list[i].url + separator
}
return strs != '' ? strs.substr(0, strs.length - 1) : ''
}
</script>
<style scoped lang="scss">
.upload-file-uploader {
margin-bottom: 5px;
}
:deep(.upload-file-list .el-upload-list__item) {
border: 1px solid #e4e7ed;
line-height: 2;
margin-bottom: 10px;
position: relative;
}
:deep(.el-upload-list__item-file-name) {
max-width: 250px;
}
:deep(.upload-file-list .ele-upload-list__item-content) {
display: flex;
justify-content: space-between;
align-items: center;
color: inherit;
}
:deep(.ele-upload-list__item-content-action .el-link) {
margin-right: 10px;
}
</style>

View File

@ -1,24 +1,27 @@
<template> <template>
<div class="component-upload-image">
<el-upload <el-upload
ref="uploadRef" ref="uploadRef"
:multiple="limit > 1" :multiple="props.limit > 1"
name="file" name="file"
v-model="valueRef"
list-type="picture-card" list-type="picture-card"
v-model:file-list="fileList" v-model:file-list="fileList"
:show-file-list="true" :show-file-list="true"
:action="updateUrl" :action="updateUrl"
:headers="uploadHeaders" :headers="uploadHeaders"
:limit="limit" :limit="props.limit"
:before-upload="beforeUpload" :before-upload="beforeUpload"
:on-exceed="handleExceed" :on-exceed="handleExceed"
:on-success="handleFileSuccess" :on-success="handleFileSuccess"
:on-error="excelUploadError" :on-error="excelUploadError"
:on-remove="handleRemove" :on-remove="handleRemove"
:on-preview="handlePictureCardPreview" :on-preview="handlePictureCardPreview"
:class="{ hide: fileList.length >= limit }" :class="{ hide: fileList.length >= props.limit }"
> >
<Icon icon="ep:upload-filled" /> <Icon icon="ep:upload-filled" />
</el-upload> </el-upload>
</div>
<!-- 文件列表 --> <!-- 文件列表 -->
<Dialog v-model="dialogVisible" title="预览" width="800" append-to-body> <Dialog v-model="dialogVisible" title="预览" width="800" append-to-body>
<img :src="dialogImageUrl" style="display: block; max-width: 100%; margin: 0 auto" /> <img :src="dialogImageUrl" style="display: block; max-width: 100%; margin: 0 auto" />
@ -26,16 +29,17 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, watch } from 'vue' import { ref, watch } from 'vue'
import { Dialog } from '@/components/Dialog'
import { useMessage } from '@/hooks/web/useMessage' import { useMessage } from '@/hooks/web/useMessage'
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'
import { getAccessToken, getTenantId } from '@/utils/auth' import { getAccessToken, getTenantId } from '@/utils/auth'
import { ElUpload, UploadInstance, UploadProps, UploadRawFile, UploadUserFile } from 'element-plus' import { ElUpload, UploadInstance, UploadProps, UploadRawFile, UploadUserFile } from 'element-plus'
const message = useMessage() // const message = useMessage() //
const emit = defineEmits(['input']) const emit = defineEmits(['update:modelValue'])
const props = defineProps({ const props = defineProps({
imgs: propTypes.oneOfType([String, Object, Array]), modelValue: propTypes.oneOfType([String, Object, Array]),
title: propTypes.string.def('图片上传'), title: propTypes.string.def('图片上传'),
updateUrl: propTypes.string.def(import.meta.env.VITE_UPLOAD_URL), updateUrl: propTypes.string.def(import.meta.env.VITE_UPLOAD_URL),
fileType: propTypes.array.def(['jpg', 'png', 'gif', 'jpeg']), // , ['png', 'jpg', 'jpeg'] fileType: propTypes.array.def(['jpg', 'png', 'gif', 'jpeg']), // , ['png', 'jpg', 'jpeg']
@ -44,6 +48,7 @@ const props = defineProps({
isShowTip: propTypes.bool.def(false) // isShowTip: propTypes.bool.def(false) //
}) })
// ========== ========== // ========== ==========
const valueRef = ref(props.modelValue)
const uploadRef = ref<UploadInstance>() const uploadRef = ref<UploadInstance>()
const uploadList = ref<UploadUserFile[]>([]) const uploadList = ref<UploadUserFile[]>([])
const fileList = ref<UploadUserFile[]>([]) const fileList = ref<UploadUserFile[]>([])
@ -55,15 +60,15 @@ const uploadHeaders = ref({
'tenant-id': getTenantId() 'tenant-id': getTenantId()
}) })
watch( watch(
() => props.imgs, () => props.modelValue,
(val) => { (val) => {
if (val) { if (val) {
// , 穿map // , 穿map
const list = Array.isArray(props.imgs) const list = Array.isArray(props.modelValue)
? props.imgs ? props.modelValue
: Array.isArray(props.imgs?.split(',')) : Array.isArray(props.modelValue?.split(','))
? props.imgs?.split(',') ? props.modelValue?.split(',')
: Array.of(props.imgs) : Array.of(props.modelValue)
// //
fileList.value = list.map((item) => { fileList.value = list.map((item) => {
if (typeof item === 'string') { if (typeof item === 'string') {
@ -84,6 +89,10 @@ watch(
) )
// //
const beforeUpload: UploadProps['beforeUpload'] = (file: UploadRawFile) => { const beforeUpload: UploadProps['beforeUpload'] = (file: UploadRawFile) => {
if (fileList.value.length >= props.limit) {
message.error(`上传文件数量不能超过${props.limit}个!`)
return false
}
let fileExtension = '' let fileExtension = ''
if (file.name.lastIndexOf('.') > -1) { if (file.name.lastIndexOf('.') > -1) {
fileExtension = file.name.slice(file.name.lastIndexOf('.') + 1) fileExtension = file.name.slice(file.name.lastIndexOf('.') + 1)
@ -111,14 +120,12 @@ const beforeUpload: UploadProps['beforeUpload'] = (file: UploadRawFile) => {
// //
const handleFileSuccess: UploadProps['onSuccess'] = (res: any): void => { const handleFileSuccess: UploadProps['onSuccess'] = (res: any): void => {
message.success('上传成功') message.success('上传成功')
console.info(uploadList.value)
console.info(fileList.value)
uploadList.value.push({ name: res.data, url: res.data }) uploadList.value.push({ name: res.data, url: res.data })
if (uploadList.value.length == uploadNumber.value) { if (uploadList.value.length == uploadNumber.value) {
fileList.value = fileList.value.concat(uploadList.value) fileList.value = fileList.value.concat(uploadList.value)
uploadList.value = [] uploadList.value = []
uploadNumber.value = 0 uploadNumber.value = 0
emit('input', listToString(fileList.value)) emit('update:modelValue', listToString(fileList.value))
} }
} }
// //
@ -134,7 +141,7 @@ const handleRemove = (file) => {
const findex = fileList.value.map((f) => f.name).indexOf(file.name) const findex = fileList.value.map((f) => f.name).indexOf(file.name)
if (findex > -1) { if (findex > -1) {
fileList.value.splice(findex, 1) fileList.value.splice(findex, 1)
emit('input', listToString(fileList.value)) emit('update:modelValue', listToString(fileList.value))
} }
} }
// //

View File

@ -191,6 +191,7 @@ export default {
transition: all 0.5s; transition: all 0.5s;
} }
.verify-tips { .verify-tips {
text-indent: 10px;
position: absolute; position: absolute;
left: 0px; left: 0px;
bottom: 0px; bottom: 0px;

View File

@ -57,7 +57,7 @@
</div> </div>
</div> </div>
</template> </template>
<script type="text/babel"> <script type="text/babel" setup>
/** /**
* VerifyPoints * VerifyPoints
* @description 点选 * @description 点选
@ -67,9 +67,8 @@ import { aesEncrypt } from './../utils/ase'
import { getCodeApi, reqCheckApi } from '@/api/login' import { getCodeApi, reqCheckApi } from '@/api/login'
import { onMounted, reactive, ref, nextTick, toRefs, getCurrentInstance } from 'vue' import { onMounted, reactive, ref, nextTick, toRefs, getCurrentInstance } from 'vue'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
export default {
name: 'VerifyPoints', const props = defineProps({
props: {
//popfixed //popfixed
mode: { mode: {
type: String, type: String,
@ -101,8 +100,8 @@ export default {
} }
} }
} }
}, })
setup(props) {
const { t } = useI18n() const { t } = useI18n()
const { mode, captchaType } = toRefs(props) const { mode, captchaType } = toRefs(props)
const { proxy } = getCurrentInstance() const { proxy } = getCurrentInstance()
@ -212,7 +211,7 @@ export default {
tempPoints.push(Object.assign({}, pos)) tempPoints.push(Object.assign({}, pos))
return num.value + 1 return num.value + 1
} }
const refresh = function () { const refresh = async function () {
tempPoints.splice(0, tempPoints.length) tempPoints.splice(0, tempPoints.length)
barAreaColor.value = '#000' barAreaColor.value = '#000'
barAreaBorderColor.value = '#ddd' barAreaBorderColor.value = '#ddd'
@ -220,17 +219,16 @@ export default {
fontPos.splice(0, fontPos.length) fontPos.splice(0, fontPos.length)
checkPosArr.splice(0, checkPosArr.length) checkPosArr.splice(0, checkPosArr.length)
num.value = 1 num.value = 1
getPictrue() await getPictrue()
text.value = t('captcha.fail')
showRefresh.value = true showRefresh.value = true
} }
// //
function getPictrue() { const getPictrue = async () => {
let data = { let data = {
captchaType: captchaType.value captchaType: captchaType.value
} }
getCodeApi(data).then((res) => { const res = await getCodeApi(data)
if (res.repCode == '0000') { if (res.repCode == '0000') {
pointBackImgBase.value = res.repData.originalImageBase64 pointBackImgBase.value = res.repData.originalImageBase64
backToken.value = res.repData.token backToken.value = res.repData.token
@ -240,7 +238,6 @@ export default {
} else { } else {
text.value = res.repMsg text.value = res.repMsg
} }
})
} }
// //
const pointTransfrom = function (pointArr, imgSize) { const pointTransfrom = function (pointArr, imgSize) {
@ -251,31 +248,4 @@ export default {
}) })
return newPointArr return newPointArr
} }
return {
secretKey,
checkNum,
fontPos,
checkPosArr,
num,
pointBackImgBase,
poinTextList,
backToken,
setSize,
tempPoints,
text,
barAreaColor,
barAreaBorderColor,
showRefresh,
bindingClick,
init,
canvas,
canvasClick,
getMousePos,
createPoint,
refresh,
getPictrue,
pointTransfrom
}
}
}
</script> </script>

View File

@ -71,7 +71,7 @@
</div> </div>
</div> </div>
</template> </template>
<script type="text/babel"> <script type="text/babel" setup>
/** /**
* VerifySlide * VerifySlide
* @description 滑块 * @description 滑块
@ -90,10 +90,8 @@ import {
toRefs, toRefs,
getCurrentInstance getCurrentInstance
} from 'vue' } from 'vue'
// "captchaType":"blockPuzzle",
export default { const props = defineProps({
name: 'VerifySlide',
props: {
captchaType: { captchaType: {
type: String type: String
}, },
@ -141,8 +139,8 @@ export default {
} }
} }
} }
}, })
setup(props) {
const { t } = useI18n() const { t } = useI18n()
const { mode, captchaType, type, blockSize, explain } = toRefs(props) const { mode, captchaType, type, blockSize, explain } = toRefs(props)
const { proxy } = getCurrentInstance() const { proxy } = getCurrentInstance()
@ -153,7 +151,6 @@ export default {
backToken = ref(''), //token backToken = ref(''), //token
startMoveTime = ref(''), // startMoveTime = ref(''), //
endMovetime = ref(''), // endMovetime = ref(''), //
tipsBackColor = ref(''), //
tipWords = ref(''), tipWords = ref(''),
text = ref(''), text = ref(''),
finishText = ref(''), finishText = ref(''),
@ -163,8 +160,6 @@ export default {
barHeight: 0, barHeight: 0,
barWidth: 0 barWidth: 0
}), }),
top = ref(0),
left = ref(0),
moveBlockLeft = ref(undefined), moveBlockLeft = ref(undefined),
leftBarWidth = ref(undefined), leftBarWidth = ref(undefined),
// //
@ -182,7 +177,7 @@ export default {
const barArea = computed(() => { const barArea = computed(() => {
return proxy.$el.querySelector('.verify-bar-area') return proxy.$el.querySelector('.verify-bar-area')
}) })
function init() { const init = () => {
if (explain.value === '') { if (explain.value === '') {
text.value = t('captcha.slide') text.value = t('captcha.slide')
} else { } else {
@ -239,7 +234,7 @@ export default {
} }
}) })
// //
function start(e) { const start = (e) => {
e = e || window.event e = e || window.event
if (!e.touches) { if (!e.touches) {
//PC //PC
@ -260,7 +255,7 @@ export default {
} }
} }
// //
function move(e) { const move = (e) => {
e = e || window.event e = e || window.event
if (status.value && isEnd.value == false) { if (status.value && isEnd.value == false) {
if (!e.touches) { if (!e.touches) {
@ -289,7 +284,7 @@ export default {
} }
// //
function end() { const end = () => {
endMovetime.value = +new Date() endMovetime.value = +new Date()
// //
if (status.value && isEnd.value == false) { if (status.value && isEnd.value == false) {
@ -350,7 +345,7 @@ export default {
} }
} }
const refresh = () => { const refresh = async () => {
showRefresh.value = true showRefresh.value = true
finishText.value = '' finishText.value = ''
@ -366,7 +361,7 @@ export default {
iconClass.value = 'icon-right' iconClass.value = 'icon-right'
isEnd.value = false isEnd.value = false
getPictrue() await getPictrue()
setTimeout(() => { setTimeout(() => {
transitionWidth.value = '' transitionWidth.value = ''
transitionLeft.value = '' transitionLeft.value = ''
@ -375,11 +370,11 @@ export default {
} }
// //
function getPictrue() { const getPictrue = async () => {
let data = { let data = {
captchaType: captchaType.value captchaType: captchaType.value
} }
getCodeApi(data).then((res) => { const res = await getCodeApi(data)
if (res.repCode == '0000') { if (res.repCode == '0000') {
backImgBase.value = res.repData.originalImageBase64 backImgBase.value = res.repData.originalImageBase64
blockBackImgBase.value = res.repData.jigsawImageBase64 blockBackImgBase.value = res.repData.jigsawImageBase64
@ -388,39 +383,5 @@ export default {
} else { } else {
tipWords.value = res.repMsg tipWords.value = res.repMsg
} }
})
}
return {
secretKey, //ase
passFlag, //
backImgBase, //
blockBackImgBase, //
backToken, //token
startMoveTime, //
endMovetime, //
tipsBackColor, //
tipWords,
text,
finishText,
setSize,
top,
left,
moveBlockLeft,
leftBarWidth,
//
moveBlockBackgroundColor,
leftBarBorderColor,
iconColor,
iconClass,
status, //
isEnd, //
showRefresh,
transitionLeft,
transitionWidth,
barArea,
refresh,
start
}
}
} }
</script> </script>

View File

@ -10,8 +10,8 @@ const props = defineProps({
fullscreen: propTypes.bool.def(false), fullscreen: propTypes.bool.def(false),
loading: propTypes.bool.def(false), loading: propTypes.bool.def(false),
title: propTypes.string.def('弹窗'), title: propTypes.string.def('弹窗'),
width: propTypes.string.def('800'), width: propTypes.string.def('40%'),
height: propTypes.string.def('480'), height: propTypes.string.def('60%'),
minWidth: propTypes.string.def('460'), minWidth: propTypes.string.def('460'),
minHeight: propTypes.string.def('320'), minHeight: propTypes.string.def('320'),
showFooter: propTypes.bool.def(true) showFooter: propTypes.bool.def(true)

View File

@ -3,7 +3,6 @@ import { Icon } from './Icon'
import { Form } from '@/components/Form' import { Form } from '@/components/Form'
import { Table } from '@/components/Table' import { Table } from '@/components/Table'
import { Search } from '@/components/Search' import { Search } from '@/components/Search'
import { Dialog } from '@/components/Dialog'
import { XModal } from '@/components/XModal' import { XModal } from '@/components/XModal'
import { XButton, XTextButton } from '@/components/XButton' import { XButton, XTextButton } from '@/components/XButton'
import { DictTag } from '@/components/DictTag' import { DictTag } from '@/components/DictTag'
@ -15,7 +14,6 @@ export const setupGlobCom = (app: App<Element>): void => {
app.component('Form', Form) app.component('Form', Form)
app.component('Table', Table) app.component('Table', Table)
app.component('Search', Search) app.component('Search', Search)
app.component('Dialog', Dialog)
app.component('XModal', XModal) app.component('XModal', XModal)
app.component('XButton', XButton) app.component('XButton', XButton)
app.component('XTextButton', XTextButton) app.component('XTextButton', XTextButton)

View File

@ -168,7 +168,11 @@ service.interceptors.response.use(
ElMessage.error(t('sys.api.errMsg500')) ElMessage.error(t('sys.api.errMsg500'))
return Promise.reject(new Error(msg)) return Promise.reject(new Error(msg))
} else if (code === 901) { } else if (code === 901) {
ElMessage.error( ElMessage.error({
duration: 5,
offset: 300,
dangerouslyUseHTMLString: true,
message:
'<div>' + '<div>' +
t('sys.api.errMsg901') + t('sys.api.errMsg901') +
'</div>' + '</div>' +
@ -176,7 +180,7 @@ service.interceptors.response.use(
'<div>参考 https://doc.iocoder.cn/ 教程</div>' + '<div>参考 https://doc.iocoder.cn/ 教程</div>' +
'<div> &nbsp; </div>' + '<div> &nbsp; </div>' +
'<div>5 分钟搭建本地环境</div>' '<div>5 分钟搭建本地环境</div>'
) })
return Promise.reject(new Error(msg)) return Promise.reject(new Error(msg))
} else if (code !== 200) { } else if (code !== 200) {
if (msg === '无效的刷新令牌') { if (msg === '无效的刷新令牌') {

View File

@ -1,5 +1,5 @@
import type { App } from 'vue' import type { App } from 'vue'
import { useCache } from '@/hooks/web/useCache' import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
const { t } = useI18n() // 国际化 const { t } = useI18n() // 国际化
@ -8,7 +8,7 @@ export function hasPermi(app: App<Element>) {
const { wsCache } = useCache() const { wsCache } = useCache()
const { value } = binding const { value } = binding
const all_permission = '*:*:*' const all_permission = '*:*:*'
const permissions = wsCache.get('user').permissions const permissions = wsCache.get(CACHE_KEY.USER).permissions
if (value && value instanceof Array && value.length > 0) { if (value && value instanceof Array && value.length > 0) {
const permissionFlag = value const permissionFlag = value

View File

@ -1,5 +1,5 @@
import type { App } from 'vue' import type { App } from 'vue'
import { useCache } from '@/hooks/web/useCache' import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
const { t } = useI18n() // 国际化 const { t } = useI18n() // 国际化
@ -8,7 +8,7 @@ export function hasRole(app: App<Element>) {
const { wsCache } = useCache() const { wsCache } = useCache()
const { value } = binding const { value } = binding
const super_admin = 'admin' const super_admin = 'admin'
const roles = wsCache.get('user').roles const roles = wsCache.get(CACHE_KEY.USER).roles
if (value && value instanceof Array && value.length > 0) { if (value && value instanceof Array && value.length > 0) {
const roleFlag = value const roleFlag = value

View File

@ -6,6 +6,16 @@ import WebStorageCache from 'web-storage-cache'
type CacheType = 'localStorage' | 'sessionStorage' type CacheType = 'localStorage' | 'sessionStorage'
export const CACHE_KEY = {
IS_DARK: 'isDark',
USER: 'user',
LANG: 'lang',
THEME: 'theme',
LAYOUT: 'layout',
ROLE_ROUTERS: 'roleRouters',
DICT_CACHE: 'dictCache'
}
export const useCache = (type: CacheType = 'localStorage') => { export const useCache = (type: CacheType = 'localStorage') => {
const wsCache: WebStorageCache = new WebStorageCache({ const wsCache: WebStorageCache = new WebStorageCache({
storage: type storage: type

View File

@ -208,7 +208,8 @@ const filterTableSchema = (crudSchema: VxeCrudSchema): VxeGridPropTypes.Columns
const tableSchemaItem = { const tableSchemaItem = {
...schemaItem.table, ...schemaItem.table,
field: schemaItem.field, field: schemaItem.field,
title: schemaItem.table?.title || schemaItem.title title: schemaItem.table?.title || schemaItem.title,
minWidth: '80px'
} }
tableSchemaItem.showOverflow = 'tooltip' tableSchemaItem.showOverflow = 'tooltip'
if (schemaItem?.formatter) { if (schemaItem?.formatter) {
@ -231,6 +232,7 @@ const filterTableSchema = (crudSchema: VxeCrudSchema): VxeGridPropTypes.Columns
const tableSchemaItem = { const tableSchemaItem = {
title: crudSchema.actionTitle ? crudSchema.actionTitle : t('table.action'), title: crudSchema.actionTitle ? crudSchema.actionTitle : t('table.action'),
field: 'actionbtns', field: 'actionbtns',
fixed: 'right' as unknown as VxeColumnPropTypes.Fixed,
width: crudSchema.actionWidth ? crudSchema.actionWidth : '200px', width: crudSchema.actionWidth ? crudSchema.actionWidth : '200px',
slots: { slots: {
default: 'actionbtns_default' default: 'actionbtns_default'

View File

@ -11,10 +11,12 @@ const message = useMessage() // 消息弹窗
interface UseVxeGridConfig<T = any> { interface UseVxeGridConfig<T = any> {
allSchemas: VxeAllSchemas allSchemas: VxeAllSchemas
height?: number // 高度 默认730
topActionSlots?: boolean // 是否开启表格内顶部操作栏插槽 topActionSlots?: boolean // 是否开启表格内顶部操作栏插槽
treeConfig?: VxeTablePropTypes.TreeConfig // 树形表单配置 treeConfig?: VxeTablePropTypes.TreeConfig // 树形表单配置
isList?: boolean // 是否不带分页的list isList?: boolean // 是否不带分页的list
getListApi: (option: any) => Promise<T> // 获取列表接口 getListApi: (option: any) => Promise<T> // 获取列表接口
getAllListApi?: (option: any) => Promise<T> // 获取全部数据接口 用于VXE导出
deleteApi?: (option: any) => Promise<T> // 删除接口 deleteApi?: (option: any) => Promise<T> // 删除接口
exportListApi?: (option: any) => Promise<T> // 导出接口 exportListApi?: (option: any) => Promise<T> // 导出接口
exportName?: string // 导出文件夹名称 exportName?: string // 导出文件夹名称
@ -47,7 +49,7 @@ export const useVxeGrid = <T = any>(config?: UseVxeGridConfig<T>) => {
const gridOptions = reactive<VxeGridProps<any>>({ const gridOptions = reactive<VxeGridProps<any>>({
loading: true, loading: true,
size: currentSize as any, size: currentSize as any,
height: 730, // 1080高度 height: config?.height ? config.height : 730,
rowConfig: { rowConfig: {
isCurrent: true, // 当鼠标点击行时,是否要高亮当前行 isCurrent: true, // 当鼠标点击行时,是否要高亮当前行
isHover: true // 当鼠标移到行时,是否要高亮当前行 isHover: true // 当鼠标移到行时,是否要高亮当前行
@ -99,8 +101,8 @@ export const useVxeGrid = <T = any>(config?: UseVxeGridConfig<T>) => {
queryAll: ({ form }) => { queryAll: ({ form }) => {
const queryParams = Object.assign({}, JSON.parse(JSON.stringify(form))) const queryParams = Object.assign({}, JSON.parse(JSON.stringify(form)))
return new Promise(async (resolve) => { return new Promise(async (resolve) => {
if (config?.exportListApi) { if (config?.getAllListApi) {
resolve(await config?.exportListApi(queryParams)) resolve(await config?.getAllListApi(queryParams))
} else { } else {
resolve(await config?.getListApi(queryParams)) resolve(await config?.getListApi(queryParams))
} }
@ -113,7 +115,7 @@ export const useVxeGrid = <T = any>(config?: UseVxeGridConfig<T>) => {
// 默认选中类型 // 默认选中类型
type: 'csv', type: 'csv',
// 自定义数据量列表 // 自定义数据量列表
modes: ['current', 'all'], modes: config?.getAllListApi ? ['current', 'all'] : ['current'],
columns: config?.allSchemas.printSchema columns: config?.allSchemas.printSchema
} }
}) })

View File

@ -2,7 +2,7 @@
import { computed, defineComponent, unref } from 'vue' import { computed, defineComponent, unref } from 'vue'
import { useAppStore } from '@/store/modules/app' import { useAppStore } from '@/store/modules/app'
import { Backtop } from '@/components/Backtop' import { Backtop } from '@/components/Backtop'
import { Setting } from '@/components/Setting' import { Setting } from '@/layout/components/Setting'
import { useRenderLayout } from './components/useRenderLayout' import { useRenderLayout } from './components/useRenderLayout'
import { useDesign } from '@/hooks/web/useDesign' import { useDesign } from '@/hooks/web/useDesign'

View File

@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { useTagsViewStore } from '@/store/modules/tagsView' import { useTagsViewStore } from '@/store/modules/tagsView'
import { useAppStore } from '@/store/modules/app' import { useAppStore } from '@/store/modules/app'
import { Footer } from '@/components/Footer' import { Footer } from '@/layout/components/Footer'
import { computed } from 'vue' import { computed } from 'vue'
const appStore = useAppStore() const appStore = useAppStore()

View File

@ -3,13 +3,13 @@ import { ElDrawer, ElDivider, ElMessage } from 'element-plus'
import { ref, unref, computed, watch } from 'vue' import { ref, unref, computed, watch } from 'vue'
import { useCssVar, useClipboard } from '@vueuse/core' import { useCssVar, useClipboard } from '@vueuse/core'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
import { useCache } from '@/hooks/web/useCache' import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
import { useDesign } from '@/hooks/web/useDesign' import { useDesign } from '@/hooks/web/useDesign'
import { trim, setCssVar } from '@/utils' import { trim, setCssVar } from '@/utils'
import { colorIsDark, lighten, hexToRGB } from '@/utils/color' import { colorIsDark, lighten, hexToRGB } from '@/utils/color'
import { useAppStore } from '@/store/modules/app' import { useAppStore } from '@/store/modules/app'
import { ThemeSwitch } from '@/components/ThemeSwitch' import { ThemeSwitch } from '@/layout/components/ThemeSwitch'
import ColorRadioPicker from './components/ColorRadioPicker.vue' import ColorRadioPicker from './components/ColorRadioPicker.vue'
import InterfaceDisplay from './components/InterfaceDisplay.vue' import InterfaceDisplay from './components/InterfaceDisplay.vue'
import LayoutRadioPicker from './components/LayoutRadioPicker.vue' import LayoutRadioPicker from './components/LayoutRadioPicker.vue'
@ -188,9 +188,9 @@ const copyConfig = async () => {
// //
const clear = () => { const clear = () => {
const { wsCache } = useCache() const { wsCache } = useCache()
wsCache.delete('layout') wsCache.delete(CACHE_KEY.LAYOUT)
wsCache.delete('theme') wsCache.delete(CACHE_KEY.THEME)
wsCache.delete('isDark') wsCache.delete(CACHE_KEY.IS_DARK)
window.location.reload() window.location.reload()
} }
</script> </script>

View File

@ -5,7 +5,7 @@ import { computed, unref, defineComponent, watch, ref, onMounted } from 'vue'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
import { ElScrollbar } from 'element-plus' import { ElScrollbar } from 'element-plus'
import { Icon } from '@/components/Icon' import { Icon } from '@/components/Icon'
import { Menu } from '@/components/Menu' import { Menu } from '@/layout/components/Menu'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { pathResolve } from '@/utils/routerHelper' import { pathResolve } from '@/utils/routerHelper'
import { cloneDeep } from 'lodash-es' import { cloneDeep } from 'lodash-es'

View File

@ -1,4 +1,4 @@
import { getAllParentPath } from '@/components/Menu/src/helper' import { getAllParentPath } from '@/layout/components/Menu/src/helper'
import type { RouteMeta } from 'vue-router' import type { RouteMeta } from 'vue-router'
import { isUrl } from '@/utils/is' import { isUrl } from '@/utils/is'
import { cloneDeep } from 'lodash-es' import { cloneDeep } from 'lodash-es'

View File

@ -7,7 +7,7 @@ import { useTagsViewStore } from '@/store/modules/tagsView'
import { useAppStore } from '@/store/modules/app' import { useAppStore } from '@/store/modules/app'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
import { filterAffixTags } from './helper' import { filterAffixTags } from './helper'
import { ContextMenu, ContextMenuExpose } from '@/components/ContextMenu' import { ContextMenu, ContextMenuExpose } from '@/layout/components/ContextMenu'
import { useDesign } from '@/hooks/web/useDesign' import { useDesign } from '@/hooks/web/useDesign'
import { useTemplateRefsList } from '@vueuse/core' import { useTemplateRefsList } from '@vueuse/core'
import { ElScrollbar } from 'element-plus' import { ElScrollbar } from 'element-plus'
@ -114,8 +114,8 @@ const toLastView = () => {
addTags() addTags()
return return
} }
// You can set another route // TODO: You can set another route
push(permissionStore.getAddRouters[0].path) push('/')
} }
} }
@ -128,7 +128,6 @@ const moveToCurrentTag = async () => {
if (v.fullPath !== unref(currentRoute).fullPath) { if (v.fullPath !== unref(currentRoute).fullPath) {
tagsViewStore.updateVisitedView(unref(currentRoute)) tagsViewStore.updateVisitedView(unref(currentRoute))
} }
break break
} }
} }

View File

@ -1,11 +1,11 @@
<script lang="tsx"> <script lang="tsx">
import { defineComponent, computed } from 'vue' import { defineComponent, computed } from 'vue'
import { Collapse } from '@/components/Collapse' import { Collapse } from '@/layout/components/Collapse'
import { LocaleDropdown } from '@/components/LocaleDropdown' import { LocaleDropdown } from '@/layout/components/LocaleDropdown'
import { SizeDropdown } from '@/components/SizeDropdown' import { SizeDropdown } from '@/layout/components/SizeDropdown'
import { UserInfo } from '@/components/UserInfo' import { UserInfo } from '@/layout/components/UserInfo'
import { Screenfull } from '@/components/Screenfull' import { Screenfull } from '@/layout/components/Screenfull'
import { Breadcrumb } from '@/components/Breadcrumb' import { Breadcrumb } from '@/layout/components/Breadcrumb'
import { useAppStore } from '@/store/modules/app' import { useAppStore } from '@/store/modules/app'
import { useDesign } from '@/hooks/web/useDesign' import { useDesign } from '@/hooks/web/useDesign'

View File

@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ElDropdown, ElDropdownMenu, ElDropdownItem, ElMessageBox } from 'element-plus' import { ElDropdown, ElDropdownMenu, ElDropdownItem, ElMessageBox } from 'element-plus'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
import { useCache } from '@/hooks/web/useCache' import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { useDesign } from '@/hooks/web/useDesign' import { useDesign } from '@/hooks/web/useDesign'
import avatarImg from '@/assets/imgs/avatar.gif' import avatarImg from '@/assets/imgs/avatar.gif'
@ -22,7 +22,7 @@ const { getPrefixCls } = useDesign()
const prefixCls = getPrefixCls('user-info') const prefixCls = getPrefixCls('user-info')
const user = wsCache.get('user') const user = wsCache.get(CACHE_KEY.USER)
const avatar = user.user.avatar ? user.user.avatar : avatarImg const avatar = user.user.avatar ? user.user.avatar : avatarImg

View File

@ -1,9 +1,9 @@
import { computed } from 'vue' import { computed } from 'vue'
import { useAppStore } from '@/store/modules/app' import { useAppStore } from '@/store/modules/app'
import { Menu } from '@/components/Menu' import { Menu } from '@/layout/components/Menu'
import { TabMenu } from '@/components/TabMenu' import { TabMenu } from '@/layout/components/TabMenu'
import { TagsView } from '@/components/TagsView' import { TagsView } from '@/layout/components/TagsView'
import { Logo } from '@/components/Logo' import { Logo } from '@/layout/components/Logo'
import AppView from './AppView.vue' import AppView from './AppView.vue'
import ToolHeader from './ToolHeader.vue' import ToolHeader from './ToolHeader.vue'
import { ElScrollbar } from 'element-plus' import { ElScrollbar } from 'element-plus'

View File

@ -1,18 +1,18 @@
import type { App } from 'vue' import type { App } from 'vue'
import { getAccessToken } from '@/utils/auth'
import type { RouteRecordRaw } from 'vue-router' import type { RouteRecordRaw } from 'vue-router'
import { createRouter, createWebHashHistory } from 'vue-router'
import remainingRouter from './modules/remaining' import remainingRouter from './modules/remaining'
import { isRelogin } from '@/config/axios/service'
import { getAccessToken } from '@/utils/auth'
import { useTitle } from '@/hooks/web/useTitle' import { useTitle } from '@/hooks/web/useTitle'
import { useNProgress } from '@/hooks/web/useNProgress' import { useNProgress } from '@/hooks/web/useNProgress'
import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
import { usePageLoading } from '@/hooks/web/usePageLoading' import { usePageLoading } from '@/hooks/web/usePageLoading'
import { createRouter, createWebHashHistory } from 'vue-router'
import { usePermissionStoreWithOut } from '@/store/modules/permission'
import { useDictStoreWithOut } from '@/store/modules/dict' import { useDictStoreWithOut } from '@/store/modules/dict'
import { useUserStoreWithOut } from '@/store/modules/user' import { useUserStoreWithOut } from '@/store/modules/user'
import { listSimpleDictDataApi } from '@/api/system/dict/dict.data' import { usePermissionStoreWithOut } from '@/store/modules/permission'
import { isRelogin } from '@/config/axios/service'
import { getInfoApi } from '@/api/login' import { getInfoApi } from '@/api/login'
import { useCache } from '@/hooks/web/useCache' import { listSimpleDictDataApi } from '@/api/system/dict/dict.data'
const { wsCache } = useCache('sessionStorage') const { wsCache } = useCache('sessionStorage')
@ -50,12 +50,12 @@ router.beforeEach(async (to, from, next) => {
const dictStore = useDictStoreWithOut() const dictStore = useDictStoreWithOut()
const userStore = useUserStoreWithOut() const userStore = useUserStoreWithOut()
const permissionStore = usePermissionStoreWithOut() const permissionStore = usePermissionStoreWithOut()
const dictMap = wsCache.get('dictCache') const dictMap = wsCache.get(CACHE_KEY.DICT_CACHE)
if (!dictMap) { if (!dictMap) {
const res = await listSimpleDictDataApi() const res = await listSimpleDictDataApi()
dictStore.setDictMap(res) dictStore.setDictMap(res)
} }
if (userStore.getRoles.length === 0) { if (!userStore.getIsSetUser) {
isRelogin.show = true isRelogin.show = true
const res = await getInfoApi() const res = await getInfoApi()
await userStore.setUserInfoAction(res) await userStore.setUserInfoAction(res)

View File

@ -2,7 +2,7 @@ import { defineStore } from 'pinia'
import { store } from '../index' import { store } from '../index'
import { setCssVar, humpToUnderline } from '@/utils' import { setCssVar, humpToUnderline } from '@/utils'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { useCache } from '@/hooks/web/useCache' import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
import { ElementPlusSize } from '@/types/elementPlus' import { ElementPlusSize } from '@/types/elementPlus'
import { LayoutType } from '@/types/layout' import { LayoutType } from '@/types/layout'
import { ThemeTypes } from '@/types/theme' import { ThemeTypes } from '@/types/theme'
@ -61,10 +61,10 @@ export const useAppStore = defineStore('app', {
greyMode: false, // 是否开始灰色模式,用于特殊悼念日 greyMode: false, // 是否开始灰色模式,用于特殊悼念日
fixedMenu: wsCache.get('fixedMenu') || false, // 是否固定菜单 fixedMenu: wsCache.get('fixedMenu') || false, // 是否固定菜单
layout: wsCache.get('layout') || 'classic', // layout布局 layout: wsCache.get(CACHE_KEY.LAYOUT) || 'classic', // layout布局
isDark: wsCache.get('isDark') || false, // 是否是暗黑模式 isDark: wsCache.get(CACHE_KEY.IS_DARK) || false, // 是否是暗黑模式
currentSize: wsCache.get('default') || 'default', // 组件尺寸 currentSize: wsCache.get('default') || 'default', // 组件尺寸
theme: wsCache.get('theme') || { theme: wsCache.get(CACHE_KEY.THEME) || {
// 主题色 // 主题色
elColorPrimary: '#409eff', elColorPrimary: '#409eff',
// 左侧菜单边框颜色 // 左侧菜单边框颜色
@ -223,7 +223,7 @@ export const useAppStore = defineStore('app', {
return return
} }
this.layout = layout this.layout = layout
wsCache.set('layout', this.layout) wsCache.set(CACHE_KEY.LAYOUT, this.layout)
}, },
setTitle(title: string) { setTitle(title: string) {
this.title = title this.title = title
@ -237,7 +237,7 @@ export const useAppStore = defineStore('app', {
document.documentElement.classList.add('light') document.documentElement.classList.add('light')
document.documentElement.classList.remove('dark') document.documentElement.classList.remove('dark')
} }
wsCache.set('isDark', this.isDark) wsCache.set(CACHE_KEY.IS_DARK, this.isDark)
}, },
setCurrentSize(currentSize: ElementPlusSize) { setCurrentSize(currentSize: ElementPlusSize) {
this.currentSize = currentSize this.currentSize = currentSize
@ -248,7 +248,7 @@ export const useAppStore = defineStore('app', {
}, },
setTheme(theme: ThemeTypes) { setTheme(theme: ThemeTypes) {
this.theme = Object.assign(this.theme, theme) this.theme = Object.assign(this.theme, theme)
wsCache.set('theme', this.theme) wsCache.set(CACHE_KEY.THEME, this.theme)
}, },
setCssVarTheme() { setCssVarTheme() {
for (const key in this.theme) { for (const key in this.theme) {

View File

@ -1,7 +1,7 @@
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { store } from '../index' import { store } from '../index'
import { DictDataVO } from '@/api/system/dict/types' import { DictDataVO } from '@/api/system/dict/types'
import { useCache } from '@/hooks/web/useCache' import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
const { wsCache } = useCache('sessionStorage') const { wsCache } = useCache('sessionStorage')
export interface DictValueType { export interface DictValueType {
@ -24,7 +24,7 @@ export const useDictStore = defineStore('dict', {
}), }),
getters: { getters: {
getDictMap(): Recordable { getDictMap(): Recordable {
const dictMap = wsCache.get('dictCache') const dictMap = wsCache.get(CACHE_KEY.DICT_CACHE)
return dictMap ? dictMap : this.dictMap return dictMap ? dictMap : this.dictMap
}, },
getHasDictData(): boolean { getHasDictData(): boolean {
@ -54,7 +54,7 @@ export const useDictStore = defineStore('dict', {
}) })
}) })
this.dictMap = dictDataMap this.dictMap = dictDataMap
wsCache.set('dictCache', dictDataMap, { exp: 60 }) // 60 秒 过期 wsCache.set(CACHE_KEY.DICT_CACHE, dictDataMap, { exp: 60 }) // 60 秒 过期
} }
} }
}) })

View File

@ -2,7 +2,7 @@ import { defineStore } from 'pinia'
import { store } from '../index' import { store } from '../index'
import zhCn from 'element-plus/es/locale/lang/zh-cn' import zhCn from 'element-plus/es/locale/lang/zh-cn'
import en from 'element-plus/es/locale/lang/en' import en from 'element-plus/es/locale/lang/en'
import { useCache } from '@/hooks/web/useCache' import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
import { LocaleDropdownType } from '@/types/localeDropdown' import { LocaleDropdownType } from '@/types/localeDropdown'
const { wsCache } = useCache() const { wsCache } = useCache()
@ -20,8 +20,8 @@ export const useLocaleStore = defineStore('locales', {
state: (): LocaleState => { state: (): LocaleState => {
return { return {
currentLocale: { currentLocale: {
lang: wsCache.get('lang') || 'zh-CN', lang: wsCache.get(CACHE_KEY.LANG) || 'zh-CN',
elLocale: elLocaleMap[wsCache.get('lang') || 'zh-CN'] elLocale: elLocaleMap[wsCache.get(CACHE_KEY.LANG) || 'zh-CN']
}, },
// 多语言 // 多语言
localeMap: [ localeMap: [
@ -49,7 +49,7 @@ export const useLocaleStore = defineStore('locales', {
// this.locale = Object.assign(this.locale, localeMap) // this.locale = Object.assign(this.locale, localeMap)
this.currentLocale.lang = localeMap?.lang this.currentLocale.lang = localeMap?.lang
this.currentLocale.elLocale = elLocaleMap[localeMap?.lang] this.currentLocale.elLocale = elLocaleMap[localeMap?.lang]
wsCache.set('lang', localeMap?.lang) wsCache.set(CACHE_KEY.LANG, localeMap?.lang)
} }
} }
}) })

View File

@ -4,7 +4,7 @@ import { cloneDeep } from 'lodash-es'
import remainingRouter from '@/router/modules/remaining' import remainingRouter from '@/router/modules/remaining'
import { generateRoute, flatMultiLevelRoutes } from '@/utils/routerHelper' import { generateRoute, flatMultiLevelRoutes } from '@/utils/routerHelper'
import { getAsyncRoutesApi } from '@/api/login' import { getAsyncRoutesApi } from '@/api/login'
import { useCache } from '@/hooks/web/useCache' import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
const { wsCache } = useCache() const { wsCache } = useCache()
@ -35,11 +35,11 @@ export const usePermissionStore = defineStore('permission', {
async generateRoutes(): Promise<unknown> { async generateRoutes(): Promise<unknown> {
return new Promise<void>(async (resolve) => { return new Promise<void>(async (resolve) => {
let res: AppCustomRouteRecordRaw[] let res: AppCustomRouteRecordRaw[]
if (wsCache.get('roleRouters')) { if (wsCache.get(CACHE_KEY.ROLE_ROUTERS)) {
res = wsCache.get('roleRouters') as AppCustomRouteRecordRaw[] res = wsCache.get(CACHE_KEY.ROLE_ROUTERS) as AppCustomRouteRecordRaw[]
} else { } else {
res = await getAsyncRoutesApi() res = await getAsyncRoutesApi()
wsCache.set('roleRouters', res) wsCache.set(CACHE_KEY.ROLE_ROUTERS, res)
} }
const routerMap: AppRouteRecordRaw[] = generateRoute(res as AppCustomRouteRecordRaw[]) const routerMap: AppRouteRecordRaw[] = generateRoute(res as AppCustomRouteRecordRaw[])
// 动态路由404一定要放到最后面 // 动态路由404一定要放到最后面

View File

@ -1,7 +1,7 @@
import { store } from '../index' import { store } from '../index'
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { getAccessToken, removeToken } from '@/utils/auth' import { getAccessToken, removeToken } from '@/utils/auth'
import { useCache } from '@/hooks/web/useCache' import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
const { wsCache } = useCache() const { wsCache } = useCache()
@ -13,6 +13,7 @@ interface UserVO {
interface UserInfoVO { interface UserInfoVO {
permissions: string[] permissions: string[]
roles: string[] roles: string[]
isSetUser: boolean
user: UserVO user: UserVO
} }
@ -20,6 +21,7 @@ export const useUserStore = defineStore('admin-user', {
state: (): UserInfoVO => ({ state: (): UserInfoVO => ({
permissions: [], permissions: [],
roles: [], roles: [],
isSetUser: false,
user: { user: {
id: 0, id: 0,
avatar: '', avatar: '',
@ -33,6 +35,9 @@ export const useUserStore = defineStore('admin-user', {
getRoles(): string[] { getRoles(): string[] {
return this.roles return this.roles
}, },
getIsSetUser(): boolean {
return this.isSetUser
},
getUser(): UserVO { getUser(): UserVO {
return this.user return this.user
} }
@ -46,7 +51,8 @@ export const useUserStore = defineStore('admin-user', {
this.permissions = userInfo.permissions this.permissions = userInfo.permissions
this.roles = userInfo.roles this.roles = userInfo.roles
this.user = userInfo.user this.user = userInfo.user
wsCache.set('user', userInfo) this.isSetUser = true
wsCache.set(CACHE_KEY.USER, userInfo)
}, },
loginOut() { loginOut() {
removeToken() removeToken()
@ -56,6 +62,7 @@ export const useUserStore = defineStore('admin-user', {
resetState() { resetState() {
this.permissions = [] this.permissions = []
this.roles = [] this.roles = []
this.isSetUser = false
this.user = { this.user = {
id: 0, id: 0,
avatar: '', avatar: '',

View File

@ -21,6 +21,8 @@ export type ComponentName =
| 'TreeSelect' | 'TreeSelect'
| 'InputPassword' | 'InputPassword'
| 'Editor' | 'Editor'
| 'UploadImg'
| 'UploadFile'
export type ColProps = { export type ColProps = {
span?: number span?: number

View File

@ -1,31 +1,33 @@
import Cookies from 'js-cookie' import { useCache } from '@/hooks/web/useCache'
import { TokenType } from '@/api/login/types' import { TokenType } from '@/api/login/types'
import { decrypt, encrypt } from '@/utils/jsencrypt' import { decrypt, encrypt } from '@/utils/jsencrypt'
const { wsCache } = useCache()
const AccessTokenKey = 'ACCESS_TOKEN' const AccessTokenKey = 'ACCESS_TOKEN'
const RefreshTokenKey = 'REFRESH_TOKEN' const RefreshTokenKey = 'REFRESH_TOKEN'
// 获取token // 获取token
export const getAccessToken = () => { export const getAccessToken = () => {
// 此处与TokenKey相同此写法解决初始化时Cookies中不存在TokenKey报错 // 此处与TokenKey相同此写法解决初始化时Cookies中不存在TokenKey报错
return Cookies.get(AccessTokenKey) ? Cookies.get(AccessTokenKey) : Cookies.get('ACCESS_TOKEN') return wsCache.get(AccessTokenKey) ? wsCache.get(AccessTokenKey) : wsCache.get('ACCESS_TOKEN')
} }
// 刷新token // 刷新token
export const getRefreshToken = () => { export const getRefreshToken = () => {
return Cookies.get(RefreshTokenKey) return wsCache.get(RefreshTokenKey)
} }
// 设置token // 设置token
export const setToken = (token: TokenType) => { export const setToken = (token: TokenType) => {
Cookies.set(RefreshTokenKey, token.refreshToken, token.expiresTime) wsCache.set(RefreshTokenKey, token.refreshToken, { exp: token.expiresTime })
Cookies.set(AccessTokenKey, token.accessToken) wsCache.set(AccessTokenKey, token.accessToken)
} }
// 删除token // 删除token
export const removeToken = () => { export const removeToken = () => {
Cookies.remove(AccessTokenKey) wsCache.delete(AccessTokenKey)
Cookies.remove(RefreshTokenKey) wsCache.delete(RefreshTokenKey)
} }
/** 格式化tokenjwt格式 */ /** 格式化tokenjwt格式 */
@ -39,40 +41,40 @@ const PasswordKey = 'PASSWORD'
const RememberMeKey = 'REMEMBER_ME' const RememberMeKey = 'REMEMBER_ME'
export const getUsername = () => { export const getUsername = () => {
return Cookies.get(UsernameKey) return wsCache.get(UsernameKey)
} }
export const setUsername = (username: string) => { export const setUsername = (username: string) => {
Cookies.set(UsernameKey, username) wsCache.set(UsernameKey, username, { exp: 30 * 24 * 60 * 60 })
} }
export const removeUsername = () => { export const removeUsername = () => {
Cookies.remove(UsernameKey) wsCache.delete(UsernameKey)
} }
export const getPassword = () => { export const getPassword = () => {
const password = Cookies.get(PasswordKey) const password = wsCache.get(PasswordKey)
return password ? decrypt(password) : undefined return password ? decrypt(password) : undefined
} }
export const setPassword = (password: string) => { export const setPassword = (password: string) => {
Cookies.set(PasswordKey, encrypt(password)) wsCache.set(PasswordKey, encrypt(password), { exp: 30 * 24 * 60 * 60 })
} }
export const removePassword = () => { export const removePassword = () => {
Cookies.remove(PasswordKey) wsCache.delete(PasswordKey)
} }
export const getRememberMe = () => { export const getRememberMe = () => {
return Cookies.get(RememberMeKey) === 'true' return wsCache.get(RememberMeKey) === true
} }
export const setRememberMe = (rememberMe: string) => { export const setRememberMe = (rememberMe: boolean) => {
Cookies.set(RememberMeKey, rememberMe) wsCache.set(RememberMeKey, rememberMe, { exp: 30 * 24 * 60 * 60 })
} }
export const removeRememberMe = () => { export const removeRememberMe = () => {
Cookies.remove(RememberMeKey) wsCache.delete(RememberMeKey)
} }
// ========== 租户相关 ========== // ========== 租户相关 ==========
@ -81,25 +83,25 @@ const TenantIdKey = 'TENANT_ID'
const TenantNameKey = 'TENANT_NAME' const TenantNameKey = 'TENANT_NAME'
export const getTenantName = () => { export const getTenantName = () => {
return Cookies.get(TenantNameKey) return wsCache.get(TenantNameKey)
} }
export const setTenantName = (username: string) => { export const setTenantName = (username: string) => {
Cookies.set(TenantNameKey, username) wsCache.set(TenantNameKey, username, { exp: 30 * 24 * 60 * 60 })
} }
export const removeTenantName = () => { export const removeTenantName = () => {
Cookies.remove(TenantNameKey) wsCache.delete(TenantNameKey)
} }
export const getTenantId = () => { export const getTenantId = () => {
return Cookies.get(TenantIdKey) return wsCache.get(TenantIdKey)
} }
export const setTenantId = (username: string) => { export const setTenantId = (username: string) => {
Cookies.set(TenantIdKey, username) wsCache.set(TenantIdKey, username)
} }
export const removeTenantId = () => { export const removeTenantId = () => {
Cookies.remove(TenantIdKey) wsCache.delete(TenantIdKey)
} }

View File

@ -18,7 +18,7 @@ export const getParentLayout = () => {
} }
// 按照路由中meta下的rank等级升序来排序路由 // 按照路由中meta下的rank等级升序来排序路由
export function ascending(arr: any[]) { export const ascending = (arr: any[]) => {
arr.forEach((v) => { arr.forEach((v) => {
if (v?.meta?.rank === null) v.meta.rank = undefined if (v?.meta?.rank === null) v.meta.rank = undefined
if (v?.meta?.rank === 0) { if (v?.meta?.rank === 0) {
@ -109,7 +109,7 @@ export const generateRoute = (routes: AppCustomRouteRecordRaw[]): AppRouteRecord
data.children = generateRoute(route.children) data.children = generateRoute(route.children)
} }
} }
res.push(data) res.push(data as AppRouteRecordRaw)
} }
return res return res
} }
@ -203,7 +203,7 @@ const addToChildren = (
} }
} }
} }
function toCamelCase(str: string, upperCaseFirst: boolean) { const toCamelCase = (str: string, upperCaseFirst: boolean) => {
str = (str || '').toLowerCase().replace(/-(.)/g, function (group1: string) { str = (str || '').toLowerCase().replace(/-(.)/g, function (group1: string) {
return group1.toUpperCase() return group1.toUpperCase()
}) })

View File

@ -8,6 +8,11 @@ const DEFAULT_CONFIG: TreeHelperConfig = {
children: 'children', children: 'children',
pid: 'pid' pid: 'pid'
} }
export const defaultProps = {
children: 'children',
label: 'name',
value: 'id'
}
const getConfig = (config: Partial<TreeHelperConfig>) => Object.assign({}, DEFAULT_CONFIG, config) const getConfig = (config: Partial<TreeHelperConfig>) => Object.assign({}, DEFAULT_CONFIG, config)
@ -214,6 +219,10 @@ export const eachTree = (treeDatas: any[], callBack: Fn, parentNode = {}) => {
* @param {*} children 'children' * @param {*} children 'children'
*/ */
export const handleTree = (data: any[], id?: string, parentId?: string, children?: string) => { export const handleTree = (data: any[], id?: string, parentId?: string, children?: string) => {
if (!Array.isArray(data)) {
console.warn('data must be an array')
return []
}
const config = { const config = {
id: id || 'id', id: id || 'id',
parentId: parentId || 'parentId', parentId: parentId || 'parentId',

View File

@ -1,5 +1,5 @@
<template> <template>
<Error type="403" @error-click="errorClick" /> <Error type="403" @error-click="errorClick()" />
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { Error } from '@/components/Error' import { Error } from '@/components/Error'

View File

@ -1,5 +1,5 @@
<template> <template>
<Error @error-click="errorClick" /> <Error @error-click="errorClick()" />
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { Error } from '@/components/Error' import { Error } from '@/components/Error'

View File

@ -1,5 +1,5 @@
<template> <template>
<Error type="500" @error-click="errorClick" /> <Error type="500" @error-click="errorClick()" />
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { Error } from '@/components/Error' import { Error } from '@/components/Error'

View File

@ -167,20 +167,21 @@ import { EChartsOption } from 'echarts'
import { ElRow, ElCol, ElSkeleton, ElCard, ElDivider, ElLink } from 'element-plus' import { ElRow, ElCol, ElSkeleton, ElCard, ElDivider, ElLink } from 'element-plus'
import { formatTime } from '@/utils' import { formatTime } from '@/utils'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
import { useCache } from '@/hooks/web/useCache' import { useUserStore } from '@/store/modules/user'
import { useWatermark } from '@/hooks/web/useWatermark' import { useWatermark } from '@/hooks/web/useWatermark'
import { Echart } from '@/components/Echart' import { Echart } from '@/components/Echart'
import { CountTo } from '@/components/CountTo' import { CountTo } from '@/components/CountTo'
import { Highlight } from '@/components/Highlight' import { Highlight } from '@/components/Highlight'
import avatarImg from '@/assets/imgs/avatar.gif'
import type { WorkplaceTotal, Project, Notice, Shortcut } from './types' import type { WorkplaceTotal, Project, Notice, Shortcut } from './types'
import { pieOptions, barOptions } from './echarts-data' import { pieOptions, barOptions } from './echarts-data'
const { t } = useI18n() const { t } = useI18n()
const { wsCache } = useCache() const userStore = useUserStore()
const { setWatermark } = useWatermark() const { setWatermark } = useWatermark()
const loading = ref(true) const loading = ref(true)
const avatar = wsCache.get('user').user.avatar const avatar = userStore.getUser.avatar ? userStore.getUser.avatar : avatarImg
const username = wsCache.get('user').user.nickname const username = userStore.getUser.nickname
const pieOptionsData = reactive<EChartsOption>(pieOptions) as EChartsOption const pieOptionsData = reactive<EChartsOption>(pieOptions) as EChartsOption
// //
let totalSate = reactive<WorkplaceTotal>({ let totalSate = reactive<WorkplaceTotal>({

View File

@ -63,8 +63,8 @@ import { underlineToHump } from '@/utils'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
import { useDesign } from '@/hooks/web/useDesign' import { useDesign } from '@/hooks/web/useDesign'
import { useAppStore } from '@/store/modules/app' import { useAppStore } from '@/store/modules/app'
import { ThemeSwitch } from '@/components/ThemeSwitch' import { ThemeSwitch } from '@/layout/components/ThemeSwitch'
import { LocaleDropdown } from '@/components/LocaleDropdown' import { LocaleDropdown } from '@/layout/components/LocaleDropdown'
import { LoginForm, MobileForm, RegisterForm, QrCodeForm } from './components' import { LoginForm, MobileForm, RegisterForm, QrCodeForm } from './components'
const { t } = useI18n() const { t } = useI18n()

View File

@ -16,7 +16,7 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="24" style="padding-left: 10px; padding-right: 10px"> <el-col :span="24" style="padding-left: 10px; padding-right: 10px">
<el-form-item prop="tenantName"> <el-form-item prop="tenantName" v-if="loginData.tenantEnable === 'true'">
<el-input <el-input
type="text" type="text"
v-model="loginData.loginForm.tenantName" v-model="loginData.loginForm.tenantName"
@ -65,9 +65,13 @@
</el-col> </el-col>
<el-col :span="24" style="padding-left: 10px; padding-right: 10px"> <el-col :span="24" style="padding-left: 10px; padding-right: 10px">
<el-form-item> <el-form-item>
<el-button :loading="loginLoading" type="primary" class="w-[100%]" @click="getCode()"> <XButton
{{ t('login.login') }} :loading="loginLoading"
</el-button> type="primary"
class="w-[100%]"
:title="t('login.login')"
@click="getCode()"
/>
</el-form-item> </el-form-item>
</el-col> </el-col>
<Verify <Verify
@ -81,19 +85,25 @@
<el-form-item> <el-form-item>
<el-row justify="space-between" style="width: 100%" :gutter="5"> <el-row justify="space-between" style="width: 100%" :gutter="5">
<el-col :span="8"> <el-col :span="8">
<el-button class="w-[100%]" @click="setLoginState(LoginStateEnum.MOBILE)"> <XButton
{{ t('login.btnMobile') }} class="w-[100%]"
</el-button> :title="t('login.btnMobile')"
@click="setLoginState(LoginStateEnum.MOBILE)"
/>
</el-col> </el-col>
<el-col :span="8"> <el-col :span="8">
<el-button class="w-[100%]" @click="setLoginState(LoginStateEnum.QR_CODE)"> <XButton
{{ t('login.btnQRCode') }} class="w-[100%]"
</el-button> :title="t('login.btnQRCode')"
@click="setLoginState(LoginStateEnum.QR_CODE)"
/>
</el-col> </el-col>
<el-col :span="8"> <el-col :span="8">
<el-button class="w-[100%]" @click="setLoginState(LoginStateEnum.REGISTER)"> <XButton
{{ t('login.btnRegister') }} class="w-[100%]"
</el-button> :title="t('login.btnRegister')"
@click="setLoginState(LoginStateEnum.REGISTER)"
/>
</el-col> </el-col>
</el-row> </el-row>
</el-form-item> </el-form-item>
@ -103,32 +113,13 @@
<el-form-item> <el-form-item>
<div class="flex justify-between w-[100%]"> <div class="flex justify-between w-[100%]">
<Icon <Icon
icon="ant-design:github-filled" v-for="(item, key) in socialList"
:key="key"
:icon="item.icon"
:size="30" :size="30"
class="cursor-pointer anticon" class="cursor-pointer anticon"
color="#999" color="#999"
@click="doSocialLogin('github')" @click="doSocialLogin(item.type)"
/>
<Icon
icon="ant-design:wechat-filled"
:size="30"
class="cursor-pointer anticon"
color="#999"
@click="doSocialLogin('wechat')"
/>
<Icon
icon="ant-design:alipay-circle-filled"
:size="30"
color="#999"
class="cursor-pointer anticon"
@click="doSocialLogin('alipay')"
/>
<Icon
icon="ant-design:dingtalk-circle-filled"
:size="30"
color="#999"
class="cursor-pointer anticon"
@click="doSocialLogin('dingtalk')"
/> />
</div> </div>
</el-form-item> </el-form-item>
@ -150,21 +141,24 @@ import {
ElDivider, ElDivider,
ElLoading ElLoading
} from 'element-plus' } from 'element-plus'
import Cookies from 'js-cookie'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import type { RouteLocationNormalizedLoaded } from 'vue-router' import type { RouteLocationNormalizedLoaded } from 'vue-router'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
import { useIcon } from '@/hooks/web/useIcon' import { useIcon } from '@/hooks/web/useIcon'
import { useMessage } from '@/hooks/web/useMessage'
import { required } from '@/utils/formRules' import { required } from '@/utils/formRules'
import { setToken, setTenantId } from '@/utils/auth' import * as authUtil from '@/utils/auth'
import { decrypt, encrypt } from '@/utils/jsencrypt' import { decrypt } from '@/utils/jsencrypt'
import { Icon } from '@/components/Icon'
import { Verify } from '@/components/Verifition' import { Verify } from '@/components/Verifition'
import { usePermissionStore } from '@/store/modules/permission' import { usePermissionStore } from '@/store/modules/permission'
import * as LoginApi from '@/api/login' import * as LoginApi from '@/api/login'
import { LoginStateEnum, useLoginState, useFormValid } from './useLogin' import { LoginStateEnum, useLoginState, useFormValid } from './useLogin'
const { t } = useI18n() const { t } = useI18n()
const message = useMessage()
const iconHouse = useIcon({ icon: 'ep:house' })
const iconAvatar = useIcon({ icon: 'ep:avatar' })
const iconLock = useIcon({ icon: 'ep:lock' })
const formLogin = ref() const formLogin = ref()
const { validForm } = useFormValid(formLogin) const { validForm } = useFormValid(formLogin)
const { setLoginState, getLoginState } = useLoginState() const { setLoginState, getLoginState } = useLoginState()
@ -172,9 +166,6 @@ const { currentRoute, push } = useRouter()
const permissionStore = usePermissionStore() const permissionStore = usePermissionStore()
const redirect = ref<string>('') const redirect = ref<string>('')
const loginLoading = ref(false) const loginLoading = ref(false)
const iconHouse = useIcon({ icon: 'ep:house' })
const iconAvatar = useIcon({ icon: 'ep:avatar' })
const iconLock = useIcon({ icon: 'ep:lock' })
const verify = ref() const verify = ref()
const captchaType = ref('blockPuzzle') // blockPuzzle clickWord const captchaType = ref('blockPuzzle') // blockPuzzle clickWord
@ -194,16 +185,33 @@ const loginData = reactive({
signIn: false signIn: false
}, },
loginForm: { loginForm: {
tenantName: Cookies.get('tenantName') ? Cookies.get('tenantName') : '芋道源码', tenantName: '芋道源码',
username: Cookies.get('username') ? Cookies.get('username') : 'admin', username: 'admin',
password: Cookies.get('password') password: 'admin123',
? (decrypt(Cookies.get('password')) as unknown as string)
: 'admin123',
captchaVerification: '', captchaVerification: '',
rememberMe: false rememberMe: false
} }
}) })
const socialList = [
{
icon: 'ant-design:github-filled',
type: 0
},
{
icon: 'ant-design:wechat-filled',
type: 30
},
{
icon: 'ant-design:alipay-circle-filled',
type: 0
},
{
icon: 'ant-design:dingtalk-circle-filled',
type: 20
}
]
// //
const getCode = async () => { const getCode = async () => {
// //
@ -217,17 +225,19 @@ const getCode = async () => {
} }
//ID //ID
const getTenantId = async () => { const getTenantId = async () => {
if (loginData.tenantEnable === 'true') {
const res = await LoginApi.getTenantIdByNameApi(loginData.loginForm.tenantName) const res = await LoginApi.getTenantIdByNameApi(loginData.loginForm.tenantName)
setTenantId(res) authUtil.setTenantId(res)
}
} }
// //
const getCookie = () => { const getCookie = () => {
const username = Cookies.get('username') const username = authUtil.getUsername()
const password = Cookies.get('password') const password = authUtil.getPassword()
? (decrypt(Cookies.get('password')) as unknown as string) ? decrypt(authUtil.getPassword() as unknown as string)
: undefined : undefined
const rememberMe = Cookies.get('rememberMe') const rememberMe = authUtil.getRememberMe()
const tenantName = Cookies.get('tenantName') const tenantName = authUtil.getTenantName()
loginData.loginForm = { loginData.loginForm = {
...loginData.loginForm, ...loginData.loginForm,
username: username ? username : loginData.loginForm.username, username: username ? username : loginData.loginForm.username,
@ -256,19 +266,17 @@ const handleLogin = async (params) => {
background: 'rgba(0, 0, 0, 0.7)' background: 'rgba(0, 0, 0, 0.7)'
}) })
if (loginData.loginForm.rememberMe) { if (loginData.loginForm.rememberMe) {
Cookies.set('username', loginData.loginForm.username, { expires: 30 }) authUtil.setUsername(loginData.loginForm.username)
Cookies.set('password', encrypt(loginData.loginForm.password as unknown as string), { authUtil.setPassword(loginData.loginForm.password)
expires: 30 authUtil.setRememberMe(loginData.loginForm.rememberMe)
}) authUtil.setTenantName(loginData.loginForm.tenantName)
Cookies.set('rememberMe', loginData.loginForm.rememberMe, { expires: 30 })
Cookies.set('tenantName', loginData.loginForm.tenantName, { expires: 30 })
} else { } else {
Cookies.remove('username') authUtil.removeUsername()
Cookies.remove('password') authUtil.removePassword()
Cookies.remove('rememberMe') authUtil.removeRememberMe()
Cookies.remove('tenantName') authUtil.removeTenantName()
} }
setToken(res) authUtil.setToken(res)
if (!redirect.value) { if (!redirect.value) {
redirect.value = '/' redirect.value = '/'
} }
@ -284,8 +292,17 @@ const handleLogin = async (params) => {
} }
// //
const doSocialLogin = async (type: string) => { const doSocialLogin = async (type: number) => {
if (type === 0) {
message.error('此方式未配置')
} else {
loginLoading.value = true loginLoading.value = true
if (loginData.tenantEnable === 'true') {
await message.prompt('请输入租户名称', t('common.reminder')).then(async ({ value }) => {
const res = await LoginApi.getTenantIdByNameApi(value)
authUtil.setTenantId(res)
})
}
// redirectUri // redirectUri
const redirectUri = const redirectUri =
location.origin + '/social-login?type=' + type + '&redirect=' + (redirect.value || '/') location.origin + '/social-login?type=' + type + '&redirect=' + (redirect.value || '/')
@ -293,6 +310,7 @@ const doSocialLogin = async (type: string) => {
const res = await LoginApi.socialAuthRedirectApi(type, encodeURIComponent(redirectUri)) const res = await LoginApi.socialAuthRedirectApi(type, encodeURIComponent(redirectUri))
window.location.href = res window.location.href = res
} }
}
watch( watch(
() => currentRoute.value, () => currentRoute.value,
(route: RouteLocationNormalizedLoaded) => { (route: RouteLocationNormalizedLoaded) => {

View File

@ -17,7 +17,7 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="24" style="padding-left: 10px; padding-right: 10px"> <el-col :span="24" style="padding-left: 10px; padding-right: 10px">
<el-form-item prop="tenantName"> <el-form-item prop="tenantName" v-if="loginData.tenantEnable === 'true'">
<el-input <el-input
type="text" type="text"
v-model="loginData.loginForm.tenantName" v-model="loginData.loginForm.tenantName"
@ -69,16 +69,23 @@
<!-- 登录按钮 / 返回按钮 --> <!-- 登录按钮 / 返回按钮 -->
<el-col :span="24" style="padding-left: 10px; padding-right: 10px"> <el-col :span="24" style="padding-left: 10px; padding-right: 10px">
<el-form-item> <el-form-item>
<el-button :loading="loginLoading" type="primary" class="w-[100%]" @click="signIn"> <XButton
{{ t('login.login') }} :loading="loginLoading"
</el-button> type="primary"
class="w-[100%]"
:title="t('login.login')"
@click="signIn()"
/>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="24" style="padding-left: 10px; padding-right: 10px"> <el-col :span="24" style="padding-left: 10px; padding-right: 10px">
<el-form-item> <el-form-item>
<el-button :loading="loginLoading" class="w-[100%]" @click="handleBackLogin"> <XButton
{{ t('login.backLogin') }} :loading="loginLoading"
</el-button> class="w-[100%]"
:title="t('login.backLogin')"
@click="handleBackLogin()"
/>
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
@ -91,17 +98,15 @@ import { useRouter } from 'vue-router'
import type { RouteLocationNormalizedLoaded } from 'vue-router' import type { RouteLocationNormalizedLoaded } from 'vue-router'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
import { useIcon } from '@/hooks/web/useIcon' import { useIcon } from '@/hooks/web/useIcon'
import { useCache } from '@/hooks/web/useCache'
import { useMessage } from '@/hooks/web/useMessage' import { useMessage } from '@/hooks/web/useMessage'
import { setToken } from '@/utils/auth'
import { required } from '@/utils/formRules' import { required } from '@/utils/formRules'
import { setTenantId, setToken } from '@/utils/auth'
import { usePermissionStore } from '@/store/modules/permission' import { usePermissionStore } from '@/store/modules/permission'
import { getTenantIdByNameApi, sendSmsCodeApi, smsLoginApi } from '@/api/login' import { getTenantIdByNameApi, sendSmsCodeApi, smsLoginApi } from '@/api/login'
import LoginFormTitle from './LoginFormTitle.vue' import LoginFormTitle from './LoginFormTitle.vue'
import { useLoginState, LoginStateEnum, useFormValid } from './useLogin' import { useLoginState, LoginStateEnum, useFormValid } from './useLogin'
const { t } = useI18n() const { t } = useI18n()
const { wsCache } = useCache()
const message = useMessage() const message = useMessage()
const permissionStore = usePermissionStore() const permissionStore = usePermissionStore()
const { currentRoute, push } = useRouter() const { currentRoute, push } = useRouter()
@ -121,7 +126,7 @@ const rules = {
} }
const loginData = reactive({ const loginData = reactive({
codeImg: '', codeImg: '',
tenantEnable: true, tenantEnable: import.meta.env.VITE_APP_TENANT_ENABLE,
token: '', token: '',
loading: { loading: {
signIn: false signIn: false
@ -171,8 +176,10 @@ watch(
) )
// ID // ID
const getTenantId = async () => { const getTenantId = async () => {
if (loginData.tenantEnable === 'true') {
const res = await getTenantIdByNameApi(loginData.loginForm.tenantName) const res = await getTenantIdByNameApi(loginData.loginForm.tenantName)
wsCache.set('tenantId', res) setTenantId(res)
}
} }
// //
const signIn = async () => { const signIn = async () => {

View File

@ -11,9 +11,7 @@
<el-divider class="enter-x">{{ t('login.qrcode') }}</el-divider> <el-divider class="enter-x">{{ t('login.qrcode') }}</el-divider>
<el-col :span="24" style="padding-left: 10px; padding-right: 10px"> <el-col :span="24" style="padding-left: 10px; padding-right: 10px">
<div class="w-[100%] mt-15px"> <div class="w-[100%] mt-15px">
<el-button class="w-[100%]" @click="handleBackLogin"> <XButton class="w-[100%]" :title="t('login.backLogin')" @click="handleBackLogin()" />
{{ t('sys.login.backSignIn') }}
</el-button>
</div> </div>
</el-col> </el-col>
</el-row> </el-row>
@ -21,11 +19,11 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, unref } from 'vue' import { computed, unref } from 'vue'
import { ElRow, ElCol, ElCard, ElDivider } from 'element-plus' import { ElRow, ElCol, ElCard, ElDivider } from 'element-plus'
import { useI18n } from '@/hooks/web/useI18n'
import { useLoginState, LoginStateEnum } from './useLogin'
import LoginFormTitle from './LoginFormTitle.vue'
import { Qrcode } from '@/components/Qrcode' import { Qrcode } from '@/components/Qrcode'
import logoImg from '@/assets/imgs/logo.png' import logoImg from '@/assets/imgs/logo.png'
import { useI18n } from '@/hooks/web/useI18n'
import LoginFormTitle from './LoginFormTitle.vue'
import { useLoginState, LoginStateEnum } from './useLogin'
const { t } = useI18n() const { t } = useI18n()
const { handleBackLogin, getLoginState } = useLoginState() const { handleBackLogin, getLoginState } = useLoginState()

View File

@ -21,14 +21,16 @@
<template #register> <template #register>
<div class="w-[100%]"> <div class="w-[100%]">
<el-button type="primary" class="w-[100%]" :loading="loading" @click="loginRegister"> <XButton
{{ t('login.register') }} :loading="loading"
</el-button> type="primary"
class="w-[100%]"
:title="t('login.register')"
@click="loginRegister()"
/>
</div> </div>
<div class="w-[100%] mt-15px"> <div class="w-[100%] mt-15px">
<el-button class="w-[100%]" @click="handleBackLogin"> <XButton class="w-[100%]" :title="t('login.hasUser')" @click="handleBackLogin()" />
{{ t('login.hasUser') }}
</el-button>
</div> </div>
</template> </template>
</Form> </Form>
@ -40,8 +42,8 @@ import { Form } from '@/components/Form'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
import { useForm } from '@/hooks/web/useForm' import { useForm } from '@/hooks/web/useForm'
import { useValidator } from '@/hooks/web/useValidator' import { useValidator } from '@/hooks/web/useValidator'
import { useLoginState, LoginStateEnum } from './useLogin'
import LoginFormTitle from './LoginFormTitle.vue' import LoginFormTitle from './LoginFormTitle.vue'
import { useLoginState, LoginStateEnum } from './useLogin'
import { FormSchema } from '@/types/form' import { FormSchema } from '@/types/form'
const { t } = useI18n() const { t } = useI18n()

View File

@ -1,7 +1,7 @@
<template> <template>
<Form ref="formRef" :rules="rules" :schema="schema" :labelWidth="80"> <Form ref="formRef" :rules="rules" :schema="schema" :labelWidth="80">
<template #sex> <template #sex="form">
<el-radio-group v-model="sexVlue"> <el-radio-group v-model="form['sex']">
<el-radio :label="1">{{ t('profile.user.man') }}</el-radio> <el-radio :label="1">{{ t('profile.user.man') }}</el-radio>
<el-radio :label="2">{{ t('profile.user.woman') }}</el-radio> <el-radio :label="2">{{ t('profile.user.woman') }}</el-radio>
</el-radio-group> </el-radio-group>
@ -67,7 +67,6 @@ const schema = reactive<FormSchema[]>([
value: 0 value: 0
} }
]) ])
const sexVlue = ref<number>()
const formRef = ref<FormExpose>() // Ref const formRef = ref<FormExpose>() // Ref
const submit = () => { const submit = () => {
const elForm = unref(formRef)?.getElFormRef() const elForm = unref(formRef)?.getElFormRef()
@ -75,7 +74,6 @@ const submit = () => {
elForm.validate(async (valid) => { elForm.validate(async (valid) => {
if (valid) { if (valid) {
const data = unref(formRef)?.formModel as UserProfileUpdateReqVO const data = unref(formRef)?.formModel as UserProfileUpdateReqVO
data.sex = sexVlue.value as unknown as number
await updateUserProfileApi(data) await updateUserProfileApi(data)
ElMessage.success(t('common.updateSuccess')) ElMessage.success(t('common.updateSuccess'))
await init() await init()
@ -84,7 +82,6 @@ const submit = () => {
} }
const init = async () => { const init = async () => {
const res = await getUserProfileApi() const res = await getUserProfileApi()
sexVlue.value = res.sex
unref(formRef)?.setValues(res) unref(formRef)?.setValues(res)
} }
onMounted(async () => { onMounted(async () => {

View File

@ -73,12 +73,12 @@ const submitForm = async () => {
} }
// ========== ========== // ========== ==========
const detailRef = ref() // Ref const detailData = ref() // Ref
// //
const handleDetail = async (row: FormVO) => { const handleDetail = async (row: FormVO) => {
// //
detailRef.value = row detailData.value = row
setDialogTile('detail') setDialogTile('detail')
} }
@ -148,7 +148,7 @@ getList()
<Descriptions <Descriptions
v-if="actionType === 'detail'" v-if="actionType === 'detail'"
:schema="allSchemas.detailSchema" :schema="allSchemas.detailSchema"
:data="detailRef" :data="detailData"
/> />
<!-- 操作按钮 --> <!-- 操作按钮 -->
<template #footer> <template #footer>

Some files were not shown because too many files have changed in this diff Show More