!64 版本升级

* Merge branch 'dev' of gitee.com:JavaLionLi/plus-ui into ts
* 升级依赖
* !61 fix: 删除重复环境变量ElUploadInstance
* fix: 删除重复环境变量ElUploadInstance
This commit is contained in:
ahaos
2023-12-13 01:01:52 +00:00
parent 58d7e50de3
commit b06f6a316b
98 changed files with 3191 additions and 3151 deletions

View File

@ -14,4 +14,4 @@ dist
.eslintrc.js .eslintrc.js
prettier.config.js prettier.config.js
src/assets src/assets
tailwind.config.js tailwind.config.js

View File

@ -1,28 +1,35 @@
module.exports = { module.exports = {
env: { env: {
browser: true, browser: true,
es2021: true, node: true,
node: true es6: true
}, },
parser: 'vue-eslint-parser', parser: 'vue-eslint-parser',
extends: [ extends: [
'eslint:recommended', 'plugin:vue/vue3-recommended',
'plugin:vue/vue3-essential',
'plugin:@typescript-eslint/recommended',
'./.eslintrc-auto-import.json', './.eslintrc-auto-import.json',
'plugin:prettier/recommended' 'plugin:@typescript-eslint/recommended',
"prettier",
'plugin:prettier/recommended',
], ],
parserOptions: { parserOptions: {
ecmaVersion: '2020', ecmaVersion: '2020',
sourceType: 'module', sourceType: 'module',
project: "./tsconfig.*?.json",
parser: '@typescript-eslint/parser' parser: '@typescript-eslint/parser'
}, },
plugins: ['vue', '@typescript-eslint'], plugins: ['vue', '@typescript-eslint', 'import', 'promise', 'node', 'prettier'],
rules: { rules: {
'vue/multi-word-component-names': 'off',
'@typescript-eslint/no-empty-function': 'off', '@typescript-eslint/no-empty-function': 'off',
'@typescript-eslint/no-explicit-any': 'off', '@typescript-eslint/no-explicit-any': 'off',
// vue
'vue/multi-word-component-names': 'off',
'vue/valid-define-props': 'off',
'vue/no-v-model-argument': 'off', 'vue/no-v-model-argument': 'off',
'prefer-rest-params': 'off',
// prettier
'prettier/prettier': 'error',
'@typescript-eslint/ban-types': [ '@typescript-eslint/ban-types': [
'error', 'error',
{ {

20
.prettierrc Normal file
View File

@ -0,0 +1,20 @@
{
"printWidth": 150,
"tabWidth": 2,
"useTabs": false,
"semi": true,
"singleQuote": true,
"quoteProps": "as-needed",
"jsxSingleQuote": false,
"bracketSameLine": false,
"trailingComma": "none",
"bracketSpacing": true,
"embeddedLanguageFormatting": "auto",
"arrowParens": "always",
"requirePragma": false,
"insertPragma": false,
"proseWrap": "preserve",
"htmlWhitespaceSensitivity": "css",
"vueIndentScriptAndStyle": false,
"endOfLine": "auto"
}

View File

@ -6,9 +6,10 @@
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
"dev": "vite serve --mode development", "dev": "vite serve --mode development",
"build:prod": "vite build --mode production &&vue-tsc --noEmit", "build:prod": "vite build --mode production",
"build:dev": "vite build --mode development",
"preview": "vite preview", "preview": "vite preview",
"lint": "eslint src/**/*.{ts,js,vue} --fix", "lint:eslint": "eslint --fix --ext .ts,.js,.vue ./src ",
"prepare": "husky install", "prepare": "husky install",
"prettier": "prettier --write ." "prettier": "prettier --write ."
}, },
@ -19,28 +20,28 @@
"dependencies": { "dependencies": {
"@element-plus/icons-vue": "2.1.0", "@element-plus/icons-vue": "2.1.0",
"@vueup/vue-quill": "1.2.0", "@vueup/vue-quill": "1.2.0",
"@vueuse/core": "9.5.0", "@vueuse/core": "10.7.0",
"animate.css": "4.1.1", "animate.css": "4.1.1",
"await-to-js": "^3.0.0", "await-to-js": "^3.0.0",
"axios": "^1.3.4", "axios": "^1.3.4",
"crypto-js": "^4.1.1",
"echarts": "5.4.0", "echarts": "5.4.0",
"element-plus": "2.2.27", "element-plus": "2.4.3",
"file-saver": "2.0.5", "file-saver": "2.0.5",
"fuse.js": "6.6.2", "fuse.js": "6.6.2",
"js-cookie": "3.0.1", "js-cookie": "3.0.1",
"jsencrypt": "3.3.1", "jsencrypt": "3.3.1",
"crypto-js": "^4.1.1",
"nprogress": "0.2.0", "nprogress": "0.2.0",
"path-browserify": "1.0.1", "path-browserify": "1.0.1",
"path-to-regexp": "6.2.0", "path-to-regexp": "6.2.0",
"pinia": "2.0.22", "pinia": "2.1.7",
"screenfull": "6.0.0", "screenfull": "6.0.0",
"vform3-builds": "3.0.8", "vform3-builds": "3.0.8",
"vue": "3.2.45", "vue": "3.3.11",
"vue-cropper": "1.0.3", "vue-cropper": "1.0.3",
"vue-i18n": "9.2.2", "vue-i18n": "9.2.2",
"vue-router": "4.1.4", "vue-router": "4.2.5",
"vue-types": "^5.0.3" "vue-types": "5.1.1"
}, },
"devDependencies": { "devDependencies": {
"@iconify/json": "^2.2.40", "@iconify/json": "^2.2.40",
@ -51,34 +52,38 @@
"@types/node": "18.14.2", "@types/node": "18.14.2",
"@types/nprogress": "0.2.0", "@types/nprogress": "0.2.0",
"@types/path-browserify": "^1.0.0", "@types/path-browserify": "^1.0.0",
"@typescript-eslint/eslint-plugin": "5.56.0", "@typescript-eslint/eslint-plugin": "6.14.0",
"@typescript-eslint/parser": "5.56.0", "@typescript-eslint/parser": "6.14.0",
"@unocss/preset-attributify": "^0.50.6", "@unocss/preset-attributify": "^0.58.0",
"@unocss/preset-icons": "^0.50.6", "@unocss/preset-icons": "^0.58.0",
"@unocss/preset-uno": "^0.50.6", "@unocss/preset-uno": "^0.58.0",
"@vitejs/plugin-vue": "4.0.0",
"@vue/compiler-sfc": "3.2.45", "@vue/compiler-sfc": "3.2.45",
"@vitejs/plugin-vue": "4.5.2",
"autoprefixer": "10.4.14", "autoprefixer": "10.4.14",
"eslint": "8.36.0", "eslint": "8.55.0",
"eslint-config-prettier": "8.8.0", "eslint-config-prettier": "9.1.0",
"eslint-plugin-prettier": "4.2.1", "eslint-define-config": "2.0.0",
"eslint-plugin-vue": "9.9.0", "eslint-plugin-prettier": "5.0.1",
"eslint-plugin-promise": "6.1.1",
"eslint-plugin-node": "11.1.0",
"eslint-plugin-import": "2.29.0",
"eslint-plugin-vue": "9.19.2",
"fast-glob": "^3.2.11", "fast-glob": "^3.2.11",
"husky": "7.0.4", "husky": "7.0.4",
"postcss": "^8.4.21", "postcss": "^8.4.21",
"prettier": "2.8.6", "prettier": "3.1.1",
"sass": "1.56.1", "sass": "1.56.1",
"typescript": "4.9.5", "typescript": "5.2.2",
"unocss": "^0.50.6", "unocss": "^0.58.0",
"unplugin-auto-import": "0.13.0", "unplugin-auto-import": "0.17.2",
"unplugin-icons": "0.15.1", "unplugin-icons": "0.18.1",
"unplugin-vue-components": "0.23.0", "unplugin-vue-components": "0.26.0",
"vite": "4.3.1", "unplugin-vue-setup-extend-plus": "0.4.9",
"vite-plugin-compression": "0.5.1", "vite-plugin-compression": "0.5.1",
"vite-plugin-svg-icons": "2.0.1", "vite-plugin-svg-icons": "2.0.1",
"unplugin-vue-setup-extend-plus": "0.4.9",
"vitest": "^0.29.7", "vitest": "^0.29.7",
"vue-eslint-parser": "9.1.0", "vue-eslint-parser": "9.3.2",
"vue-tsc": "0.35.0" "vue-tsc": "0.35.0",
"vite": "5.0.4"
} }
} }

View File

@ -5,8 +5,8 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import useSettingsStore from '@/store/modules/settings' import useSettingsStore from '@/store/modules/settings';
import { handleThemeStyle } from '@/utils/theme' import { handleThemeStyle } from '@/utils/theme';
import useAppStore from '@/store/modules/app'; import useAppStore from '@/store/modules/app';
const appStore = useAppStore(); const appStore = useAppStore();
@ -15,7 +15,7 @@ const size = computed(() => appStore.size as any);
onMounted(() => { onMounted(() => {
nextTick(() => { nextTick(() => {
// 初始化主题样式 // 初始化主题样式
handleThemeStyle(useSettingsStore().theme) handleThemeStyle(useSettingsStore().theme);
}) });
}) });
</script> </script>

View File

@ -28,7 +28,7 @@ export const getGenTable = (tableId: string | number): AxiosPromise<GenTableVO>
}; };
// 修改代码生成信息 // 修改代码生成信息
export const updateGenTable = (data: DbTableForm) => { export const updateGenTable = (data: DbTableForm): AxiosPromise<GenTableVO> => {
return request({ return request({
url: '/tool/gen', url: '/tool/gen',
method: 'put', method: 'put',
@ -37,7 +37,7 @@ export const updateGenTable = (data: DbTableForm) => {
}; };
// 导入表 // 导入表
export const importTable = (data: { tables: string; dataName: string }) => { export const importTable = (data: { tables: string; dataName: string }): AxiosPromise<GenTableVO> => {
return request({ return request({
url: '/tool/gen/importTable', url: '/tool/gen/importTable',
method: 'post', method: 'post',

View File

@ -2,8 +2,7 @@
<el-breadcrumb class="app-breadcrumb" separator="/"> <el-breadcrumb class="app-breadcrumb" separator="/">
<transition-group name="breadcrumb"> <transition-group name="breadcrumb">
<el-breadcrumb-item v-for="(item, index) in levelList" :key="item.path"> <el-breadcrumb-item v-for="(item, index) in levelList" :key="item.path">
<span v-if="item.redirect === 'noRedirect' || index == levelList.length - 1" class="no-redirect">{{ <span v-if="item.redirect === 'noRedirect' || index == levelList.length - 1" class="no-redirect">{{ item.meta?.title }}</span>
item.meta?.title }}</span>
<a v-else @click.prevent="handleLink(item)">{{ item.meta?.title }}</a> <a v-else @click.prevent="handleLink(item)">{{ item.meta?.title }}</a>
</el-breadcrumb-item> </el-breadcrumb-item>
</transition-group> </transition-group>
@ -11,42 +10,42 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { RouteLocationMatched } from 'vue-router' import { RouteLocationMatched } from 'vue-router';
const route = useRoute(); const route = useRoute();
const router = useRouter(); const router = useRouter();
const levelList = ref<RouteLocationMatched[]>([]) const levelList = ref<RouteLocationMatched[]>([]);
const getBreadcrumb = () => { const getBreadcrumb = () => {
// only show routes with meta.title // only show routes with meta.title
let matched = route.matched.filter(item => item.meta && item.meta.title); let matched = route.matched.filter((item) => item.meta && item.meta.title);
const first = matched[0] const first = matched[0];
// 判断是否为首页 // 判断是否为首页
if (!isDashboard(first)) { if (!isDashboard(first)) {
matched = ([{ path: '/index', meta: { title: '首页' } }] as any).concat(matched) matched = ([{ path: '/index', meta: { title: '首页' } }] as any).concat(matched);
} }
levelList.value = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false) levelList.value = matched.filter((item) => item.meta && item.meta.title && item.meta.breadcrumb !== false);
} };
const isDashboard = (route: RouteLocationMatched) => { const isDashboard = (route: RouteLocationMatched) => {
const name = route && route.name as string const name = route && (route.name as string);
if (!name) { if (!name) {
return false return false;
} }
return name.trim() === 'Index' return name.trim() === 'Index';
} };
const handleLink = (item: RouteLocationMatched) => { const handleLink = (item: RouteLocationMatched) => {
const { redirect, path } = item const { redirect, path } = item;
redirect ? router.push(redirect as string) : router.push(path) redirect ? router.push(redirect as string) : router.push(path);
} };
watchEffect(() => { watchEffect(() => {
// if you go to the redirect page, do not update the breadcrumbs // if you go to the redirect page, do not update the breadcrumbs
if (route.path.startsWith('/redirect/')) return if (route.path.startsWith('/redirect/')) return;
getBreadcrumb() getBreadcrumb();
}) });
onMounted(() => { onMounted(() => {
getBreadcrumb(); getBreadcrumb();
}) });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -1,53 +1,50 @@
<!-- 代码构建 -->
<script setup lang="ts">
const props = defineProps({
showBtn: {
type: Boolean,
default: false
},
formJson: {
type: Object,
default: undefined
}
})
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const buildRef = ref();
const emits = defineEmits(['reJson', 'saveDesign']);
//获取表单json
const getJson = () => {
const formJson = JSON.stringify(buildRef.value.getFormJson())
const fieldJson = JSON.stringify(buildRef.value.getFieldWidgets())
let data = {
formJson, fieldJson
}
emits("saveDesign", data)
}
onMounted(() => {
if (props.formJson) {
buildRef.value.setFormJson(props.formJson)
}
})
</script>
<template> <template>
<!-- 代码构建 -->
<div> <div>
<v-form-designer <v-form-designer
class="build"
ref="buildRef" ref="buildRef"
class="build"
:designer-config="{ importJsonButton: true, exportJsonButton: true, exportCodeButton: true, generateSFCButton: true, formTemplates: true }" :designer-config="{ importJsonButton: true, exportJsonButton: true, exportCodeButton: true, generateSFCButton: true, formTemplates: true }"
> >
<template #customToolButtons v-if="showBtn"> <template v-if="showBtn" #customToolButtons>
<el-button link type="primary" icon="Select" @click="getJson">保存</el-button> <el-button link type="primary" icon="Select" @click="getJson">保存</el-button>
</template> </template>
</v-form-designer> </v-form-designer>
</div> </div>
</template> </template>
<script setup lang="ts">
interface Props {
showBtn: boolean;
formJson: any;
}
const props = withDefaults(defineProps<Props>(), {
showBtn: true,
formJson: ''
});
const buildRef = ref();
const emits = defineEmits(['reJson', 'saveDesign']);
//获取表单json
const getJson = () => {
const formJson = JSON.stringify(buildRef.value.getFormJson());
const fieldJson = JSON.stringify(buildRef.value.getFieldWidgets());
let data = {
formJson,
fieldJson
};
emits('saveDesign', data);
};
onMounted(() => {
if (props.formJson) {
buildRef.value.setFormJson(props.formJson);
}
});
</script>
<style lang="scss"> <style lang="scss">
.build { .build {
margin: 0 !important; margin: 0 !important;

View File

@ -1,26 +1,28 @@
<template>
<div class="">
<v-form-render ref="vFormRef" :form-json="formJson" :form-data="formData" />
</div>
</template>
<!-- 动态表单渲染 --> <!-- 动态表单渲染 -->
<script setup name="Render"> <script setup name="Render" lang="ts">
interface Props {
formJson: string | object;
formData: string | object;
isView: boolean;
}
const props = defineProps({ const props = withDefaults(defineProps<Props>(), {
formJson: { formJson: '',
type: [String, Object], formData: '',
default: "" isView: false
}, });
formData: {
type: [String, Object],
default: ""
},
isView: {
type: Boolean,
default: false
}
})
const vFormRef = ref(null) const vFormRef = ref(null);
// 获取表单数据-异步 // 获取表单数据-异步
const getFormData = () => { const getFormData = () => {
return vFormRef.value.getFormData() return vFormRef.value.getFormData();
} };
/** /**
* 设置表单内容 * 设置表单内容
@ -28,35 +30,28 @@ const getFormData = () => {
* formConfig{ formTemplate表单模板formData表单数据hiddenField需要隐藏的字段字符串集合disabledField需要禁用的自读字符串集合} * formConfig{ formTemplate表单模板formData表单数据hiddenField需要隐藏的字段字符串集合disabledField需要禁用的自读字符串集合}
*/ */
const initForm = (formConf) => { const initForm = (formConf) => {
const { formTemplate, formData, hiddenField, disabledField } = toRaw(formConf) const { formTemplate, formData, hiddenField, disabledField } = toRaw(formConf);
if (formTemplate) { if (formTemplate) {
vFormRef.value.setFormJson(formTemplate) vFormRef.value.setFormJson(formTemplate);
if (formData) { if (formData) {
vFormRef.value.setFormData(formData) vFormRef.value.setFormData(formData);
} }
if (disabledField && disabledField.length > 0) { if (disabledField && disabledField.length > 0) {
setTimeout(() => { setTimeout(() => {
vFormRef.value.disableWidgets(disabledField) vFormRef.value.disableWidgets(disabledField);
}, 200) }, 200);
} }
if (hiddenField && hiddenField.length > 0) { if (hiddenField && hiddenField.length > 0) {
setTimeout(() => { setTimeout(() => {
vFormRef.value.hideWidgets(hiddenField) vFormRef.value.hideWidgets(hiddenField);
}, 200) }, 200);
} }
if (props.isView) { if (props.isView) {
console.log(props.isView)
setTimeout(() => { setTimeout(() => {
vFormRef.value.disableForm() vFormRef.value.disableForm();
}, 100) }, 100);
} }
} }
} };
defineExpose({ getFormData, initForm }) defineExpose({ getFormData, initForm });
</script> </script>
<template>
<div class="">
<v-form-render ref="vFormRef" :form-json="formJson" :form-data="formData" />
</div>
</template>

View File

@ -8,13 +8,13 @@
</span> </span>
<el-tag <el-tag
v-else v-else
:disable-transitions="true"
:key="item.value + ''" :key="item.value + ''"
:disable-transitions="true"
:index="index" :index="index"
:type="(item.elTagType === 'primary' || item.elTagType === 'default')? '' : item.elTagType" :type="(item.elTagType === 'primary' || item.elTagType === 'default')? '' : item.elTagType"
:class="item.elTagClass" :class="item.elTagClass"
> >
{{ item.label + " " }} {{ item.label + ' ' }}
</el-tag> </el-tag>
</template> </template>
</template> </template>
@ -25,57 +25,53 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { propTypes } from '@/utils/propTypes'; interface Props {
options: Array<DictDataOption>;
value: number | string | Array<number | string>;
const props = defineProps({ showValue: boolean;
// 数据 separator: string;
options: { }
type: Array as PropType<DictDataOption[]>, const props = withDefaults(defineProps<Props>(), {
default: null, showValue: true,
}, separator: ','
// 当前的值
value: [Number, String, Array] as PropType<number | string | Array<number | string>>,
// 当未找到匹配的数据时显示value
showValue: propTypes.bool.def(true),
separator: propTypes.string.def(","),
}); });
const values = computed(() => { const values = computed(() => {
if (props.value === '' || props.value === null || typeof props.value === "undefined") return [] if (props.value === '' || props.value === null || typeof props.value === 'undefined') return [];
return Array.isArray(props.value) ? props.value.map(item => '' + item) : String(props.value).split(props.separator); return Array.isArray(props.value) ? props.value.map((item) => '' + item) : String(props.value).split(props.separator);
}); });
const unmatch = computed(() => { const unmatch = computed(() => {
if (props.options?.length == 0 || props.value === '' || props.value === null || typeof props.value === "undefined") return false if (props.options?.length == 0 || props.value === '' || props.value === null || typeof props.value === 'undefined') return false;
// 传入值为非数组 // 传入值为非数组
values.value.forEach(item => { values.value.forEach((item) => {
if (!props.options.some(v => v.value === item)) { if (!props.options.some((v) => v.value === item)) {
return true // 如果有未匹配项将标志设置为true return true; // 如果有未匹配项将标志设置为true
} }
}) });
return false // 返回标志的值 return false; // 返回标志的值
}); });
const unmatchArray = computed(() => { const unmatchArray = computed(() => {
// 记录未匹配的项 // 记录未匹配的项
const itemUnmatchArray: Array<string | number> = []; const itemUnmatchArray: Array<string | number> = [];
if (props.value !== '' && props.value !== null && typeof props.value !== "undefined") { if (props.value !== '' && props.value !== null && typeof props.value !== 'undefined') {
values.value.forEach(item => { values.value.forEach((item) => {
if (!props.options.some(v => v.value === item)) { if (!props.options.some((v) => v.value === item)) {
itemUnmatchArray.push(item); itemUnmatchArray.push(item);
} }
}) });
} }
// 没有value不显示 // 没有value不显示
return handleArray(itemUnmatchArray); return handleArray(itemUnmatchArray);
}); });
const handleArray = (array: Array<string | number>) => { const handleArray = (array: Array<string | number>) => {
if (array.length === 0) return ""; if (array.length === 0) return '';
return array.reduce((pre, cur) => { return array.reduce((pre, cur) => {
return pre + " " + cur; return pre + ' ' + cur;
}); });
} };
</script> </script>
<style scoped> <style scoped>

View File

@ -1,6 +1,8 @@
<template> <template>
<div> <div>
<el-upload <el-upload
v-if="type === 'url'"
ref="uploadRef"
:action="upload.url" :action="upload.url"
:before-upload="handleBeforeUpload" :before-upload="handleBeforeUpload"
:on-success="handleUploadSuccess" :on-success="handleUploadSuccess"
@ -9,18 +11,16 @@
name="file" name="file"
:show-file-list="false" :show-file-list="false"
:headers="upload.headers" :headers="upload.headers"
ref="uploadRef"
v-if="type === 'url'"
> >
</el-upload> </el-upload>
<div class="editor"> <div class="editor">
<quill-editor <quill-editor
ref="quillEditorRef" ref="quillEditorRef"
v-model:content="content" v-model:content="content"
contentType="html" content-type="html"
@textChange="(e: any) => $emit('update:modelValue', content)"
:options="options" :options="options"
:style="styles" :style="styles"
@text-change="(e: any) => $emit('update:modelValue', content)"
/> />
</div> </div>
</div> </div>
@ -30,7 +30,7 @@
import { QuillEditor, Quill } from '@vueup/vue-quill'; import { QuillEditor, Quill } from '@vueup/vue-quill';
import '@vueup/vue-quill/dist/vue-quill.snow.css'; import '@vueup/vue-quill/dist/vue-quill.snow.css';
import { propTypes } from '@/utils/propTypes'; import { propTypes } from '@/utils/propTypes';
import { globalHeaders } from "@/utils/request"; import { globalHeaders } from '@/utils/request';
const props = defineProps({ const props = defineProps({
/* 编辑器的内容 */ /* 编辑器的内容 */
@ -52,42 +52,42 @@ const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const upload = reactive<UploadOption>({ const upload = reactive<UploadOption>({
headers: globalHeaders(), headers: globalHeaders(),
url: import.meta.env.VITE_APP_BASE_API + '/resource/oss/upload' url: import.meta.env.VITE_APP_BASE_API + '/resource/oss/upload'
}) });
const quillEditorRef = ref(); const quillEditorRef = ref();
const options = ref({ const options = ref({
theme: "snow", theme: 'snow',
bounds: document.body, bounds: document.body,
debug: "warn", debug: 'warn',
modules: { modules: {
// 工具栏配置 // 工具栏配置
toolbar: { toolbar: {
container: [ container: [
["bold", "italic", "underline", "strike"], // 加粗 斜体 下划线 删除线 ['bold', 'italic', 'underline', 'strike'], // 加粗 斜体 下划线 删除线
["blockquote", "code-block"], // 引用 代码块 ['blockquote', 'code-block'], // 引用 代码块
[{ list: "ordered" }, { list: "bullet" }], // 有序、无序列表 [{ list: 'ordered' }, { list: 'bullet' }], // 有序、无序列表
[{ indent: "-1" }, { indent: "+1" }], // 缩进 [{ indent: '-1' }, { indent: '+1' }], // 缩进
[{ size: ["small", false, "large", "huge"] }], // 字体大小 [{ size: ['small', false, 'large', 'huge'] }], // 字体大小
[{ header: [1, 2, 3, 4, 5, 6, false] }], // 标题 [{ header: [1, 2, 3, 4, 5, 6, false] }], // 标题
[{ color: [] }, { background: [] }], // 字体颜色、字体背景颜色 [{ color: [] }, { background: [] }], // 字体颜色、字体背景颜色
[{ align: [] }], // 对齐方式 [{ align: [] }], // 对齐方式
["clean"], // 清除文本格式 ['clean'], // 清除文本格式
["link", "image", "video"] // 链接、图片、视频 ['link', 'image', 'video'] // 链接、图片、视频
], ],
handlers: { handlers: {
image: function (value: any) { image: function (value: any) {
if (value) { if (value) {
// 调用element图片上传 // 调用element图片上传
(document.querySelector(".editor-img-uploader>.el-upload") as HTMLDivElement)?.click(); (document.querySelector('.editor-img-uploader>.el-upload') as HTMLDivElement)?.click();
} else { } else {
Quill.format("image", true); Quill.format('image', true);
} }
}, }
}, }
} }
}, },
placeholder: "请输入内容", placeholder: '请输入内容',
readOnly: props.readOnly, readOnly: props.readOnly
}); });
const styles = computed(() => { const styles = computed(() => {
@ -99,14 +99,18 @@ const styles = computed(() => {
style.height = `${props.height}px`; style.height = `${props.height}px`;
} }
return style; return style;
}) });
const content = ref(""); const content = ref('');
watch(() => props.modelValue, (v) => { watch(
if (v !== content.value) { () => props.modelValue,
content.value = v === undefined ? "<p></p>" : v; (v) => {
} if (v !== content.value) {
}, { immediate: true }); content.value = v === undefined ? '<p></p>' : v;
}
},
{ immediate: true }
);
// 图片上传成功返回图片地址 // 图片上传成功返回图片地址
const handleUploadSuccess = (res: any) => { const handleUploadSuccess = (res: any) => {
@ -117,7 +121,7 @@ const handleUploadSuccess = (res: any) => {
// 获取光标位置 // 获取光标位置
let length = quill.selection.savedRange.index; let length = quill.selection.savedRange.index;
// 插入图片res为服务器返回的图片链接地址 // 插入图片res为服务器返回的图片链接地址
quill.insertEmbed(length, "image", res.data.url); quill.insertEmbed(length, 'image', res.data.url);
// 调整光标到最后 // 调整光标到最后
quill.setSelection(length + 1); quill.setSelection(length + 1);
proxy?.$modal.closeLoading(); proxy?.$modal.closeLoading();
@ -125,11 +129,11 @@ const handleUploadSuccess = (res: any) => {
proxy?.$modal.loading(res.msg); proxy?.$modal.loading(res.msg);
proxy?.$modal.closeLoading(); proxy?.$modal.closeLoading();
} }
} };
// 图片上传前拦截 // 图片上传前拦截
const handleBeforeUpload = (file: any) => { const handleBeforeUpload = (file: any) => {
const type = ["image/jpeg", "image/jpg", "image/png", "image/svg"]; const type = ['image/jpeg', 'image/jpg', 'image/png', 'image/svg'];
const isJPG = type.includes(file.type); const isJPG = type.includes(file.type);
//检验文件格式 //检验文件格式
if (!isJPG) { if (!isJPG) {
@ -146,13 +150,13 @@ const handleBeforeUpload = (file: any) => {
} }
proxy?.$modal.loading('正在上传文件,请稍候...'); proxy?.$modal.loading('正在上传文件,请稍候...');
return true; return true;
} };
// 图片失败拦截 // 图片失败拦截
const handleUploadError = (err: any) => { const handleUploadError = (err: any) => {
console.error(err); console.error(err);
proxy?.$modal.msgError('上传文件失败'); proxy?.$modal.msgError('上传文件失败');
} };
</script> </script>
<style> <style>
@ -167,71 +171,71 @@ const handleUploadError = (err: any) => {
.quill-img { .quill-img {
display: none; display: none;
} }
.ql-snow .ql-tooltip[data-mode="link"]::before { .ql-snow .ql-tooltip[data-mode='link']::before {
content: "请输入链接地址:"; content: '请输入链接地址:';
} }
.ql-snow .ql-tooltip.ql-editing a.ql-action::after { .ql-snow .ql-tooltip.ql-editing a.ql-action::after {
border-right: 0; border-right: 0;
content: "保存"; content: '保存';
padding-right: 0; padding-right: 0;
} }
.ql-snow .ql-tooltip[data-mode="video"]::before { .ql-snow .ql-tooltip[data-mode='video']::before {
content: "请输入视频地址:"; content: '请输入视频地址:';
} }
.ql-snow .ql-picker.ql-size .ql-picker-label::before, .ql-snow .ql-picker.ql-size .ql-picker-label::before,
.ql-snow .ql-picker.ql-size .ql-picker-item::before { .ql-snow .ql-picker.ql-size .ql-picker-item::before {
content: "14px"; content: '14px';
} }
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="small"]::before, .ql-snow .ql-picker.ql-size .ql-picker-label[data-value='small']::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="small"]::before { .ql-snow .ql-picker.ql-size .ql-picker-item[data-value='small']::before {
content: "10px"; content: '10px';
} }
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="large"]::before, .ql-snow .ql-picker.ql-size .ql-picker-label[data-value='large']::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="large"]::before { .ql-snow .ql-picker.ql-size .ql-picker-item[data-value='large']::before {
content: "18px"; content: '18px';
} }
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="huge"]::before, .ql-snow .ql-picker.ql-size .ql-picker-label[data-value='huge']::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="huge"]::before { .ql-snow .ql-picker.ql-size .ql-picker-item[data-value='huge']::before {
content: "32px"; content: '32px';
} }
.ql-snow .ql-picker.ql-header .ql-picker-label::before, .ql-snow .ql-picker.ql-header .ql-picker-label::before,
.ql-snow .ql-picker.ql-header .ql-picker-item::before { .ql-snow .ql-picker.ql-header .ql-picker-item::before {
content: "文本"; content: '文本';
} }
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before, .ql-snow .ql-picker.ql-header .ql-picker-label[data-value='1']::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before { .ql-snow .ql-picker.ql-header .ql-picker-item[data-value='1']::before {
content: "标题1"; content: '标题1';
} }
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before, .ql-snow .ql-picker.ql-header .ql-picker-label[data-value='2']::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before { .ql-snow .ql-picker.ql-header .ql-picker-item[data-value='2']::before {
content: "标题2"; content: '标题2';
} }
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before, .ql-snow .ql-picker.ql-header .ql-picker-label[data-value='3']::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before { .ql-snow .ql-picker.ql-header .ql-picker-item[data-value='3']::before {
content: "标题3"; content: '标题3';
} }
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"]::before, .ql-snow .ql-picker.ql-header .ql-picker-label[data-value='4']::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before { .ql-snow .ql-picker.ql-header .ql-picker-item[data-value='4']::before {
content: "标题4"; content: '标题4';
} }
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"]::before, .ql-snow .ql-picker.ql-header .ql-picker-label[data-value='5']::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before { .ql-snow .ql-picker.ql-header .ql-picker-item[data-value='5']::before {
content: "标题5"; content: '标题5';
} }
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"]::before, .ql-snow .ql-picker.ql-header .ql-picker-label[data-value='6']::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before { .ql-snow .ql-picker.ql-header .ql-picker-item[data-value='6']::before {
content: "标题6"; content: '标题6';
} }
.ql-snow .ql-picker.ql-font .ql-picker-label::before, .ql-snow .ql-picker.ql-font .ql-picker-label::before,
.ql-snow .ql-picker.ql-font .ql-picker-item::before { .ql-snow .ql-picker.ql-font .ql-picker-item::before {
content: "标准字体"; content: '标准字体';
} }
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value="serif"]::before, .ql-snow .ql-picker.ql-font .ql-picker-label[data-value='serif']::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value="serif"]::before { .ql-snow .ql-picker.ql-font .ql-picker-item[data-value='serif']::before {
content: "衬线字体"; content: '衬线字体';
} }
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value="monospace"]::before, .ql-snow .ql-picker.ql-font .ql-picker-label[data-value='monospace']::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value="monospace"]::before { .ql-snow .ql-picker.ql-font .ql-picker-item[data-value='monospace']::before {
content: "等宽字体"; content: '等宽字体';
} }
</style> </style>

View File

@ -1,6 +1,7 @@
<template> <template>
<div class="upload-file"> <div class="upload-file">
<el-upload <el-upload
ref="fileUploadRef"
multiple multiple
:action="uploadFileUrl" :action="uploadFileUrl"
:before-upload="handleBeforeUpload" :before-upload="handleBeforeUpload"
@ -12,30 +13,29 @@
:show-file-list="false" :show-file-list="false"
:headers="headers" :headers="headers"
class="upload-file-uploader" class="upload-file-uploader"
ref="fileUploadRef"
> >
<!-- 上传按钮 --> <!-- 上传按钮 -->
<el-button type="primary">选取文件</el-button> <el-button type="primary">选取文件</el-button>
</el-upload> </el-upload>
<!-- 上传提示 --> <!-- 上传提示 -->
<div class="el-upload__tip" v-if="showTip"> <div v-if="showTip" class="el-upload__tip">
请上传 请上传
<template v-if="fileSize"> <template v-if="fileSize">
大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b> 大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b>
</template> </template>
<template v-if="fileType"> <template v-if="fileType">
格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b> 格式为 <b style="color: #f56c6c">{{ fileType.join('/') }}</b>
</template> </template>
的文件 的文件
</div> </div>
<!-- 文件列表 --> <!-- 文件列表 -->
<transition-group class="upload-file-list el-upload-list el-upload-list--text" name="el-fade-in-linear" tag="ul"> <transition-group class="upload-file-list el-upload-list el-upload-list--text" name="el-fade-in-linear" tag="ul">
<li :key="file.uid" class="el-upload-list__item ele-upload-list__item-content" v-for="(file, index) in fileList"> <li v-for="(file, index) in fileList" :key="file.uid" class="el-upload-list__item ele-upload-list__item-content">
<el-link :href="`${file.url}`" :underline="false" target="_blank"> <el-link :href="`${file.url}`" :underline="false" target="_blank">
<span class="el-icon-document"> {{ getFileName(file.name) }} </span> <span class="el-icon-document"> {{ getFileName(file.name) }} </span>
</el-link> </el-link>
<div class="ele-upload-list__item-content-action"> <div class="ele-upload-list__item-content-action">
<el-link :underline="false" @click="handleDelete(index)" type="danger">删除</el-link> <el-link :underline="false" type="danger" @click="handleDelete(index)">删除</el-link>
</div> </div>
</li> </li>
</transition-group> </transition-group>
@ -43,20 +43,20 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { listByIds, delOss } from "@/api/system/oss"; import { listByIds, delOss } from '@/api/system/oss';
import { propTypes } from '@/utils/propTypes'; import { propTypes } from '@/utils/propTypes';
import { globalHeaders } from "@/utils/request"; import { globalHeaders } from '@/utils/request';
const props = defineProps({ const props = defineProps({
modelValue: [String, Object, Array], modelValue: [String, Object, Array],
// 数量限制 // 数量限制
limit: propTypes.number.def(5), limit: propTypes.number.def(5),
// 大小限制(MB) // 大小限制(MB)
fileSize: propTypes.number.def(5), fileSize: propTypes.number.def(5),
// 文件类型, 例如['png', 'jpg', 'jpeg'] // 文件类型, 例如['png', 'jpg', 'jpeg']
fileType: propTypes.array.def(["doc", "xls", "ppt", "txt", "pdf"]), fileType: propTypes.array.def(['doc', 'xls', 'ppt', 'txt', 'pdf']),
// 是否显示提示 // 是否显示提示
isShowTip: propTypes.bool.def(true), isShowTip: propTypes.bool.def(true)
}); });
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@ -65,153 +65,163 @@ const number = ref(0);
const uploadList = ref<any[]>([]); const uploadList = ref<any[]>([]);
const baseUrl = import.meta.env.VITE_APP_BASE_API; const baseUrl = import.meta.env.VITE_APP_BASE_API;
const uploadFileUrl = ref(baseUrl + "/resource/oss/upload"); // 上传文件服务器地址 const uploadFileUrl = ref(baseUrl + '/resource/oss/upload'); // 上传文件服务器地址
const headers = ref(globalHeaders()); const headers = ref(globalHeaders());
const fileList = ref<any[]>([]); const fileList = ref<any[]>([]);
const showTip = computed( const showTip = computed(() => props.isShowTip && (props.fileType || props.fileSize));
() => props.isShowTip && (props.fileType || props.fileSize)
);
const fileUploadRef = ref<ElUploadInstance>(); const fileUploadRef = ref<ElUploadInstance>();
watch(() => props.modelValue, async val => { watch(
() => props.modelValue,
async (val) => {
if (val) { if (val) {
let temp = 1; let temp = 1;
// 首先将值转为数组 // 首先将值转为数组
let list = []; let list = [];
if (Array.isArray(val)) { if (Array.isArray(val)) {
list = val; list = val;
} else { } else {
const res = await listByIds(val as string) const res = await listByIds(val as string);
list = res.data.map((oss) => { list = res.data.map((oss) => {
const data = { name: oss.originalName, url: oss.url, ossId: oss.ossId }; const data = {
return data; name: oss.originalName,
}); url: oss.url,
} ossId: oss.ossId
// 然后将数组转为对象数组 };
fileList.value = list.map(item => { return data;
item = { name: item.name, url: item.url, ossId: item.ossId };
item.uid = item.uid || new Date().getTime() + temp++;
return item;
}); });
}
// 然后将数组转为对象数组
fileList.value = list.map((item) => {
item = { name: item.name, url: item.url, ossId: item.ossId };
item.uid = item.uid || new Date().getTime() + temp++;
return item;
});
} else { } else {
fileList.value = []; fileList.value = [];
return []; return [];
} }
}, { deep: true, immediate: true }); },
{ deep: true, immediate: true }
);
// 上传前校检格式和大小 // 上传前校检格式和大小
const handleBeforeUpload = (file: any) => { const handleBeforeUpload = (file: any) => {
// 校检文件类型 // 校检文件类型
if (props.fileType.length) { if (props.fileType.length) {
const fileName = file.name.split('.'); const fileName = file.name.split('.');
const fileExt = fileName[fileName.length - 1]; const fileExt = fileName[fileName.length - 1];
const isTypeOk = props.fileType.indexOf(fileExt) >= 0; const isTypeOk = props.fileType.indexOf(fileExt) >= 0;
if (!isTypeOk) { if (!isTypeOk) {
proxy?.$modal.msgError(`文件格式不正确, 请上传${props.fileType.join("/")}格式文件!`); proxy?.$modal.msgError(`文件格式不正确, 请上传${props.fileType.join('/')}格式文件!`);
return false; return false;
}
} }
// 校检文件大小 }
if (props.fileSize) { // 校检文件大小
const isLt = file.size / 1024 / 1024 < props.fileSize; if (props.fileSize) {
if (!isLt) { const isLt = file.size / 1024 / 1024 < props.fileSize;
proxy?.$modal.msgError(`上传文件大小不能超过 ${props.fileSize} MB!`); if (!isLt) {
return false; proxy?.$modal.msgError(`上传文件大小不能超过 ${props.fileSize} MB!`);
} return false;
} }
proxy?.$modal.loading("正在上传文件,请稍候..."); }
number.value++; proxy?.$modal.loading('正在上传文件,请稍候...');
return true; number.value++;
} return true;
};
// 文件个数超出 // 文件个数超出
const handleExceed = () => { const handleExceed = () => {
proxy?.$modal.msgError(`上传文件数量不能超过 ${props.limit} 个!`); proxy?.$modal.msgError(`上传文件数量不能超过 ${props.limit} 个!`);
} };
// 上传失败 // 上传失败
const handleUploadError = () => { const handleUploadError = () => {
proxy?.$modal.msgError("上传文件失败"); proxy?.$modal.msgError('上传文件失败');
} };
// 上传成功回调 // 上传成功回调
const handleUploadSuccess = (res: any, file: UploadFile) => { const handleUploadSuccess = (res: any, file: UploadFile) => {
if (res.code === 200) { if (res.code === 200) {
uploadList.value.push({ name: res.data.fileName, url: res.data.url, ossId: res.data.ossId }); uploadList.value.push({
uploadedSuccessfully(); name: res.data.fileName,
} else { url: res.data.url,
number.value--; ossId: res.data.ossId
proxy?.$modal.closeLoading(); });
proxy?.$modal.msgError(res.msg); uploadedSuccessfully();
fileUploadRef.value?.handleRemove(file); } else {
uploadedSuccessfully(); number.value--;
} proxy?.$modal.closeLoading();
} proxy?.$modal.msgError(res.msg);
fileUploadRef.value?.handleRemove(file);
uploadedSuccessfully();
}
};
// 删除文件 // 删除文件
const handleDelete = (index: number) => { const handleDelete = (index: number) => {
let ossId = fileList.value[index].ossId; let ossId = fileList.value[index].ossId;
delOss(ossId); delOss(ossId);
fileList.value.splice(index, 1); fileList.value.splice(index, 1);
emit("update:modelValue", listToString(fileList.value)); emit('update:modelValue', listToString(fileList.value));
} };
// 上传结束处理 // 上传结束处理
const uploadedSuccessfully = () => { const uploadedSuccessfully = () => {
if (number.value > 0 && uploadList.value.length === number.value) { if (number.value > 0 && uploadList.value.length === number.value) {
fileList.value = fileList.value.filter(f => f.url !== undefined).concat(uploadList.value); fileList.value = fileList.value.filter((f) => f.url !== undefined).concat(uploadList.value);
uploadList.value = []; uploadList.value = [];
number.value = 0; number.value = 0;
emit("update:modelValue", listToString(fileList.value)); emit('update:modelValue', listToString(fileList.value));
proxy?.$modal.closeLoading(); proxy?.$modal.closeLoading();
} }
} };
// 获取文件名称 // 获取文件名称
const getFileName = (name: string) => { const getFileName = (name: string) => {
// 如果是url那么取最后的名字 如果不是直接返回 // 如果是url那么取最后的名字 如果不是直接返回
if (name.lastIndexOf("/") > -1) { if (name.lastIndexOf('/') > -1) {
return name.slice(name.lastIndexOf("/") + 1); return name.slice(name.lastIndexOf('/') + 1);
} else { } else {
return name; return name;
} }
} };
// 对象转成指定字符串分隔 // 对象转成指定字符串分隔
const listToString = (list: any[], separator?: string) => { const listToString = (list: any[], separator?: string) => {
let strs = ""; let strs = '';
separator = separator || ","; separator = separator || ',';
list.forEach(item => { list.forEach((item) => {
if (item.ossId) { if (item.ossId) {
strs += item.ossId + separator; strs += item.ossId + separator;
} }
}) });
return strs != "" ? strs.substring(0, strs.length - 1) : ""; return strs != '' ? strs.substring(0, strs.length - 1) : '';
} };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.upload-file-uploader { .upload-file-uploader {
margin-bottom: 5px; margin-bottom: 5px;
} }
.upload-file-list .el-upload-list__item { .upload-file-list .el-upload-list__item {
border: 1px solid #e4e7ed; border: 1px solid #e4e7ed;
line-height: 2; line-height: 2;
margin-bottom: 10px; margin-bottom: 10px;
position: relative; position: relative;
} }
.upload-file-list .ele-upload-list__item-content { .upload-file-list .ele-upload-list__item-content {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
color: inherit; color: inherit;
} }
.ele-upload-list__item-content-action .el-link { .ele-upload-list__item-content-action .el-link {
margin-right: 10px; margin-right: 10px;
} }
</style> </style>

View File

@ -1,5 +1,5 @@
<template> <template>
<div style="padding: 0 15px;" @click="toggleClick"> <div style="padding: 0 15px" @click="toggleClick">
<svg :class="{ 'is-active': isActive }" class="hamburger" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="64" height="64"> <svg :class="{ 'is-active': isActive }" class="hamburger" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="64" height="64">
<path <path
d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z" d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z"
@ -13,12 +13,12 @@ import { propTypes } from '@/utils/propTypes';
defineProps({ defineProps({
isActive: propTypes.bool.def(false) isActive: propTypes.bool.def(false)
}) });
const emit = defineEmits(['toggleClick']) const emit = defineEmits(['toggleClick']);
const toggleClick = () => { const toggleClick = () => {
emit('toggleClick'); emit('toggleClick');
} };
</script> </script>
<style scoped> <style scoped>

View File

@ -1,6 +1,6 @@
<template> <template>
<div :class="{ 'show': show }" class="header-search"> <div :class="{ show: show }" class="header-search">
<svg-icon class-name="search-icon" icon-class="search" @click.stop="click"/> <svg-icon class-name="search-icon" icon-class="search" @click.stop="click" />
<el-select <el-select
ref="headerSearchSelectRef" ref="headerSearchSelectRef"
v-model="search" v-model="search"
@ -12,23 +12,22 @@
class="header-search-select" class="header-search-select"
@change="change" @change="change"
> >
<el-option v-for="option in options" :key="option.item.path" :value="option.item" <el-option v-for="option in options" :key="option.item.path" :value="option.item" :label="option.item.title.join(' > ')" />
:label="option.item.title.join(' > ')"/>
</el-select> </el-select>
</div> </div>
</template> </template>
<script setup lang="ts" name="HeaderSearch"> <script setup lang="ts" name="HeaderSearch">
import Fuse from 'fuse.js'; import Fuse from 'fuse.js';
import {getNormalPath} from '@/utils/ruoyi'; import { getNormalPath } from '@/utils/ruoyi';
import {isHttp} from '@/utils/validate'; import { isHttp } from '@/utils/validate';
import usePermissionStore from '@/store/modules/permission'; import usePermissionStore from '@/store/modules/permission';
import {RouteOption} from 'vue-router'; import { RouteRecordRaw } from 'vue-router';
type Router = Array<{ type Router = Array<{
path: string; path: string;
title: string[]; title: string[];
}> }>;
const search = ref(''); const search = ref('');
const options = ref<any>([]); const options = ref<any>([]);
@ -40,36 +39,36 @@ const router = useRouter();
const routes = computed(() => usePermissionStore().routes); const routes = computed(() => usePermissionStore().routes);
const click = () => { const click = () => {
show.value = !show.value show.value = !show.value;
if (show.value) { if (show.value) {
headerSearchSelectRef.value && headerSearchSelectRef.value.focus() headerSearchSelectRef.value && headerSearchSelectRef.value.focus();
} }
}; };
const close = () => { const close = () => {
headerSearchSelectRef.value && headerSearchSelectRef.value.blur() headerSearchSelectRef.value && headerSearchSelectRef.value.blur();
options.value = [] options.value = [];
show.value = false show.value = false;
} };
const change = (val: any) => { const change = (val: any) => {
const path = val.path; const path = val.path;
const query = val.query; const query = val.query;
if (isHttp(path)) { if (isHttp(path)) {
// http(s):// 路径新窗口打开 // http(s):// 路径新窗口打开
const pindex = path.indexOf("http"); const pindex = path.indexOf('http');
window.open(path.substr(pindex, path.length), "_blank"); window.open(path.substr(pindex, path.length), '_blank');
} else { } else {
if (query) { if (query) {
router.push({ path: path, query: JSON.parse(query) }); router.push({ path: path, query: JSON.parse(query) });
} else { } else {
router.push(path) router.push(path);
} }
} }
search.value = '' search.value = '';
options.value = [] options.value = [];
nextTick(() => { nextTick(() => {
show.value = false show.value = false;
}) });
} };
const initFuse = (list: Router) => { const initFuse = (list: Router) => {
fuse.value = new Fuse(list, { fuse.value = new Fuse(list, {
shouldSort: true, shouldSort: true,
@ -77,20 +76,23 @@ const initFuse = (list: Router) => {
location: 0, location: 0,
distance: 100, distance: 100,
minMatchCharLength: 1, minMatchCharLength: 1,
keys: [{ keys: [
name: 'title', {
weight: 0.7 name: 'title',
}, { weight: 0.7
name: 'path', },
weight: 0.3 {
}] name: 'path',
}) weight: 0.3
} }
]
});
};
// Filter out the routes that can be displayed in the sidebar // Filter out the routes that can be displayed in the sidebar
// And generate the internationalized title // And generate the internationalized title
const generateRoutes = (routes: RouteOption[], basePath = '', prefixTitle: string[] = []) => { const generateRoutes = (routes: RouteRecordRaw[], basePath = '', prefixTitle: string[] = []) => {
let res: Router = [] let res: Router = [];
routes.forEach(r => { routes.forEach((r) => {
// skip hidden router // skip hidden router
if (!r.hidden) { if (!r.hidden) {
const p = r.path.length > 0 && r.path[0] === '/' ? r.path : '/' + r.path; const p = r.path.length > 0 && r.path[0] === '/' ? r.path : '/' + r.path;
@ -98,7 +100,7 @@ const generateRoutes = (routes: RouteOption[], basePath = '', prefixTitle: strin
path: !isHttp(r.path) ? getNormalPath(basePath + p) : r.path, path: !isHttp(r.path) ? getNormalPath(basePath + p) : r.path,
title: [...prefixTitle], title: [...prefixTitle],
query: '' query: ''
} };
if (r.meta && r.meta.title) { if (r.meta && r.meta.title) {
data.title = [...data.title, r.meta.title]; data.title = [...data.title, r.meta.title];
if (r.redirect !== 'noRedirect') { if (r.redirect !== 'noRedirect') {
@ -109,7 +111,7 @@ const generateRoutes = (routes: RouteOption[], basePath = '', prefixTitle: strin
} }
if (r.query) { if (r.query) {
data.query = r.query data.query = r.query;
} }
// recursive child routes // recursive child routes
@ -120,20 +122,20 @@ const generateRoutes = (routes: RouteOption[], basePath = '', prefixTitle: strin
} }
} }
} }
}) });
return res; return res;
} };
const querySearch = (query: string) => { const querySearch = (query: string) => {
if (query !== '') { if (query !== '') {
options.value = fuse.value.search(query) options.value = fuse.value.search(query);
} else { } else {
options.value = [] options.value = [];
} }
} };
onMounted(() => { onMounted(() => {
searchPool.value = generateRoutes(routes.value); searchPool.value = generateRoutes(routes.value);
}) });
// watchEffect(() => { // watchEffect(() => {
// searchPool.value = generateRoutes(routes.value) // searchPool.value = generateRoutes(routes.value)
@ -141,15 +143,15 @@ onMounted(() => {
watch(show, (value) => { watch(show, (value) => {
if (value) { if (value) {
document.body.addEventListener('click', close) document.body.addEventListener('click', close);
} else { } else {
document.body.removeEventListener('click', close) document.body.removeEventListener('click', close);
} }
}) });
watch(searchPool, (list) => { watch(searchPool, (list) => {
initFuse(list) initFuse(list);
}) });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="relative" :style="{ width: width }"> <div class="relative" :style="{ width: width }">
<el-input v-model="modelValue" readonly @click="visible = !visible" placeholder="点击选择图标"> <el-input v-model="modelValue" readonly placeholder="点击选择图标" @click="visible = !visible">
<template #prepend> <template #prepend>
<svg-icon :icon-class="modelValue" /> <svg-icon :icon-class="modelValue" />
</template> </template>
@ -8,18 +8,18 @@
<el-popover shadow="none" :visible="visible" placement="bottom-end" trigger="click" :width="450"> <el-popover shadow="none" :visible="visible" placement="bottom-end" trigger="click" :width="450">
<template #reference> <template #reference>
<div @click="visible = !visible" class="cursor-pointer text-[#999] absolute right-[10px] top-0 height-[32px] leading-[32px]"> <div class="cursor-pointer text-[#999] absolute right-[10px] top-0 height-[32px] leading-[32px]" @click="visible = !visible">
<i-ep-caret-top v-show="visible"></i-ep-caret-top> <i-ep-caret-top v-show="visible"></i-ep-caret-top>
<i-ep-caret-bottom v-show="!visible"></i-ep-caret-bottom> <i-ep-caret-bottom v-show="!visible"></i-ep-caret-bottom>
</div> </div>
</template> </template>
<el-input class="p-2" v-model="filterValue" placeholder="搜索图标" clearable @input="filterIcons" /> <el-input v-model="filterValue" class="p-2" placeholder="搜索图标" clearable @input="filterIcons" />
<el-scrollbar height="w-[200px]"> <el-scrollbar height="w-[200px]">
<ul class="icon-list"> <ul class="icon-list">
<el-tooltip v-for="(iconName, index) in iconNames" :key="index" :content="iconName" placement="bottom" effect="light"> <el-tooltip v-for="(iconName, index) in iconNames" :key="index" :content="iconName" placement="bottom" effect="light">
<li :class="['icon-item', {active: modelValue == iconName}]" @click="selectedIcon(iconName)"> <li :class="['icon-item', { active: modelValue == iconName }]" @click="selectedIcon(iconName)">
<svg-icon color="var(--el-text-color-regular)" :icon-class="iconName" /> <svg-icon color="var(--el-text-color-regular)" :icon-class="iconName" />
</li> </li>
</el-tooltip> </el-tooltip>
@ -50,13 +50,11 @@ const filterValue = ref('');
*/ */
const filterIcons = () => { const filterIcons = () => {
if (filterValue.value) { if (filterValue.value) {
iconNames.value = icons.filter(iconName => iconNames.value = icons.filter((iconName) => iconName.includes(filterValue.value));
iconName.includes(filterValue.value)
);
} else { } else {
iconNames.value = icons; iconNames.value = icons;
} }
} };
/** /**
* 选择图标 * 选择图标
* @param iconName 选择的图标名称 * @param iconName 选择的图标名称
@ -64,12 +62,12 @@ const filterIcons = () => {
const selectedIcon = (iconName: string) => { const selectedIcon = (iconName: string) => {
emit('update:modelValue', iconName); emit('update:modelValue', iconName);
visible.value = false; visible.value = false;
} };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.el-scrollbar { .el-scrollbar {
max-height: calc(50vh - 100px)!important; max-height: calc(50vh - 100px) !important;
overflow-y: auto; overflow-y: auto;
} }
.el-divider--horizontal { .el-divider--horizontal {
@ -99,8 +97,8 @@ const selectedIcon = (iconName: string) => {
} }
} }
.active { .active {
border-color: var(--el-color-primary); border-color: var(--el-color-primary);
color: var(--el-color-primary); color: var(--el-color-primary);
} }
} }
</style> </style>

View File

@ -15,11 +15,11 @@ const props = defineProps({
src: propTypes.string.def(''), src: propTypes.string.def(''),
width: { width: {
type: [Number, String], type: [Number, String],
default: "" default: ''
}, },
height: { height: {
type: [Number, String], type: [Number, String],
default: "" default: ''
} }
}); });
@ -27,7 +27,7 @@ const realSrc = computed(() => {
if (!props.src) { if (!props.src) {
return; return;
} }
let real_src = props.src.split(",")[0]; let real_src = props.src.split(',')[0];
return real_src; return real_src;
}); });
@ -35,21 +35,17 @@ const realSrcList = computed(() => {
if (!props.src) { if (!props.src) {
return; return;
} }
let real_src_list = props.src.split(","); let real_src_list = props.src.split(',');
let srcList: string[] = []; let srcList: string[] = [];
real_src_list.forEach(item => { real_src_list.forEach((item) => {
return srcList.push(item); return srcList.push(item);
}); });
return srcList; return srcList;
}); });
const realWidth = computed(() => const realWidth = computed(() => (typeof props.width == 'string' ? props.width : `${props.width}px`));
typeof props.width == "string" ? props.width : `${props.width}px`
);
const realHeight = computed(() => const realHeight = computed(() => (typeof props.height == 'string' ? props.height : `${props.height}px`));
typeof props.height == "string" ? props.height : `${props.height}px`
);
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -1,6 +1,7 @@
<template> <template>
<div class="component-upload-image"> <div class="component-upload-image">
<el-upload <el-upload
ref="imageUpload"
multiple multiple
:action="uploadImgUrl" :action="uploadImgUrl"
list-type="picture-card" list-type="picture-card"
@ -9,7 +10,6 @@
:limit="limit" :limit="limit"
:on-error="handleUploadError" :on-error="handleUploadError"
:on-exceed="handleExceed" :on-exceed="handleExceed"
ref="imageUpload"
:before-remove="handleDelete" :before-remove="handleDelete"
:show-file-list="true" :show-file-list="true"
:headers="headers" :headers="headers"
@ -22,13 +22,13 @@
</el-icon> </el-icon>
</el-upload> </el-upload>
<!-- 上传提示 --> <!-- 上传提示 -->
<div class="el-upload__tip" v-if="showTip"> <div v-if="showTip" class="el-upload__tip">
请上传 请上传
<template v-if="fileSize"> <template v-if="fileSize">
大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b> 大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b>
</template> </template>
<template v-if="fileType"> <template v-if="fileType">
格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b> 格式为 <b style="color: #f56c6c">{{ fileType.join('/') }}</b>
</template> </template>
的文件 的文件
</div> </div>
@ -40,177 +40,176 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { listByIds, delOss } from "@/api/system/oss"; import { listByIds, delOss } from '@/api/system/oss';
import { ComponentInternalInstance } from "vue"; import { OssVO } from '@/api/system/oss/types';
import { OssVO } from "@/api/system/oss/types";
import { propTypes } from '@/utils/propTypes'; import { propTypes } from '@/utils/propTypes';
import {globalHeaders} from "@/utils/request"; import { globalHeaders } from '@/utils/request';
const props = defineProps({ const props = defineProps({
modelValue: [String, Object, Array], modelValue: [String, Object, Array],
// 图片数量限制 // 图片数量限制
limit: propTypes.number.def(5), limit: propTypes.number.def(5),
// 大小限制(MB) // 大小限制(MB)
fileSize: propTypes.number.def(5), fileSize: propTypes.number.def(5),
// 文件类型, 例如['png', 'jpg', 'jpeg'] // 文件类型, 例如['png', 'jpg', 'jpeg']
fileType: propTypes.array.def(["png", "jpg", "jpeg"]), fileType: propTypes.array.def(['png', 'jpg', 'jpeg']),
// 是否显示提示 // 是否显示提示
isShowTip: { isShowTip: {
type: Boolean, type: Boolean,
default: true default: true
}, }
}); });
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const emit = defineEmits(['update:modelValue']); const emit = defineEmits(['update:modelValue']);
const number = ref(0); const number = ref(0);
const uploadList = ref<any[]>([]); const uploadList = ref<any[]>([]);
const dialogImageUrl = ref(""); const dialogImageUrl = ref('');
const dialogVisible = ref(false); const dialogVisible = ref(false);
const baseUrl = import.meta.env.VITE_APP_BASE_API; const baseUrl = import.meta.env.VITE_APP_BASE_API;
const uploadImgUrl = ref(baseUrl + "/resource/oss/upload"); // 上传的图片服务器地址 const uploadImgUrl = ref(baseUrl + '/resource/oss/upload'); // 上传的图片服务器地址
const headers = ref(globalHeaders()); const headers = ref(globalHeaders());
const fileList = ref<any[]>([]); const fileList = ref<any[]>([]);
const showTip = computed( const showTip = computed(() => props.isShowTip && (props.fileType || props.fileSize));
() => props.isShowTip && (props.fileType || props.fileSize)
);
const imageUploadRef = ref<ElUploadInstance>(); const imageUploadRef = ref<ElUploadInstance>();
watch(() => props.modelValue, async val => { watch(
() => props.modelValue,
async (val) => {
if (val) { if (val) {
// 首先将值转为数组 // 首先将值转为数组
let list: OssVO[] = []; let list: OssVO[] = [];
if (Array.isArray(val)) { if (Array.isArray(val)) {
list = val as OssVO[]; list = val as OssVO[];
} else {
const res = await listByIds(val as string);
list = res.data;
}
// 然后将数组转为对象数组
fileList.value = list.map((item) => {
// 字符串回显处理 如果此处存的是url可直接回显 如果存的是id需要调用接口查出来
let itemData;
if (typeof item === 'string') {
itemData = { name: item, url: item };
} else { } else {
const res = await listByIds(val as string) // 此处name使用ossId 防止删除出现重名
list = res.data itemData = { name: item.ossId, url: item.url, ossId: item.ossId };
} }
// 然后将数组转为对象数组 return itemData;
fileList.value = list.map(item => { });
// 字符串回显处理 如果此处存的是url可直接回显 如果存的是id需要调用接口查出来
let itemData;
if (typeof item === "string") {
itemData = { name: item, url: item };
} else {
// 此处name使用ossId 防止删除出现重名
itemData = { name: item.ossId, url: item.url, ossId: item.ossId };
}
return itemData;
});
} else { } else {
fileList.value = []; fileList.value = [];
return []; return [];
} }
}, { deep: true, immediate: true }); },
{ deep: true, immediate: true }
);
/** 上传前loading加载 */ /** 上传前loading加载 */
const handleBeforeUpload = (file: any) => { const handleBeforeUpload = (file: any) => {
let isImg = false; let isImg = false;
if (props.fileType.length) { if (props.fileType.length) {
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);
}
isImg = props.fileType.some((type: any) => {
if (file.type.indexOf(type) > -1) return true;
if (fileExtension && fileExtension.indexOf(type) > -1) return true;
return false;
});
} else {
isImg = file.type.indexOf("image") > -1;
} }
if (!isImg) { isImg = props.fileType.some((type: any) => {
proxy?.$modal.msgError( if (file.type.indexOf(type) > -1) return true;
`文件格式不正确, 请上传${props.fileType.join("/")}图片格式文件!` if (fileExtension && fileExtension.indexOf(type) > -1) return true;
); return false;
return false; });
} else {
isImg = file.type.indexOf('image') > -1;
}
if (!isImg) {
proxy?.$modal.msgError(`文件格式不正确, 请上传${props.fileType.join('/')}图片格式文件!`);
return false;
}
if (props.fileSize) {
const isLt = file.size / 1024 / 1024 < props.fileSize;
if (!isLt) {
proxy?.$modal.msgError(`上传头像图片大小不能超过 ${props.fileSize} MB!`);
return false;
} }
if (props.fileSize) { }
const isLt = file.size / 1024 / 1024 < props.fileSize; proxy?.$modal.loading('正在上传图片,请稍候...');
if (!isLt) { number.value++;
proxy?.$modal.msgError(`上传头像图片大小不能超过 ${props.fileSize} MB!`); };
return false;
}
}
proxy?.$modal.loading("正在上传图片,请稍候...");
number.value++;
}
// 文件个数超出 // 文件个数超出
const handleExceed = () => { const handleExceed = () => {
proxy?.$modal.msgError(`上传文件数量不能超过 ${props.limit} 个!`); proxy?.$modal.msgError(`上传文件数量不能超过 ${props.limit} 个!`);
} };
// 上传成功回调 // 上传成功回调
const handleUploadSuccess = (res: any, file: UploadFile) => { const handleUploadSuccess = (res: any, file: UploadFile) => {
if (res.code === 200) { if (res.code === 200) {
uploadList.value.push({ name: res.data.fileName, url: res.data.url, ossId: res.data.ossId }); uploadList.value.push({ name: res.data.fileName, url: res.data.url, ossId: res.data.ossId });
uploadedSuccessfully(); uploadedSuccessfully();
} else { } else {
number.value--; number.value--;
proxy?.$modal.closeLoading(); proxy?.$modal.closeLoading();
proxy?.$modal.msgError(res.msg); proxy?.$modal.msgError(res.msg);
imageUploadRef.value?.handleRemove(file); imageUploadRef.value?.handleRemove(file);
uploadedSuccessfully(); uploadedSuccessfully();
} }
} };
// 删除图片 // 删除图片
const handleDelete = (file: UploadFile): boolean => { const handleDelete = (file: UploadFile): boolean => {
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 && uploadList.value.length === number.value) { if (findex > -1 && uploadList.value.length === number.value) {
let ossId = fileList.value[findex].ossId; let ossId = fileList.value[findex].ossId;
delOss(ossId); delOss(ossId);
fileList.value.splice(findex, 1); fileList.value.splice(findex, 1);
emit("update:modelValue", listToString(fileList.value)); emit('update:modelValue', listToString(fileList.value));
return false; return false;
} }
return true; return true;
} };
// 上传结束处理 // 上传结束处理
const uploadedSuccessfully = () => { const uploadedSuccessfully = () => {
if (number.value > 0 && uploadList.value.length === number.value) { if (number.value > 0 && uploadList.value.length === number.value) {
fileList.value = fileList.value.filter(f => f.url !== undefined).concat(uploadList.value); fileList.value = fileList.value.filter((f) => f.url !== undefined).concat(uploadList.value);
uploadList.value = []; uploadList.value = [];
number.value = 0; number.value = 0;
emit("update:modelValue", listToString(fileList.value)); emit('update:modelValue', listToString(fileList.value));
proxy?.$modal.closeLoading(); proxy?.$modal.closeLoading();
} }
} };
// 上传失败 // 上传失败
const handleUploadError = () => { const handleUploadError = () => {
proxy?.$modal.msgError("上传图片失败"); proxy?.$modal.msgError('上传图片失败');
proxy?.$modal.closeLoading(); proxy?.$modal.closeLoading();
} };
// 预览 // 预览
const handlePictureCardPreview = (file: any) => { const handlePictureCardPreview = (file: any) => {
dialogImageUrl.value = file.url; dialogImageUrl.value = file.url;
dialogVisible.value = true; dialogVisible.value = true;
} };
// 对象转成指定字符串分隔 // 对象转成指定字符串分隔
const listToString = (list: any[], separator?: string) => { const listToString = (list: any[], separator?: string) => {
let strs = ""; let strs = '';
separator = separator || ","; separator = separator || ',';
for (let i in list) { for (let i in list) {
if (undefined !== list[i].ossId && list[i].url.indexOf("blob:") !== 0) { if (undefined !== list[i].ossId && list[i].url.indexOf('blob:') !== 0) {
strs += list[i].ossId + separator; strs += list[i].ossId + separator;
}
} }
return strs != "" ? strs.substring(0, strs.length - 1) : ""; }
} return strs != '' ? strs.substring(0, strs.length - 1) : '';
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
// .el-upload--picture-card 控制加号部分 // .el-upload--picture-card 控制加号部分
:deep(.hide .el-upload--picture-card) { :deep(.hide .el-upload--picture-card) {
display: none; display: none;
} }
</style> </style>

View File

@ -20,16 +20,15 @@ import { useAppStore } from '@/store/modules/app';
const appStore = useAppStore(); const appStore = useAppStore();
const { locale } = useI18n(); const { locale } = useI18n();
const message: any = { const message: any = {
zh_CN: '切换语言成功!', zh_CN: '切换语言成功!',
en_US: 'Switch Language Successful!', en_US: 'Switch Language Successful!'
} };
const handleLanguageChange = (lang: string) => { const handleLanguageChange = (lang: string) => {
locale.value = lang; locale.value = lang;
appStore.changeLanguage(lang); appStore.changeLanguage(lang);
ElMessage.success(message[lang] || '切换语言成功!'); ElMessage.success(message[lang] || '切换语言成功!');
} };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -1,9 +1,9 @@
<template> <template>
<div :class="{ 'hidden': hidden }" class="pagination-container"> <div :class="{ hidden: hidden }" class="pagination-container">
<el-pagination <el-pagination
:background="background"
v-model:current-page="currentPage" v-model:current-page="currentPage"
v-model:page-size="pageSize" v-model:page-size="pageSize"
:background="background"
:layout="layout" :layout="layout"
:page-sizes="pageSizes" :page-sizes="pageSizes"
:pager-count="pagerCount" :pager-count="pagerCount"
@ -16,69 +16,69 @@
<script lang="ts"> <script lang="ts">
export default { export default {
name: 'Pagination' name: 'Pagination'
} };
</script> </script>
<script setup lang="ts"> <script setup lang="ts">
import { scrollTo } from '@/utils/scroll-to' import { scrollTo } from '@/utils/scroll-to';
import { propTypes } from "@/utils/propTypes"; import { propTypes } from '@/utils/propTypes';
const props = defineProps({ const props = defineProps({
total: propTypes.number, total: propTypes.number,
page: propTypes.number.def(1), page: propTypes.number.def(1),
limit: propTypes.number.def(20), limit: propTypes.number.def(20),
pageSizes: { pageSizes: {
type: Array as PropType<number[]>, type: Array,
default: () => [10, 20, 30, 50] default: () => [10, 20, 30, 50]
}, },
// 移动端页码按钮的数量端默认值5 // 移动端页码按钮的数量端默认值5
pagerCount: propTypes.number.def(document.body.clientWidth < 992 ? 5 : 7), pagerCount: propTypes.number.def(document.body.clientWidth < 992 ? 5 : 7),
layout: propTypes.string.def('total, sizes, prev, pager, next, jumper'), layout: propTypes.string.def('total, sizes, prev, pager, next, jumper'),
background: propTypes.bool.def(true), background: propTypes.bool.def(true),
autoScroll: propTypes.bool.def(true), autoScroll: propTypes.bool.def(true),
hidden: propTypes.bool.def(false), hidden: propTypes.bool.def(false),
float: propTypes.string.def('right') float: propTypes.string.def('right')
}) });
const emit = defineEmits(['update:page', 'update:limit', 'pagination']); const emit = defineEmits(['update:page', 'update:limit', 'pagination']);
const currentPage = computed({ const currentPage = computed({
get() { get() {
return props.page return props.page;
}, },
set(val) { set(val) {
emit('update:page', val) emit('update:page', val);
} }
}) });
const pageSize = computed({ const pageSize = computed({
get() { get() {
return props.limit return props.limit;
}, },
set(val){ set(val) {
emit('update:limit', val) emit('update:limit', val);
} }
}) });
function handleSizeChange(val: number) { function handleSizeChange(val: number) {
if (currentPage.value * val > props.total) { if (currentPage.value * val > props.total) {
currentPage.value = 1 currentPage.value = 1;
} }
emit('pagination', { page: currentPage.value, limit: val }) emit('pagination', { page: currentPage.value, limit: val });
if (props.autoScroll) { if (props.autoScroll) {
scrollTo(0, 800) scrollTo(0, 800);
} }
} }
function handleCurrentChange(val: number) { function handleCurrentChange(val: number) {
emit('pagination', { page: val, limit: pageSize.value }) emit('pagination', { page: val, limit: pageSize.value });
if (props.autoScroll) { if (props.autoScroll) {
scrollTo(0, 800) scrollTo(0, 800);
} }
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.pagination-container { .pagination-container {
padding: 32px 16px; padding: 32px 16px;
.el-pagination{ .el-pagination {
float: v-bind(float); float: v-bind(float);
} }
} }

View File

@ -1,13 +1,13 @@
<template> <template>
<div class="top-right-btn" :style="style"> <div class="top-right-btn" :style="style">
<el-row> <el-row>
<el-tooltip class="item" effect="dark" :content="showSearch ? '隐藏搜索' : '显示搜索'" placement="top" v-if="search"> <el-tooltip v-if="search" class="item" effect="dark" :content="showSearch ? '隐藏搜索' : '显示搜索'" placement="top">
<el-button circle icon="Search" @click="toggleSearch()" /> <el-button circle icon="Search" @click="toggleSearch()" />
</el-tooltip> </el-tooltip>
<el-tooltip class="item" effect="dark" content="刷新" placement="top"> <el-tooltip class="item" effect="dark" content="刷新" placement="top">
<el-button circle icon="Refresh" @click="refresh()" /> <el-button circle icon="Refresh" @click="refresh()" />
</el-tooltip> </el-tooltip>
<el-tooltip class="item" effect="dark" content="显示/隐藏列" placement="top" v-if="columns"> <el-tooltip v-if="columns" class="item" effect="dark" content="显示/隐藏列" placement="top">
<div class="show-btn"> <div class="show-btn">
<el-popover placement="bottom" trigger="click"> <el-popover placement="bottom" trigger="click">
<div class="tree-header">显示/隐藏列</div> <div class="tree-header">显示/隐藏列</div>
@ -15,9 +15,9 @@
ref="columnRef" ref="columnRef"
:data="columns" :data="columns"
show-checkbox show-checkbox
@check="columnChange"
node-key="key" node-key="key"
:props="{ label: 'label', children: 'children' }" :props="{ label: 'label', children: 'children' }"
@check="columnChange"
></el-tree> ></el-tree>
<template #reference> <template #reference>
<el-button circle icon="Menu" /> <el-button circle icon="Menu" />
@ -33,51 +33,49 @@
import { propTypes } from '@/utils/propTypes'; import { propTypes } from '@/utils/propTypes';
const props = defineProps({ const props = defineProps({
showSearch: propTypes.bool.def(true), showSearch: propTypes.bool.def(true),
columns: { columns: propTypes.fieldOption,
type: Array as PropType<FieldOption[]>, search: propTypes.bool.def(true),
}, gutter: propTypes.number.def(10)
search: propTypes.bool.def(true), });
gutter: propTypes.number.def(10),
})
const columnRef = ref<ElTreeInstance>(); const columnRef = ref<ElTreeInstance>();
const emits = defineEmits(['update:showSearch', 'queryTable']); const emits = defineEmits(['update:showSearch', 'queryTable']);
const style = computed(() => { const style = computed(() => {
const ret: any = {}; const ret: any = {};
if (props.gutter) { if (props.gutter) {
ret.marginRight = `${props.gutter / 2}px`; ret.marginRight = `${props.gutter / 2}px`;
} }
return ret; return ret;
}); });
// 搜索 // 搜索
function toggleSearch() { function toggleSearch() {
emits("update:showSearch", !props.showSearch); emits('update:showSearch', !props.showSearch);
} }
// 刷新 // 刷新
function refresh() { function refresh() {
emits("queryTable"); emits('queryTable');
} }
// 更改数据列的显示和隐藏 // 更改数据列的显示和隐藏
function columnChange(...args: any[]) { function columnChange(...args: any[]) {
props.columns?.forEach((item) => { props.columns?.forEach((item) => {
item.visible = args[1].checkedKeys.includes(item.key); item.visible = args[1].checkedKeys.includes(item.key);
}) });
} }
// 显隐列初始默认隐藏列 // 显隐列初始默认隐藏列
onMounted(() => { onMounted(() => {
props.columns?.forEach((item) => { props.columns?.forEach((item) => {
if (item.visible) { if (item.visible) {
columnRef.value?.setChecked(item.key, true, false); columnRef.value?.setChecked(item.key, true, false);
// value.value.push(item.key); // value.value.push(item.key);
} }
}) });
}) });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -93,7 +91,7 @@ onMounted(() => {
.my-el-transfer { .my-el-transfer {
text-align: center; text-align: center;
} }
.tree-header{ .tree-header {
width: 100%; width: 100%;
line-height: 24px; line-height: 24px;
text-align: center; text-align: center;

View File

@ -8,6 +8,6 @@
const url = ref('https://plus-doc.dromara.org/'); const url = ref('https://plus-doc.dromara.org/');
function goto() { function goto() {
window.open(url.value) window.open(url.value);
} }
</script> </script>

View File

@ -8,6 +8,6 @@
const url = ref('https://gitee.com/dromara/RuoYi-Vue-Plus'); const url = ref('https://gitee.com/dromara/RuoYi-Vue-Plus');
function goto() { function goto() {
window.open(url.value) window.open(url.value);
} }
</script> </script>

View File

@ -16,20 +16,20 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import useAppStore from "@/store/modules/app"; import useAppStore from '@/store/modules/app';
const appStore = useAppStore(); const appStore = useAppStore();
const size = computed(() => appStore.size); const size = computed(() => appStore.size);
const sizeOptions = ref([ const sizeOptions = ref([
{ label: "较大", value: "large" }, { label: '较大', value: 'large' },
{ label: "默认", value: "default" }, { label: '默认', value: 'default' },
{ label: "稍小", value: "small" }, { label: '稍小', value: 'small' }
]); ]);
const handleSetSize = (size: string) => { const handleSetSize = (size: string) => {
appStore.setSize(size); appStore.setSize(size);
} };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -8,17 +8,17 @@
import { propTypes } from '@/utils/propTypes'; import { propTypes } from '@/utils/propTypes';
const props = defineProps({ const props = defineProps({
iconClass: propTypes.string.isRequired, iconClass: propTypes.string.isRequired,
className: propTypes.string.def(''), className: propTypes.string.def(''),
color: propTypes.string.def(''), color: propTypes.string.def('')
}) });
const iconName = computed(() => `#icon-${props.iconClass}`); const iconName = computed(() => `#icon-${props.iconClass}`);
const svgClass = computed(() => { const svgClass = computed(() => {
if (props.className) { if (props.className) {
return `svg-icon ${props.className}` return `svg-icon ${props.className}`;
} }
return 'svg-icon' return 'svg-icon';
}) });
</script> </script>
<style scope lang="scss"> <style scope lang="scss">

View File

@ -1,19 +1,18 @@
<template> <template>
<el-menu :default-active="activeMenu" mode="horizontal" @select="handleSelect" :ellipsis="false"> <el-menu :default-active="activeMenu" mode="horizontal" :ellipsis="false" @select="handleSelect">
<template v-for="(item, index) in topMenus"> <template v-for="(item, index) in topMenus">
<el-menu-item :style="{'--theme': theme}" :index="item.path" :key="index" v-if="index < visibleNumber" <el-menu-item v-if="index < visibleNumber" :key="index" :style="{ '--theme': theme }" :index="item.path"
><svg-icon ><svg-icon v-if="item.meta && item.meta.icon && item.meta.icon !== '#'" :icon-class="item.meta ? item.meta.icon : ''" />
v-if="item.meta && item.meta.icon && item.meta.icon !== '#'" {{ item.meta?.title }}</el-menu-item
:icon-class="item.meta ? item.meta.icon : '' " /> {{ item.meta?.title }}</el-menu-item
> >
</template> </template>
<!-- 顶部菜单超出数量折叠 --> <!-- 顶部菜单超出数量折叠 -->
<el-sub-menu :style="{'--theme': theme}" index="more" v-if="topMenus.length > visibleNumber"> <el-sub-menu v-if="topMenus.length > visibleNumber" :style="{ '--theme': theme }" index="more">
<template #title>更多菜单</template> <template #title>更多菜单</template>
<template v-for="(item, index) in topMenus"> <template v-for="(item, index) in topMenus">
<el-menu-item :index="item.path" :key="index" v-if="index >= visibleNumber" <el-menu-item v-if="index >= visibleNumber" :key="index" :index="item.path"
><svg-icon :icon-class="item.meta ? item.meta.icon : '' " /> {{ item.meta?.title }}</el-menu-item ><svg-icon :icon-class="item.meta ? item.meta.icon : ''" /> {{ item.meta?.title }}</el-menu-item
> >
</template> </template>
</el-sub-menu> </el-sub-menu>
@ -26,7 +25,7 @@ import { isHttp } from '@/utils/validate';
import useAppStore from '@/store/modules/app'; import useAppStore from '@/store/modules/app';
import useSettingsStore from '@/store/modules/settings'; import useSettingsStore from '@/store/modules/settings';
import usePermissionStore from '@/store/modules/permission'; import usePermissionStore from '@/store/modules/permission';
import { RouteOption } from 'vue-router'; import { RouteRecordRaw } from 'vue-router';
// 顶部栏初始数 // 顶部栏初始数
const visibleNumber = ref<number>(-1); const visibleNumber = ref<number>(-1);
@ -35,9 +34,9 @@ const currentIndex = ref<string>();
// 隐藏侧边栏路由 // 隐藏侧边栏路由
const hideList = ['/index', '/user/profile']; const hideList = ['/index', '/user/profile'];
const appStore = useAppStore() const appStore = useAppStore();
const settingsStore = useSettingsStore() const settingsStore = useSettingsStore();
const permissionStore = usePermissionStore() const permissionStore = usePermissionStore();
const route = useRoute(); const route = useRoute();
const router = useRouter(); const router = useRouter();
@ -48,73 +47,73 @@ const routers = computed(() => permissionStore.topbarRouters);
// 顶部显示菜单 // 顶部显示菜单
const topMenus = computed(() => { const topMenus = computed(() => {
let topMenus:RouteOption[] = []; let topMenus: RouteRecordRaw[] = [];
routers.value.map((menu) => { routers.value.map((menu) => {
if (menu.hidden !== true) { if (menu.hidden !== true) {
// 兼容顶部栏一级菜单内部跳转 // 兼容顶部栏一级菜单内部跳转
if (menu.path === "/") { if (menu.path === '/') {
topMenus.push(menu.children? menu.children[0] : menu); topMenus.push(menu.children ? menu.children[0] : menu);
} else { } else {
topMenus.push(menu); topMenus.push(menu);
} }
} }
}) });
return topMenus; return topMenus;
}) });
// 设置子路由 // 设置子路由
const childrenMenus = computed(() => { const childrenMenus = computed(() => {
let childrenMenus:RouteOption[] = []; let childrenMenus: RouteRecordRaw[] = [];
routers.value.map((router) => { routers.value.map((router) => {
router.children?.forEach((item) => { router.children?.forEach((item) => {
if (item.parentPath === undefined) { if (item.parentPath === undefined) {
if(router.path === "/") { if (router.path === '/') {
item.path = "/" + item.path; item.path = '/' + item.path;
} else { } else {
if(!isHttp(item.path)) { if (!isHttp(item.path)) {
item.path = router.path + "/" + item.path; item.path = router.path + '/' + item.path;
} }
} }
item.parentPath = router.path; item.parentPath = router.path;
} }
childrenMenus.push(item); childrenMenus.push(item);
}) });
}) });
return constantRoutes.concat(childrenMenus); return constantRoutes.concat(childrenMenus);
}) });
// 默认激活的菜单 // 默认激活的菜单
const activeMenu = computed(() => { const activeMenu = computed(() => {
const path = route.path; const path = route.path;
let activePath = path; let activePath = path;
if (path !== undefined && path.lastIndexOf("/") > 0 && hideList.indexOf(path) === -1) { if (path !== undefined && path.lastIndexOf('/') > 0 && hideList.indexOf(path) === -1) {
const tmpPath = path.substring(1, path.length); const tmpPath = path.substring(1, path.length);
activePath = "/" + tmpPath.substring(0, tmpPath.indexOf("/")); activePath = '/' + tmpPath.substring(0, tmpPath.indexOf('/'));
if (!route.meta.link) { if (!route.meta.link) {
appStore.toggleSideBarHide(false); appStore.toggleSideBarHide(false);
} }
} else if(!route.children) { } else if (!route.children) {
activePath = path; activePath = path;
appStore.toggleSideBarHide(true); appStore.toggleSideBarHide(true);
} }
activeRoutes(activePath); activeRoutes(activePath);
return activePath; return activePath;
}) });
const setVisibleNumber = () => { const setVisibleNumber = () => {
const width = document.body.getBoundingClientRect().width / 3; const width = document.body.getBoundingClientRect().width / 3;
visibleNumber.value = parseInt(String(width / 85)); visibleNumber.value = parseInt(String(width / 85));
} };
const handleSelect = (key: string) => { const handleSelect = (key: string) => {
currentIndex.value = key; currentIndex.value = key;
const route = routers.value.find(item => item.path === key); const route = routers.value.find((item) => item.path === key);
if (isHttp(key)) { if (isHttp(key)) {
// http(s):// 路径新窗口打开 // http(s):// 路径新窗口打开
window.open(key, "_blank"); window.open(key, '_blank');
} else if (!route || !route.children) { } else if (!route || !route.children) {
// 没有子路由路径内部打开 // 没有子路由路径内部打开
const routeMenu = childrenMenus.value.find(item => item.path === key); const routeMenu = childrenMenus.value.find((item) => item.path === key);
if (routeMenu && routeMenu.query) { if (routeMenu && routeMenu.query) {
let query = JSON.parse(routeMenu.query); let query = JSON.parse(routeMenu.query);
router.push({ path: key, query: query }); router.push({ path: key, query: query });
@ -127,35 +126,35 @@ const handleSelect = (key: string) => {
activeRoutes(key); activeRoutes(key);
appStore.toggleSideBarHide(false); appStore.toggleSideBarHide(false);
} }
} };
const activeRoutes = (key: string) => { const activeRoutes = (key: string) => {
let routes:RouteOption[] = []; let routes: RouteRecordRaw[] = [];
if (childrenMenus.value && childrenMenus.value.length > 0) { if (childrenMenus.value && childrenMenus.value.length > 0) {
childrenMenus.value.map((item) => { childrenMenus.value.map((item) => {
if (key == item.parentPath || (key == "index" && "" == item.path)) { if (key == item.parentPath || (key == 'index' && '' == item.path)) {
routes.push(item); routes.push(item);
} }
}); });
} }
if(routes.length > 0) { if (routes.length > 0) {
permissionStore.setSidebarRouters(routes); permissionStore.setSidebarRouters(routes);
} else { } else {
appStore.toggleSideBarHide(true); appStore.toggleSideBarHide(true);
} }
return routes; return routes;
} };
onMounted(() => { onMounted(() => {
window.addEventListener('resize', setVisibleNumber) window.addEventListener('resize', setVisibleNumber);
}) });
onBeforeUnmount(() => { onBeforeUnmount(() => {
window.removeEventListener('resize', setVisibleNumber) window.removeEventListener('resize', setVisibleNumber);
}) });
onMounted(() => { onMounted(() => {
setVisibleNumber() setVisibleNumber();
}) });
</script> </script>
<style lang="scss"> <style lang="scss">
@ -168,7 +167,8 @@ onMounted(() => {
margin: 0 10px !important; margin: 0 10px !important;
} }
.topmenu-container.el-menu--horizontal > .el-menu-item.is-active, .el-menu--horizontal > .el-sub-menu.is-active .el-submenu__title { .topmenu-container.el-menu--horizontal > .el-menu-item.is-active,
.el-menu--horizontal > .el-sub-menu.is-active .el-submenu__title {
border-bottom: 2px solid #{'var(--theme)'} !important; border-bottom: 2px solid #{'var(--theme)'} !important;
color: #303133; color: #303133;
} }
@ -184,7 +184,9 @@ onMounted(() => {
} }
/* 背景色隐藏 */ /* 背景色隐藏 */
.topmenu-container.el-menu--horizontal>.el-menu-item:not(.is-disabled):focus, .topmenu-container.el-menu--horizontal>.el-menu-item:not(.is-disabled):hover, .topmenu-container.el-menu--horizontal>.el-submenu .el-submenu__title:hover { .topmenu-container.el-menu--horizontal > .el-menu-item:not(.is-disabled):focus,
.topmenu-container.el-menu--horizontal > .el-menu-item:not(.is-disabled):hover,
.topmenu-container.el-menu--horizontal > .el-submenu .el-submenu__title:hover {
background-color: #ffffff !important; background-color: #ffffff !important;
} }

View File

@ -1,14 +1,14 @@
<template> <template>
<div class="el-tree-select"> <div class="el-tree-select">
<el-select <el-select
style="width: 100%"
v-model="valueId"
ref="treeSelect" ref="treeSelect"
v-model="valueId"
style="width: 100%"
:filterable="true" :filterable="true"
:clearable="true" :clearable="true"
@clear="clearHandle"
:filter-method="selectFilterData" :filter-method="selectFilterData"
:placeholder="placeholder" :placeholder="placeholder"
@clear="clearHandle"
> >
<el-option :value="valueId" :label="valueTitle"> <el-option :value="valueId" :label="valueTitle">
<el-tree <el-tree
@ -29,43 +29,32 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
interface ObjMap {
value: string;
label: string;
children: string;
}
interface Props {
objMap: ObjMap;
accordion: boolean;
value: string | number;
options: any[];
placeholder: string;
}
const props = defineProps({ const props = withDefaults(defineProps<Props>(), {
/* 配置项 */ objMap: () => {
objMap: { return {
type: Object, value: 'id',
default: () => { label: 'label',
return { children: 'children'
value: 'id', // ID字段名 };
label: 'label', // 显示名称
children: 'children' // 子级字段名
}
}
}, },
/* 自动收起 */ accordion: false,
accordion: { value: '',
type: Boolean, options: () => [],
default: () => { placeholder: ''
return false });
}
},
/**当前双向数据绑定的值 */
value: {
type: [String, Number],
default: ''
},
/**当前的数据 */
options: {
type: Array,
default: () => []
},
/**输入框内部的文字 */
placeholder: {
type: String,
default: ''
}
})
const selectTree = ref<ElTreeSelectInstance>(); const selectTree = ref<ElTreeSelectInstance>();
@ -74,7 +63,7 @@ const emit = defineEmits(['update:value']);
const valueId = computed({ const valueId = computed({
get: () => props.value, get: () => props.value,
set: (val) => { set: (val) => {
emit('update:value', val) emit('update:value', val);
} }
}); });
const valueTitle = ref(''); const valueTitle = ref('');
@ -83,54 +72,54 @@ const defaultExpandedKey = ref<any[]>([]);
const initHandle = () => { const initHandle = () => {
nextTick(() => { nextTick(() => {
const selectedValue = valueId.value; const selectedValue = valueId.value;
if (selectedValue !== null && typeof (selectedValue) !== 'undefined') { if (selectedValue !== null && typeof selectedValue !== 'undefined') {
const node = selectTree.value?.getNode(selectedValue) const node = selectTree.value?.getNode(selectedValue);
if (node) { if (node) {
valueTitle.value = node.data[props.objMap.label] valueTitle.value = node.data[props.objMap.label];
selectTree.value?.setCurrentKey(selectedValue) // 设置默认选中 selectTree.value?.setCurrentKey(selectedValue); // 设置默认选中
defaultExpandedKey.value = [selectedValue] // 设置默认展开 defaultExpandedKey.value = [selectedValue]; // 设置默认展开
} }
} else { } else {
clearHandle() clearHandle();
} }
}) });
} };
const handleNodeClick = (node: any) => { const handleNodeClick = (node: any) => {
valueTitle.value = node[props.objMap.label] valueTitle.value = node[props.objMap.label];
valueId.value = node[props.objMap.value]; valueId.value = node[props.objMap.value];
defaultExpandedKey.value = []; defaultExpandedKey.value = [];
selectTree.value?.blur() selectTree.value?.blur();
selectFilterData('') selectFilterData('');
} };
const selectFilterData = (val: any) => { const selectFilterData = (val: any) => {
selectTree.value?.filter(val) selectTree.value?.filter(val);
} };
const filterNode = (value: any, data: any) => { const filterNode = (value: any, data: any) => {
if (!value) return true if (!value) return true;
return data[props.objMap['label']].indexOf(value) !== -1 return data[props.objMap['label']].indexOf(value) !== -1;
} };
const clearHandle = () => { const clearHandle = () => {
valueTitle.value = '' valueTitle.value = '';
valueId.value = '' valueId.value = '';
defaultExpandedKey.value = []; defaultExpandedKey.value = [];
clearSelected() clearSelected();
} };
const clearSelected = () => { const clearSelected = () => {
const allNode = document.querySelectorAll('#tree-option .el-tree-node') const allNode = document.querySelectorAll('#tree-option .el-tree-node');
allNode.forEach((element) => element.classList.remove('is-current')) allNode.forEach((element) => element.classList.remove('is-current'));
} };
onMounted(() => { onMounted(() => {
initHandle() initHandle();
}) });
watch(valueId, () => { watch(valueId, () => {
initHandle(); initHandle();
}) });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import "@/assets/styles/variables.module.scss"; @import '@/assets/styles/variables.module.scss';
.el-scrollbar .el-scrollbar__view .el-select-dropdown__item { .el-scrollbar .el-scrollbar__view .el-select-dropdown__item {
padding: 0; padding: 0;

View File

@ -9,18 +9,18 @@ import { propTypes } from '@/utils/propTypes';
const props = defineProps({ const props = defineProps({
src: propTypes.string.isRequired src: propTypes.string.isRequired
}) });
const height = ref(document.documentElement.clientHeight - 94.5 + "px;") const height = ref(document.documentElement.clientHeight - 94.5 + 'px;');
const loading = ref(true) const loading = ref(true);
const url = computed(() => props.src) const url = computed(() => props.src);
onMounted(() => { onMounted(() => {
setTimeout(() => { setTimeout(() => {
loading.value = false; loading.value = false;
}, 300); }, 300);
window.onresize = function temp() { window.onresize = function temp() {
height.value = document.documentElement.clientHeight - 94.5 + "px;"; height.value = document.documentElement.clientHeight - 94.5 + 'px;';
}; };
}) });
</script> </script>

View File

@ -9,7 +9,7 @@ export const hasPermi: Directive = {
// 「其他角色」按钮权限校验 // 「其他角色」按钮权限校验
const { value } = binding; const { value } = binding;
if (value && value instanceof Array && value.length > 0) { if (value && value instanceof Array && value.length > 0) {
const hasPermission = permissions.some((permi) => { const hasPermission = permissions.some((permi: string) => {
return permi === '*:*:*' || value.includes(permi); return permi === '*:*:*' || value.includes(permi);
}); });
if (!hasPermission) { if (!hasPermission) {
@ -30,7 +30,7 @@ export const hasRoles: Directive = {
const { value } = binding; const { value } = binding;
const { roles } = useUserStore(); const { roles } = useUserStore();
if (value && value instanceof Array && value.length > 0) { if (value && value instanceof Array && value.length > 0) {
const hasRole = roles.some((role) => { const hasRole = roles.some((role: string) => {
return role === 'admin' || value.includes(role); return role === 'admin' || value.includes(role);
}); });
if (!hasRole) { if (!hasRole) {

View File

@ -3,7 +3,7 @@
<router-view v-slot="{ Component, route }"> <router-view v-slot="{ Component, route }">
<transition :enter-active-class="animante" mode="out-in"> <transition :enter-active-class="animante" mode="out-in">
<keep-alive :include="tagsViewStore.cachedViews"> <keep-alive :include="tagsViewStore.cachedViews">
<component v-if="!route.meta.link" :is="Component" :key="route.path" /> <component :is="Component" v-if="!route.meta.link" :key="route.path" />
</keep-alive> </keep-alive>
</transition> </transition>
</router-view> </router-view>
@ -14,22 +14,25 @@
<script setup name="AppMain" lang="ts"> <script setup name="AppMain" lang="ts">
import useTagsViewStore from '@/store/modules/tagsView'; import useTagsViewStore from '@/store/modules/tagsView';
import useSettingsStore from '@/store/modules/settings'; import useSettingsStore from '@/store/modules/settings';
import IframeToggle from './IframeToggle/index.vue' import IframeToggle from './IframeToggle/index.vue';
import { ComponentInternalInstance } from "vue";
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const tagsViewStore = useTagsViewStore(); const tagsViewStore = useTagsViewStore();
// 随机动画集合 // 随机动画集合
const animante = ref<string>(''); const animante = ref<string>('');
const animationEnable = ref(useSettingsStore().animationEnable); const animationEnable = ref(useSettingsStore().animationEnable);
watch(()=> useSettingsStore().animationEnable, (val) => { watch(
() => useSettingsStore().animationEnable,
(val) => {
animationEnable.value = val; animationEnable.value = val;
if (val) { if (val) {
animante.value = proxy?.animate.animateList[Math.round(Math.random() * proxy?.animate.animateList.length)] as string; animante.value = proxy?.animate.animateList[Math.round(Math.random() * proxy?.animate.animateList.length)] as string;
} else { } else {
animante.value = proxy?.animate.defaultAnimate as string; animante.value = proxy?.animate.defaultAnimate as string;
} }
}, { immediate: true }); },
{ immediate: true }
);
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -41,7 +44,7 @@ watch(()=> useSettingsStore().animationEnable, (val) => {
overflow: hidden; overflow: hidden;
} }
.fixed-header+.app-main { .fixed-header + .app-main {
padding-top: 50px; padding-top: 50px;
} }
@ -51,7 +54,7 @@ watch(()=> useSettingsStore().animationEnable, (val) => {
min-height: calc(100vh - 84px); min-height: calc(100vh - 84px);
} }
.fixed-header+.app-main { .fixed-header + .app-main {
padding-top: 84px; padding-top: 84px;
} }
} }

View File

@ -2,16 +2,16 @@
<transition-group name="fade-transform" mode="out-in"> <transition-group name="fade-transform" mode="out-in">
<inner-link <inner-link
v-for="(item, index) in tagsViewStore.iframeViews" v-for="(item, index) in tagsViewStore.iframeViews"
:key="item.path"
:iframeId="'iframe' + index"
v-show="route.path === item.path" v-show="route.path === item.path"
:key="item.path"
:iframe-id="'iframe' + index"
:src="iframeUrl(item.meta ? item.meta.link : '', item.query)" :src="iframeUrl(item.meta ? item.meta.link : '', item.query)"
></inner-link> ></inner-link>
</transition-group> </transition-group>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import InnerLink from "../InnerLink/index.vue"; import InnerLink from '../InnerLink/index.vue';
import useTagsViewStore from '@/store/modules/tagsView'; import useTagsViewStore from '@/store/modules/tagsView';
const route = useRoute(); const route = useRoute();
@ -19,8 +19,10 @@ const tagsViewStore = useTagsViewStore();
function iframeUrl(url: string, query: any) { function iframeUrl(url: string, query: any) {
if (Object.keys(query).length > 0) { if (Object.keys(query).length > 0) {
let params = Object.keys(query).map((key) => key + "=" + query[key]).join("&"); let params = Object.keys(query)
return url + "?" + params; .map((key) => key + '=' + query[key])
.join('&');
return url + '?' + params;
} }
return url; return url;
} }

View File

@ -5,14 +5,11 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { propTypes } from '@/utils/propTypes';
const props = defineProps({ const props = defineProps({
src: { src: propTypes.string.def('/'),
type: String, iframeId: propTypes.string.isRequired
default: "/"
},
iframeId: {
type: String
}
}); });
const height = ref(document.documentElement.clientHeight - 94.5 + "px"); const height = ref(document.documentElement.clientHeight - 94.5 + 'px');
</script> </script>

View File

@ -1,18 +1,18 @@
<template> <template>
<div class="navbar"> <div class="navbar">
<hamburger id="hamburger-container" :is-active="appStore.sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" /> <hamburger id="hamburger-container" :is-active="appStore.sidebar.opened" class="hamburger-container" @toggle-click="toggleSideBar" />
<breadcrumb id="breadcrumb-container" class="breadcrumb-container" v-if="!settingsStore.topNav" /> <breadcrumb v-if="!settingsStore.topNav" id="breadcrumb-container" class="breadcrumb-container" />
<top-nav id="topmenu-container" class="topmenu-container" v-if="settingsStore.topNav" /> <top-nav v-if="settingsStore.topNav" id="topmenu-container" class="topmenu-container" />
<div class="right-menu flex align-center"> <div class="right-menu flex align-center">
<template v-if="appStore.device !== 'mobile'"> <template v-if="appStore.device !== 'mobile'">
<el-select <el-select
v-if="userId === 1 && tenantEnabled"
v-model="companyName" v-model="companyName"
clearable clearable
filterable filterable
reserve-keyword reserve-keyword
:placeholder="$t('navbar.selectTenant')" :placeholder="$t('navbar.selectTenant')"
v-if="userId === 1 && tenantEnabled"
@change="dynamicTenantEvent" @change="dynamicTenantEvent"
@clear="dynamicClearEvent" @clear="dynamicClearEvent"
> >
@ -63,17 +63,17 @@
</el-tooltip> </el-tooltip>
</template> </template>
<div class="avatar-container"> <div class="avatar-container">
<el-dropdown @command="handleCommand" class="right-menu-item hover-effect" trigger="click"> <el-dropdown class="right-menu-item hover-effect" trigger="click" @command="handleCommand">
<div class="avatar-wrapper"> <div class="avatar-wrapper">
<img :src="userStore.avatar" class="user-avatar" /> <img :src="userStore.avatar" class="user-avatar" />
<el-icon><caret-bottom /></el-icon> <el-icon><caret-bottom /></el-icon>
</div> </div>
<template #dropdown> <template #dropdown>
<el-dropdown-menu> <el-dropdown-menu>
<router-link to="/user/profile" v-if="!dynamic"> <router-link v-if="!dynamic" to="/user/profile">
<el-dropdown-item>{{ $t('navbar.personalCenter') }}</el-dropdown-item> <el-dropdown-item>{{ $t('navbar.personalCenter') }}</el-dropdown-item>
</router-link> </router-link>
<el-dropdown-item command="setLayout" v-if="settingsStore.showSettings"> <el-dropdown-item v-if="settingsStore.showSettings" command="setLayout">
<span>{{ $t('navbar.layoutSetting') }}</span> <span>{{ $t('navbar.layoutSetting') }}</span>
</el-dropdown-item> </el-dropdown-item>
<el-dropdown-item divided command="logout"> <el-dropdown-item divided command="logout">
@ -92,10 +92,9 @@ import SearchMenu from './TopBar/search.vue';
import useAppStore from '@/store/modules/app'; import useAppStore from '@/store/modules/app';
import useUserStore from '@/store/modules/user'; import useUserStore from '@/store/modules/user';
import useSettingsStore from '@/store/modules/settings'; import useSettingsStore from '@/store/modules/settings';
import { getTenantList } from "@/api/login"; import { getTenantList } from '@/api/login';
import { dynamicClear, dynamicTenant } from "@/api/system/tenant"; import { dynamicClear, dynamicTenant } from '@/api/system/tenant';
import { ComponentInternalInstance } from "vue"; import { TenantVO } from '@/api/types';
import { TenantVO } from "@/api/types";
import notice from './notice/index.vue'; import notice from './notice/index.vue';
import useNoticeStore from '@/store/modules/notice'; import useNoticeStore from '@/store/modules/notice';
@ -119,7 +118,7 @@ const searchMenuRef = ref<InstanceType<typeof SearchMenu>>();
const openSearchMenu = () => { const openSearchMenu = () => {
searchMenuRef.value?.openSearch(); searchMenuRef.value?.openSearch();
} };
// 动态切换 // 动态切换
const dynamicTenantEvent = async (tenantId: string) => { const dynamicTenantEvent = async (tenantId: string) => {
@ -129,14 +128,14 @@ const dynamicTenantEvent = async (tenantId: string) => {
proxy?.$tab.closeAllPage(); proxy?.$tab.closeAllPage();
proxy?.$router.push('/'); proxy?.$router.push('/');
} }
} };
const dynamicClearEvent = async () => { const dynamicClearEvent = async () => {
await dynamicClear(); await dynamicClear();
dynamic.value = false; dynamic.value = false;
proxy?.$tab.closeAllPage(); proxy?.$tab.closeAllPage();
proxy?.$router.push('/'); proxy?.$router.push('/');
} };
/** 租户列表 */ /** 租户列表 */
const initTenantList = async () => { const initTenantList = async () => {
@ -145,56 +144,58 @@ const initTenantList = async () => {
if (tenantEnabled.value) { if (tenantEnabled.value) {
tenantList.value = data.voList; tenantList.value = data.voList;
} }
} };
defineExpose({ defineExpose({
initTenantList, initTenantList
}) });
const toggleSideBar = () => { const toggleSideBar = () => {
appStore.toggleSideBar(false); appStore.toggleSideBar(false);
} };
const logout = async () => { const logout = async () => {
await ElMessageBox.confirm('确定注销并退出系统吗?', '提示', { await ElMessageBox.confirm('确定注销并退出系统吗?', '提示', {
confirmButtonText: '确定', confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning' type: 'warning'
}) });
await userStore.logout() await userStore.logout();
location.href = import.meta.env.VITE_APP_CONTEXT_PATH + 'index'; location.href = import.meta.env.VITE_APP_CONTEXT_PATH + 'index';
} };
const emits = defineEmits(['setLayout']) const emits = defineEmits(['setLayout']);
const setLayout = () => { const setLayout = () => {
emits('setLayout'); emits('setLayout');
} };
// 定义Command方法对象 通过key直接调用方法 // 定义Command方法对象 通过key直接调用方法
const commandMap: {[key: string]: any} = { const commandMap: { [key: string]: any } = {
setLayout, setLayout,
logout logout
}; };
const handleCommand = (command: string) => { const handleCommand = (command: string) => {
// 判断是否存在该方法 // 判断是否存在该方法
if (commandMap[command]) { if (commandMap[command]) {
commandMap[command](); commandMap[command]();
} }
} };
//用深度监听 消息 //用深度监听 消息
watch(() => noticeStore.state.value.notices, (newVal, oldVal) => { watch(
newNotice.value = newVal.filter((item: any) => !item.read).length; () => noticeStore.state.value.notices,
}, { deep: true }); (newVal) => {
newNotice.value = newVal.filter((item: any) => !item.read).length;
},
{ deep: true }
);
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
:deep(.el-select .el-input__wrapper) { :deep(.el-select .el-input__wrapper) {
height:30px; height: 30px;
} }
:deep(.el-badge__content.is-fixed){ :deep(.el-badge__content.is-fixed) {
top: 12px; top: 12px;
} }
.flex { .flex {

View File

@ -1,11 +1,11 @@
<template> <template>
<el-drawer v-model="showSettings" :withHeader="false" direction="rtl" size="300px" close-on-click-modal> <el-drawer v-model="showSettings" :with-header="false" direction="rtl" size="300px" close-on-click-modal>
<h3 class="drawer-title">主题风格设置</h3> <h3 class="drawer-title">主题风格设置</h3>
<div class="setting-drawer-block-checbox"> <div class="setting-drawer-block-checbox">
<div class="setting-drawer-block-checbox-item" @click="handleTheme(SideThemeEnum.DARK)"> <div class="setting-drawer-block-checbox-item" @click="handleTheme(SideThemeEnum.DARK)">
<img src="@/assets/images/dark.svg" alt="dark" /> <img src="@/assets/images/dark.svg" alt="dark" />
<div v-if="sideTheme === 'theme-dark'" class="setting-drawer-block-checbox-selectIcon" style="display: block;"> <div v-if="sideTheme === 'theme-dark'" class="setting-drawer-block-checbox-selectIcon" style="display: block">
<i aria-label="图标: check" class="anticon anticon-check"> <i aria-label="图标: check" class="anticon anticon-check">
<svg viewBox="64 64 896 896" data-icon="check" width="1em" height="1em" :fill="theme" aria-hidden="true" focusable="false" class> <svg viewBox="64 64 896 896" data-icon="check" width="1em" height="1em" :fill="theme" aria-hidden="true" focusable="false" class>
<path <path
@ -17,7 +17,7 @@
</div> </div>
<div class="setting-drawer-block-checbox-item" @click="handleTheme(SideThemeEnum.LIGHT)"> <div class="setting-drawer-block-checbox-item" @click="handleTheme(SideThemeEnum.LIGHT)">
<img src="@/assets/images/light.svg" alt="light" /> <img src="@/assets/images/light.svg" alt="light" />
<div v-if="sideTheme === 'theme-light'" class="setting-drawer-block-checbox-selectIcon" style="display: block;"> <div v-if="sideTheme === 'theme-light'" class="setting-drawer-block-checbox-selectIcon" style="display: block">
<i aria-label="图标: check" class="anticon anticon-check"> <i aria-label="图标: check" class="anticon anticon-check">
<svg viewBox="64 64 896 896" data-icon="check" width="1em" height="1em" :fill="theme" aria-hidden="true" focusable="false" class> <svg viewBox="64 64 896 896" data-icon="check" width="1em" height="1em" :fill="theme" aria-hidden="true" focusable="false" class>
<path <path
@ -37,7 +37,7 @@
<div class="drawer-item"> <div class="drawer-item">
<span>深色模式</span> <span>深色模式</span>
<span class="comp-style"> <span class="comp-style">
<el-switch v-model="isDark" @change="toggleDark" class="drawer-switch" /> <el-switch v-model="isDark" class="drawer-switch" @change="toggleDark" />
</span> </span>
</div> </div>
@ -88,126 +88,126 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { useDynamicTitle } from '@/utils/dynamicTitle' import { useDynamicTitle } from '@/utils/dynamicTitle';
import useAppStore from '@/store/modules/app' import useAppStore from '@/store/modules/app';
import useSettingsStore from '@/store/modules/settings' import useSettingsStore from '@/store/modules/settings';
import usePermissionStore from '@/store/modules/permission' import usePermissionStore from '@/store/modules/permission';
import { handleThemeStyle } from '@/utils/theme' import { handleThemeStyle } from '@/utils/theme';
import { ComponentInternalInstance } from "vue"; import { SettingTypeEnum } from '@/enums/SettingTypeEnum';
import { SettingTypeEnum } from "@/enums/SettingTypeEnum"; import { SideThemeEnum } from '@/enums/SideThemeEnum';
import { SideThemeEnum } from "@/enums/SideThemeEnum";
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const appStore = useAppStore() const appStore = useAppStore();
const settingsStore = useSettingsStore() const settingsStore = useSettingsStore();
const permissionStore = usePermissionStore() const permissionStore = usePermissionStore();
const showSettings = ref(false); const showSettings = ref(false);
const theme = ref(settingsStore.theme); const theme = ref(settingsStore.theme);
const sideTheme = ref(settingsStore.sideTheme); const sideTheme = ref(settingsStore.sideTheme);
const storeSettings = computed(() => settingsStore); const storeSettings = computed(() => settingsStore);
const predefineColors = ref(["#409EFF", "#ff4500", "#ff8c00", "#ffd700", "#90ee90", "#00ced1", "#1e90ff", "#c71585"]); const predefineColors = ref(['#409EFF', '#ff4500', '#ff8c00', '#ffd700', '#90ee90', '#00ced1', '#1e90ff', '#c71585']);
// 是否暗黑模式 // 是否暗黑模式
const isDark = useDark({ const isDark = useDark({
storageKey: 'useDarkKey', storageKey: 'useDarkKey',
valueDark: 'dark', valueDark: 'dark',
valueLight: 'light', valueLight: 'light'
}); });
watch(isDark, ()=> { watch(isDark, () => {
if (isDark.value) { if (isDark.value) {
settingsStore.changeSetting({ key: SettingTypeEnum.SIDE_THEME, value: SideThemeEnum.DARK }) settingsStore.changeSetting({ key: SettingTypeEnum.SIDE_THEME, value: SideThemeEnum.DARK });
} else { } else {
settingsStore.changeSetting({ key: SettingTypeEnum.SIDE_THEME, value: sideTheme.value }) settingsStore.changeSetting({ key: SettingTypeEnum.SIDE_THEME, value: sideTheme.value });
} }
}) });
const toggleDark = () => useToggle(isDark); const toggleDark = () => useToggle(isDark);
/** 是否需要topNav */ /** 是否需要topNav */
const topNav = computed({ const topNav = computed({
get: () => storeSettings.value.topNav, get: () => storeSettings.value.topNav,
set: (val) => { set: (val) => {
settingsStore.changeSetting({ key: SettingTypeEnum.TOP_NAV, value: val }) settingsStore.changeSetting({ key: SettingTypeEnum.TOP_NAV, value: val });
if (!val) { if (!val) {
appStore.toggleSideBarHide(false); appStore.toggleSideBarHide(false);
permissionStore.setSidebarRouters(permissionStore.defaultRoutes); permissionStore.setSidebarRouters(permissionStore.defaultRoutes);
}
} }
}) }
});
/** 是否需要tagview */ /** 是否需要tagview */
const tagsView = computed({ const tagsView = computed({
get: () => storeSettings.value.tagsView, get: () => storeSettings.value.tagsView,
set: (val) => { set: (val) => {
settingsStore.changeSetting({ key: SettingTypeEnum.TAGS_VIEW, value: val }) settingsStore.changeSetting({ key: SettingTypeEnum.TAGS_VIEW, value: val });
} }
}) });
/**是否需要固定头部 */ /**是否需要固定头部 */
const fixedHeader = computed({ const fixedHeader = computed({
get: () => storeSettings.value.fixedHeader, get: () => storeSettings.value.fixedHeader,
set: (val) => { set: (val) => {
settingsStore.changeSetting({ key: SettingTypeEnum.FIXED_HEADER, value: val }) settingsStore.changeSetting({ key: SettingTypeEnum.FIXED_HEADER, value: val });
} }
}) });
/**是否需要侧边栏的logo */ /**是否需要侧边栏的logo */
const sidebarLogo = computed({ const sidebarLogo = computed({
get: () => storeSettings.value.sidebarLogo, get: () => storeSettings.value.sidebarLogo,
set: (val) => { set: (val) => {
settingsStore.changeSetting({ key: SettingTypeEnum.SIDEBAR_LOGO, value: val }) settingsStore.changeSetting({ key: SettingTypeEnum.SIDEBAR_LOGO, value: val });
} }
}) });
/**是否需要侧边栏的动态网页的title */ /**是否需要侧边栏的动态网页的title */
const dynamicTitle = computed({ const dynamicTitle = computed({
get: () => storeSettings.value.dynamicTitle, get: () => storeSettings.value.dynamicTitle,
set: (val) => { set: (val) => {
settingsStore.changeSetting({ key: SettingTypeEnum.DYNAMIC_TITLE, value: val }) settingsStore.changeSetting({ key: SettingTypeEnum.DYNAMIC_TITLE, value: val });
// 动态设置网页标题 // 动态设置网页标题
useDynamicTitle() useDynamicTitle();
} }
}) });
const themeChange = (val: string | null) => { const themeChange = (val: string) => {
settingsStore.changeSetting({ key: SettingTypeEnum.THEME, value: val }) settingsStore.changeSetting({ key: SettingTypeEnum.THEME, value: val });
theme.value = val; theme.value = val;
if (val) { if (val) {
handleThemeStyle(val); handleThemeStyle(val);
} }
} };
const handleTheme = (val: string) => { const handleTheme = (val: string) => {
sideTheme.value = val; sideTheme.value = val;
if (isDark.value && val === SideThemeEnum.LIGHT) { if (isDark.value && val === SideThemeEnum.LIGHT) {
// 暗黑模式颜色不变 // 暗黑模式颜色不变
settingsStore.changeSetting({ key: SettingTypeEnum.SIDE_THEME, value: SideThemeEnum.DARK }) settingsStore.changeSetting({ key: SettingTypeEnum.SIDE_THEME, value: SideThemeEnum.DARK });
return return;
} }
settingsStore.changeSetting({ key: SettingTypeEnum.SIDE_THEME, value: val }) settingsStore.changeSetting({ key: SettingTypeEnum.SIDE_THEME, value: val });
} };
const saveSetting = () => { const saveSetting = () => {
proxy?.$modal.loading("正在保存到本地,请稍候..."); proxy?.$modal.loading('正在保存到本地,请稍候...');
let layoutSetting = { let layoutSetting = {
"topNav": storeSettings.value.topNav, topNav: storeSettings.value.topNav,
"tagsView": storeSettings.value.tagsView, tagsView: storeSettings.value.tagsView,
"fixedHeader": storeSettings.value.fixedHeader, fixedHeader: storeSettings.value.fixedHeader,
"sidebarLogo": storeSettings.value.sidebarLogo, sidebarLogo: storeSettings.value.sidebarLogo,
"dynamicTitle": storeSettings.value.dynamicTitle, dynamicTitle: storeSettings.value.dynamicTitle,
"sideTheme": storeSettings.value.sideTheme, sideTheme: storeSettings.value.sideTheme,
"theme": storeSettings.value.theme theme: storeSettings.value.theme
}; };
localStorage.setItem("layout-setting", JSON.stringify(layoutSetting)); localStorage.setItem('layout-setting', JSON.stringify(layoutSetting));
setTimeout(() => {proxy?.$modal.closeLoading()}, 1000) setTimeout(() => {
} proxy?.$modal.closeLoading();
}, 1000);
};
const resetSetting = () => { const resetSetting = () => {
proxy?.$modal.loading("正在清除设置缓存并刷新,请稍候..."); proxy?.$modal.loading('正在清除设置缓存并刷新,请稍候...');
localStorage.removeItem("layout-setting") localStorage.removeItem('layout-setting');
setTimeout("window.location.reload()", 1000) setTimeout('window.location.reload()', 1000);
} };
const openSetting = () => { const openSetting = () => {
showSettings.value = true; showSettings.value = true;
} };
defineExpose({ defineExpose({
openSetting, openSetting
}) });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -5,36 +5,36 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { isExternal } from '@/utils/validate' import { isExternal } from '@/utils/validate';
const props = defineProps({ const props = defineProps({
to: { to: {
type: [String, Object], type: [String, Object],
required: true required: true
} }
}) });
const isExt = computed(() => { const isExt = computed(() => {
return isExternal(props.to as string) return isExternal(props.to as string);
}) });
const type = computed(() => { const type = computed(() => {
if (isExt.value) { if (isExt.value) {
return 'a' return 'a';
} }
return 'router-link' return 'router-link';
}) });
function linkProps() { function linkProps() {
if (isExt.value) { if (isExt.value) {
return {
href: props.to,
target: '_blank',
rel: 'noopener'
}
}
return { return {
to: props.to href: props.to,
} target: '_blank',
rel: 'noopener'
};
}
return {
to: props.to
};
} }
</script> </script>

View File

@ -1,7 +1,7 @@
<template> <template>
<div <div
class="sidebar-logo-container" class="sidebar-logo-container"
:class="{ 'collapse': collapse }" :class="{ collapse: collapse }"
:style="{ backgroundColor: sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground }" :style="{ backgroundColor: sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground }"
> >
<transition :enter-active-class="proxy?.animate.logoAnimate.enter" mode="out-in"> <transition :enter-active-class="proxy?.animate.logoAnimate.enter" mode="out-in">
@ -22,18 +22,17 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import variables from '@/assets/styles/variables.module.scss' import variables from '@/assets/styles/variables.module.scss';
import logo from '@/assets/logo/logo.png' import logo from '@/assets/logo/logo.png';
import useSettingsStore from '@/store/modules/settings' import useSettingsStore from '@/store/modules/settings';
import { ComponentInternalInstance } from "vue";
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
defineProps({ defineProps({
collapse: { collapse: {
type: Boolean, type: Boolean,
required: true required: true
} }
}) });
const title = ref('RuoYi-Vue-Plus'); const title = ref('RuoYi-Vue-Plus');
const settingsStore = useSettingsStore(); const settingsStore = useSettingsStore();
@ -77,7 +76,12 @@ const sideTheme = computed(() => settingsStore.sideTheme);
font-weight: 600; font-weight: 600;
line-height: 50px; line-height: 50px;
font-size: 14px; font-size: 14px;
font-family: Avenir, Helvetica Neue, Arial, Helvetica, sans-serif; font-family:
Avenir,
Helvetica Neue,
Arial,
Helvetica,
sans-serif;
vertical-align: middle; vertical-align: middle;
} }
} }

View File

@ -13,7 +13,7 @@
<el-sub-menu v-else ref="subMenu" :index="resolvePath(item.path)" teleported> <el-sub-menu v-else ref="subMenu" :index="resolvePath(item.path)" teleported>
<template v-if="item.meta" #title> <template v-if="item.meta" #title>
<svg-icon :icon-class="item.meta ? item.meta.icon : '' " /> <svg-icon :icon-class="item.meta ? item.meta.icon : ''" />
<span class="menu-title" :title="hasTitle(item.meta?.title)">{{ item.meta?.title }}</span> <span class="menu-title" :title="hasTitle(item.meta?.title)">{{ item.meta?.title }}</span>
</template> </template>
@ -30,79 +30,75 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { isExternal } from '@/utils/validate' import { isExternal } from '@/utils/validate';
import AppLink from './Link.vue' import AppLink from './Link.vue';
import { getNormalPath } from '@/utils/ruoyi' import { getNormalPath } from '@/utils/ruoyi';
import { RouteOption } from "vue-router"; import { RouteRecordRaw } from 'vue-router';
const props = defineProps({ const props = defineProps({
// route object // route object
item: { item: {
type: Object as PropType<RouteOption>, type: Object as PropType<RouteRecordRaw>,
required: true required: true
}, },
isNest: { isNest: {
type: Boolean, type: Boolean,
default: false default: false
}, },
basePath: { basePath: {
type: String, type: String,
default: '' default: ''
} }
}) });
const onlyOneChild = ref<any>({}); const onlyOneChild = ref<any>({});
const hasOneShowingChild = (parent: RouteOption, children?:RouteOption[]) => { const hasOneShowingChild = (parent: RouteRecordRaw, children?: RouteRecordRaw[]) => {
if (!children) { if (!children) {
children = []; children = [];
}
const showingChildren = children.filter((item) => {
if (item.hidden) {
return false;
} else {
// Temp set(will be used if only has one showing child)
onlyOneChild.value = item;
return true;
} }
const showingChildren = children.filter(item => { });
if (item.hidden) {
return false
} else {
// Temp set(will be used if only has one showing child)
onlyOneChild.value = item
return true
}
})
// When there is only one child router, the child router is displayed by default // When there is only one child router, the child router is displayed by default
if (showingChildren.length === 1) { if (showingChildren.length === 1) {
return true return true;
} }
// Show parent if there are no child router to display // Show parent if there are no child router to display
if (showingChildren.length === 0) { if (showingChildren.length === 0) {
onlyOneChild.value = { ...parent, path: '', noShowingChildren: true } onlyOneChild.value = { ...parent, path: '', noShowingChildren: true };
if (parent.name === '2222') { return true;
console.log(onlyOneChild.value) }
}
return true
}
return false;
return false
}; };
const resolvePath = (routePath:string, routeQuery?:string): any => { const resolvePath = (routePath: string, routeQuery?: string): any => {
if (isExternal(routePath)) { if (isExternal(routePath)) {
return routePath return routePath;
} }
if (isExternal(props.basePath)) { if (isExternal(props.basePath)) {
return props.basePath return props.basePath;
} }
if (routeQuery) { if (routeQuery) {
let query = JSON.parse(routeQuery); let query = JSON.parse(routeQuery);
return { path: getNormalPath(props.basePath + '/' + routePath), query: query } return { path: getNormalPath(props.basePath + '/' + routePath), query: query };
} }
return getNormalPath(props.basePath + '/' + routePath) return getNormalPath(props.basePath + '/' + routePath);
} };
const hasTitle = (title: string | undefined): string => { const hasTitle = (title: string | undefined): string => {
if(!title || title.length <= 5) { if (!title || title.length <= 5) {
return ""; return '';
} }
return title; return title;
} };
</script> </script>

View File

@ -21,35 +21,34 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import Logo from './Logo.vue' import Logo from './Logo.vue';
import SidebarItem from './SidebarItem.vue' import SidebarItem from './SidebarItem.vue';
import variables from '@/assets/styles/variables.module.scss' import variables from '@/assets/styles/variables.module.scss';
import useAppStore from '@/store/modules/app' import useAppStore from '@/store/modules/app';
import useSettingsStore from '@/store/modules/settings' import useSettingsStore from '@/store/modules/settings';
import usePermissionStore from '@/store/modules/permission' import usePermissionStore from '@/store/modules/permission';
import { RouteOption } from "vue-router"; import { RouteRecordRaw } from 'vue-router';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const route = useRoute(); const route = useRoute();
const appStore = useAppStore() const appStore = useAppStore();
const settingsStore = useSettingsStore() const settingsStore = useSettingsStore();
const permissionStore = usePermissionStore() const permissionStore = usePermissionStore();
const sidebarRouters = computed<RouteRecordRaw[]>(() => permissionStore.sidebarRouters);
const sidebarRouters = computed<RouteOption[]>(() => permissionStore.sidebarRouters);
const showLogo = computed(() => settingsStore.sidebarLogo); const showLogo = computed(() => settingsStore.sidebarLogo);
const sideTheme = computed(() => settingsStore.sideTheme); const sideTheme = computed(() => settingsStore.sideTheme);
const theme = computed(() => settingsStore.theme); const theme = computed(() => settingsStore.theme);
const isCollapse = computed(() => !appStore.sidebar.opened); const isCollapse = computed(() => !appStore.sidebar.opened);
const activeMenu = computed(() => { const activeMenu = computed(() => {
const { meta, path } = route; const { meta, path } = route;
// if set path, the sidebar will highlight the path you set // if set path, the sidebar will highlight the path you set
if (meta.activeMenu) { if (meta.activeMenu) {
return meta.activeMenu; return meta.activeMenu;
} }
return path; return path;
}) });
const bgColor = computed(() => sideTheme.value === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground); const bgColor = computed(() => (sideTheme.value === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground));
const textColor = computed(() => sideTheme.value === 'theme-dark' ? variables.menuColor : variables.menuLightColor); const textColor = computed(() => (sideTheme.value === 'theme-dark' ? variables.menuColor : variables.menuLightColor));
</script> </script>

View File

@ -10,7 +10,6 @@ import { LoginData } from '@/api/types';
const route = useRoute(); const route = useRoute();
const loading = ref(true); const loading = ref(true);
/** /**
* 接收Route传递的参数 * 接收Route传递的参数
* @param {Object} route.query. * @param {Object} route.query.
@ -18,8 +17,7 @@ const loading = ref(true);
const code = route.query.code as string; const code = route.query.code as string;
const state = route.query.state as string; const state = route.query.state as string;
const source = route.query.source as string; const source = route.query.source as string;
const tenantId = localStorage.getItem("tenantId") ? localStorage.getItem("tenantId") as string : '000000'; const tenantId = localStorage.getItem('tenantId') ? (localStorage.getItem('tenantId') as string) : '000000';
const processResponse = async (res: any) => { const processResponse = async (res: any) => {
if (res.code !== 200) { if (res.code !== 200) {
@ -52,7 +50,6 @@ const callbackByCode = async (data: LoginData) => {
}; };
const loginByCode = async (data: LoginData) => { const loginByCode = async (data: LoginData) => {
console.log(2)
try { try {
const res = await login(data); const res = await login(data);
await processResponse(res); await processResponse(res);

View File

@ -5,84 +5,84 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import useTagsViewStore from '@/store/modules/tagsView' import useTagsViewStore from '@/store/modules/tagsView';
import { TagView } from 'vue-router' import { TagView } from 'vue-router';
const tagAndTagSpacing = ref(4); const tagAndTagSpacing = ref(4);
const scrollContainerRef = ref<ElScrollbarInstance>() const scrollContainerRef = ref<ElScrollbarInstance>();
const scrollWrapper = computed(() => scrollContainerRef.value?.$refs.wrapRef as any); const scrollWrapper = computed(() => scrollContainerRef.value?.$refs.wrapRef as any);
onMounted(() => { onMounted(() => {
scrollWrapper.value?.addEventListener('scroll', emitScroll, true) scrollWrapper.value?.addEventListener('scroll', emitScroll, true);
}) });
onBeforeUnmount(() => { onBeforeUnmount(() => {
scrollWrapper.value?.removeEventListener('scroll', emitScroll) scrollWrapper.value?.removeEventListener('scroll', emitScroll);
}) });
const handleScroll = (e: WheelEvent) => { const handleScroll = (e: WheelEvent) => {
const eventDelta = (e as any).wheelDelta || - e.deltaY * 40 const eventDelta = (e as any).wheelDelta || -e.deltaY * 40;
const $scrollWrapper = scrollWrapper.value; const $scrollWrapper = scrollWrapper.value;
$scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4 $scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4;
} };
const emits = defineEmits(['scroll']) const emits = defineEmits(['scroll']);
const emitScroll = () => { const emitScroll = () => {
emits('scroll') emits('scroll');
} };
const tagsViewStore = useTagsViewStore() const tagsViewStore = useTagsViewStore();
const visitedViews = computed(() => tagsViewStore.visitedViews); const visitedViews = computed(() => tagsViewStore.visitedViews);
const moveToTarget = (currentTag: TagView) => { const moveToTarget = (currentTag: TagView) => {
const $container = scrollContainerRef.value?.$el const $container = scrollContainerRef.value?.$el;
const $containerWidth = $container.offsetWidth const $containerWidth = $container.offsetWidth;
const $scrollWrapper = scrollWrapper.value; const $scrollWrapper = scrollWrapper.value;
let firstTag = null let firstTag = null;
let lastTag = null let lastTag = null;
// find first tag and last tag // find first tag and last tag
if (visitedViews.value.length > 0) { if (visitedViews.value.length > 0) {
firstTag = visitedViews.value[0] firstTag = visitedViews.value[0];
lastTag = visitedViews.value[visitedViews.value.length - 1] lastTag = visitedViews.value[visitedViews.value.length - 1];
}
if (firstTag === currentTag) {
$scrollWrapper.scrollLeft = 0;
} else if (lastTag === currentTag) {
$scrollWrapper.scrollLeft = $scrollWrapper.scrollWidth - $containerWidth;
} else {
const tagListDom: any = document.getElementsByClassName('tags-view-item');
const currentIndex = visitedViews.value.findIndex((item) => item === currentTag);
let prevTag = null;
let nextTag = null;
for (const k in tagListDom) {
if (k !== 'length' && Object.hasOwnProperty.call(tagListDom, k)) {
if (tagListDom[k].dataset.path === visitedViews.value[currentIndex - 1].path) {
prevTag = tagListDom[k];
}
if (tagListDom[k].dataset.path === visitedViews.value[currentIndex + 1].path) {
nextTag = tagListDom[k];
}
}
} }
if (firstTag === currentTag) { // the tag's offsetLeft after of nextTag
$scrollWrapper.scrollLeft = 0 const afterNextTagOffsetLeft = nextTag.offsetLeft + nextTag.offsetWidth + tagAndTagSpacing.value;
} else if (lastTag === currentTag) {
$scrollWrapper.scrollLeft = $scrollWrapper.scrollWidth - $containerWidth
} else {
const tagListDom: any = document.getElementsByClassName('tags-view-item');
const currentIndex = visitedViews.value.findIndex(item => item === currentTag)
let prevTag = null
let nextTag = null
for (const k in tagListDom) { // the tag's offsetLeft before of prevTag
if (k !== 'length' && Object.hasOwnProperty.call(tagListDom, k)) { const beforePrevTagOffsetLeft = prevTag.offsetLeft - tagAndTagSpacing.value;
if (tagListDom[k].dataset.path === visitedViews.value[currentIndex - 1].path) { if (afterNextTagOffsetLeft > $scrollWrapper.scrollLeft + $containerWidth) {
prevTag = tagListDom[k]; $scrollWrapper.scrollLeft = afterNextTagOffsetLeft - $containerWidth;
} } else if (beforePrevTagOffsetLeft < $scrollWrapper.scrollLeft) {
if (tagListDom[k].dataset.path === visitedViews.value[currentIndex + 1].path) { $scrollWrapper.scrollLeft = beforePrevTagOffsetLeft;
nextTag = tagListDom[k];
}
}
}
// the tag's offsetLeft after of nextTag
const afterNextTagOffsetLeft = nextTag.offsetLeft + nextTag.offsetWidth + tagAndTagSpacing.value
// the tag's offsetLeft before of prevTag
const beforePrevTagOffsetLeft = prevTag.offsetLeft - tagAndTagSpacing.value
if (afterNextTagOffsetLeft > $scrollWrapper.scrollLeft + $containerWidth) {
$scrollWrapper.scrollLeft = afterNextTagOffsetLeft - $containerWidth
} else if (beforePrevTagOffsetLeft < $scrollWrapper.scrollLeft) {
$scrollWrapper.scrollLeft = beforePrevTagOffsetLeft
}
} }
} }
};
defineExpose({ defineExpose({
moveToTarget, moveToTarget
}) });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -14,36 +14,35 @@
> >
{{ tag.title }} {{ tag.title }}
<span v-if="!isAffix(tag)" @click.prevent.stop="closeSelectedTag(tag)"> <span v-if="!isAffix(tag)" @click.prevent.stop="closeSelectedTag(tag)">
<close class="el-icon-close" style="width: 1em; height: 1em;vertical-align: middle;" /> <close class="el-icon-close" style="width: 1em; height: 1em; vertical-align: middle" />
</span> </span>
</router-link> </router-link>
</scroll-pane> </scroll-pane>
<ul v-show="visible" :style="{ left: left + 'px', top: top + 'px' }" class="contextmenu"> <ul v-show="visible" :style="{ left: left + 'px', top: top + 'px' }" class="contextmenu">
<li @click="refreshSelectedTag(selectedTag)"><refresh-right style="width: 1em; height: 1em;" /> 刷新页面</li> <li @click="refreshSelectedTag(selectedTag)"><refresh-right style="width: 1em; height: 1em" /> 刷新页面</li>
<li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)"><close style="width: 1em; height: 1em;" /> 关闭当前</li> <li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)"><close style="width: 1em; height: 1em" /> 关闭当前</li>
<li @click="closeOthersTags"><circle-close style="width: 1em; height: 1em;" /> 关闭其他</li> <li @click="closeOthersTags"><circle-close style="width: 1em; height: 1em" /> 关闭其他</li>
<li v-if="!isFirstView()" @click="closeLeftTags"><back style="width: 1em; height: 1em;" /> 关闭左侧</li> <li v-if="!isFirstView()" @click="closeLeftTags"><back style="width: 1em; height: 1em" /> 关闭左侧</li>
<li v-if="!isLastView()" @click="closeRightTags"><right style="width: 1em; height: 1em;" /> 关闭右侧</li> <li v-if="!isLastView()" @click="closeRightTags"><right style="width: 1em; height: 1em" /> 关闭右侧</li>
<li @click="closeAllTags(selectedTag)"><circle-close style="width: 1em; height: 1em;" /> 全部关闭</li> <li @click="closeAllTags(selectedTag)"><circle-close style="width: 1em; height: 1em" /> 全部关闭</li>
</ul> </ul>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import ScrollPane from './ScrollPane.vue' import ScrollPane from './ScrollPane.vue';
import { getNormalPath } from '@/utils/ruoyi' import { getNormalPath } from '@/utils/ruoyi';
import useTagsViewStore from "@/store/modules/tagsView"; import useTagsViewStore from '@/store/modules/tagsView';
import useSettingsStore from '@/store/modules/settings' import useSettingsStore from '@/store/modules/settings';
import usePermissionStore from '@/store/modules/permission' import usePermissionStore from '@/store/modules/permission';
import { ComponentInternalInstance } from "vue"; import { RouteRecordRaw, TagView } from 'vue-router';
import { RouteOption, TagView, RouteLocationRaw } from "vue-router";
const visible = ref(false); const visible = ref(false);
const top = ref(0); const top = ref(0);
const left = ref(0); const left = ref(0);
const selectedTag = ref<TagView>({}); const selectedTag = ref<TagView>({});
const affixTags = ref<TagView[]>([]); const affixTags = ref<TagView[]>([]);
const scrollPaneRef = ref(ScrollPane); const scrollPaneRef = ref<InstanceType<typeof ScrollPane>>();
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const route = useRoute(); const route = useRoute();
@ -54,186 +53,186 @@ const routes = computed(() => usePermissionStore().routes);
const theme = computed(() => useSettingsStore().theme); const theme = computed(() => useSettingsStore().theme);
watch(route, () => { watch(route, () => {
addTags(); addTags();
moveToCurrentTag(); moveToCurrentTag();
}) });
watch(visible, (value) => { watch(visible, (value) => {
if (value) { if (value) {
document.body.addEventListener('click', closeMenu); document.body.addEventListener('click', closeMenu);
} else { } else {
document.body.removeEventListener('click', closeMenu); document.body.removeEventListener('click', closeMenu);
} }
}) });
const isActive = (r: TagView): boolean => { const isActive = (r: TagView): boolean => {
return r.path === route.path; return r.path === route.path;
} };
const activeStyle = (tag: TagView) => { const activeStyle = (tag: TagView) => {
if (!isActive(tag)) return {}; if (!isActive(tag)) return {};
return { return {
"background-color": theme.value, 'background-color': theme.value,
"border-color": theme.value 'border-color': theme.value
}; };
} };
const isAffix = (tag: TagView) => { const isAffix = (tag: TagView) => {
return tag.meta && tag.meta.affix; return tag.meta && tag.meta.affix;
} };
const isFirstView = () => { const isFirstView = () => {
try { try {
return selectedTag.value.fullPath === '/index' || selectedTag.value.fullPath === visitedViews.value[1].fullPath; return selectedTag.value.fullPath === '/index' || selectedTag.value.fullPath === visitedViews.value[1].fullPath;
} catch (err) { } catch (err) {
return false; return false;
} }
} };
const isLastView = () => { const isLastView = () => {
try { try {
return selectedTag.value.fullPath === visitedViews.value[visitedViews.value.length - 1].fullPath; return selectedTag.value.fullPath === visitedViews.value[visitedViews.value.length - 1].fullPath;
} catch (err) { } catch (err) {
return false; return false;
}
};
const filterAffixTags = (routes: RouteRecordRaw[], basePath = '') => {
let tags: TagView[] = [];
routes.forEach((route) => {
if (route.meta && route.meta.affix) {
const tagPath = getNormalPath(basePath + '/' + route.path);
tags.push({
fullPath: tagPath,
path: tagPath,
name: route.name as string,
meta: { ...route.meta }
});
} }
} if (route.children) {
const filterAffixTags = (routes:RouteOption [], basePath = '') => { const tempTags = filterAffixTags(route.children, route.path);
let tags:TagView[] = [] if (tempTags.length >= 1) {
routes.forEach(route => { tags = [...tags, ...tempTags];
if (route.meta && route.meta.affix) { }
const tagPath = getNormalPath(basePath + '/' + route.path); }
tags.push({ });
fullPath: tagPath, return tags;
path: tagPath, };
name: route.name,
meta: { ...route.meta }
})
}
if (route.children) {
const tempTags = filterAffixTags(route.children, route.path);
if (tempTags.length >= 1) {
tags = [...tags, ...tempTags];
}
}
})
return tags
}
const initTags = () => { const initTags = () => {
const res = filterAffixTags(routes.value); const res = filterAffixTags(routes.value as any);
affixTags.value = res; affixTags.value = res;
for (const tag of res) { for (const tag of res) {
// Must have tag name // Must have tag name
if (tag.name) { if (tag.name) {
useTagsViewStore().addVisitedView(tag); useTagsViewStore().addVisitedView(tag);
}
} }
} }
};
const addTags = () => { const addTags = () => {
const { name } = route; const { name } = route;
if(route.query.title) { if (route.query.title) {
route.meta.title = route.query.title; route.meta.title = route.query.title as string;
} }
if (name) { if (name) {
useTagsViewStore().addView(route); useTagsViewStore().addView(route as any);
if (route.meta.link) {
useTagsViewStore().addIframeView(route);
}
}
return false
}
const moveToCurrentTag = () => {
nextTick(() => {
for (const r of visitedViews.value) {
if (r.path === route.path) {
scrollPaneRef.value.moveToTarget(r);
// when query is different then update
if (r.fullPath !== route.fullPath) {
useTagsViewStore().updateVisitedView(route);
}
}
}
})
}
const refreshSelectedTag = (view: TagView) => {
proxy?.$tab.refreshPage(view);
if (route.meta.link) { if (route.meta.link) {
useTagsViewStore().delIframeView(route); useTagsViewStore().addIframeView(route as any);
} }
} }
return false;
};
const moveToCurrentTag = () => {
nextTick(() => {
for (const r of visitedViews.value) {
if (r.path === route.path) {
scrollPaneRef.value?.moveToTarget(r);
// when query is different then update
if (r.fullPath !== route.fullPath) {
useTagsViewStore().updateVisitedView(route);
}
}
}
});
};
const refreshSelectedTag = (view: TagView) => {
proxy?.$tab.refreshPage(view);
if (route.meta.link) {
useTagsViewStore().delIframeView(route as any);
}
};
const closeSelectedTag = (view: TagView) => { const closeSelectedTag = (view: TagView) => {
proxy?.$tab.closePage(view).then(({ visitedViews }: any) => { proxy?.$tab.closePage(view).then(({ visitedViews }: any) => {
if (isActive(view)) { if (isActive(view)) {
toLastView(visitedViews, view); toLastView(visitedViews, view);
} }
}) });
} };
const closeRightTags = () => { const closeRightTags = () => {
proxy?.$tab.closeRightPage(selectedTag.value).then(visitedViews => { proxy?.$tab.closeRightPage(selectedTag.value).then((visitedViews) => {
if (!visitedViews.find(i => i.fullPath === route.fullPath)) { if (!visitedViews.find((i) => i.fullPath === route.fullPath)) {
toLastView(visitedViews); toLastView(visitedViews);
} }
}) });
} };
const closeLeftTags = () => { const closeLeftTags = () => {
proxy?.$tab.closeLeftPage(selectedTag.value).then(visitedViews => { proxy?.$tab.closeLeftPage(selectedTag.value).then((visitedViews) => {
if (!visitedViews.find(i => i.fullPath === route.fullPath)) { if (!visitedViews.find((i: TagView) => i.fullPath === route.fullPath)) {
toLastView(visitedViews); toLastView(visitedViews);
} }
}) });
} };
const closeOthersTags = () => { const closeOthersTags = () => {
router.push(selectedTag.value as RouteLocationRaw).catch(() => { }); router.push(selectedTag.value).catch(() => {});
proxy?.$tab.closeOtherPage(selectedTag.value).then(() => { proxy?.$tab.closeOtherPage(selectedTag.value).then(() => {
moveToCurrentTag(); moveToCurrentTag();
}) });
} };
const closeAllTags = (view: TagView) => { const closeAllTags = (view: TagView) => {
proxy?.$tab.closeAllPage().then(({ visitedViews }) => { proxy?.$tab.closeAllPage().then(({ visitedViews }) => {
if (affixTags.value.some(tag => tag.path === route.path)) { if (affixTags.value.some((tag) => tag.path === route.path)) {
return; return;
}
toLastView(visitedViews, view);
})
}
const toLastView = (visitedViews:TagView[], view?: TagView) => {
const latestView = visitedViews.slice(-1)[0];
if (latestView) {
router.push(latestView.fullPath as string);
} else {
// now the default is to redirect to the home page if there is no tags-view,
// you can adjust it according to your needs.
if (view?.name === 'Dashboard') {
// to reload home page
router.replace({ path: '/redirect' + view?.fullPath });
} else {
router.push('/');
}
} }
} toLastView(visitedViews, view);
});
};
const toLastView = (visitedViews: TagView[], view?: TagView) => {
const latestView = visitedViews.slice(-1)[0];
if (latestView) {
router.push(latestView.fullPath as string);
} else {
// now the default is to redirect to the home page if there is no tags-view,
// you can adjust it according to your needs.
if (view?.name === 'Dashboard') {
// to reload home page
router.replace({ path: '/redirect' + view?.fullPath });
} else {
router.push('/');
}
}
};
const openMenu = (tag: TagView, e: MouseEvent) => { const openMenu = (tag: TagView, e: MouseEvent) => {
const menuMinWidth = 105; const menuMinWidth = 105;
const offsetLeft = proxy?.$el.getBoundingClientRect().left; // container margin left const offsetLeft = proxy?.$el.getBoundingClientRect().left; // container margin left
const offsetWidth = proxy?.$el.offsetWidth; // container width const offsetWidth = proxy?.$el.offsetWidth; // container width
const maxLeft = offsetWidth - menuMinWidth; // left boundary const maxLeft = offsetWidth - menuMinWidth; // left boundary
const l = e.clientX - offsetLeft + 15; // 15: margin right const l = e.clientX - offsetLeft + 15; // 15: margin right
if (l > maxLeft) { if (l > maxLeft) {
left.value = maxLeft; left.value = maxLeft;
} else { } else {
left.value = l; left.value = l;
} }
top.value = e.clientY top.value = e.clientY;
visible.value = true; visible.value = true;
selectedTag.value = tag; selectedTag.value = tag;
} };
const closeMenu = () => { const closeMenu = () => {
visible.value = false; visible.value = false;
} };
const handleScroll = () => { const handleScroll = () => {
closeMenu(); closeMenu();
} };
onMounted(() => { onMounted(() => {
initTags(); initTags();
addTags(); addTags();
}) });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -242,7 +241,9 @@ onMounted(() => {
width: 100%; width: 100%;
background-color: var(--el-bg-color); background-color: var(--el-bg-color);
border: 1px solid var(--el-border-color-light); border: 1px solid var(--el-border-color-light);
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 0 3px 0 rgba(0, 0, 0, 0.04); box-shadow:
0 1px 3px 0 rgba(0, 0, 0, 0.12),
0 0 3px 0 rgba(0, 0, 0, 0.04);
.tags-view-wrapper { .tags-view-wrapper {
.tags-view-item { .tags-view-item {
display: inline-block; display: inline-block;
@ -271,7 +272,7 @@ onMounted(() => {
color: #fff; color: #fff;
border-color: #42b983; border-color: #42b983;
&::before { &::before {
content: ""; content: '';
background: #fff; background: #fff;
display: inline-block; display: inline-block;
width: 8px; width: 8px;

View File

@ -3,12 +3,12 @@
<el-dialog v-model="state.isShowSearch" destroy-on-close :show-close="false"> <el-dialog v-model="state.isShowSearch" destroy-on-close :show-close="false">
<template #footer> <template #footer>
<el-autocomplete <el-autocomplete
ref="layoutMenuAutocompleteRef"
v-model="state.menuQuery" v-model="state.menuQuery"
:fetch-suggestions="menuSearch" :fetch-suggestions="menuSearch"
placeholder="搜索" placeholder="搜索"
ref="layoutMenuAutocompleteRef"
@select="onHandleSelect"
:fit-input-width="true" :fit-input-width="true"
@select="onHandleSelect"
> >
<template #prefix> <template #prefix>
<svg-icon class-name="search-icon" icon-class="search" /> <svg-icon class-name="search-icon" icon-class="search" />
@ -29,130 +29,129 @@
import { getNormalPath } from '@/utils/ruoyi'; import { getNormalPath } from '@/utils/ruoyi';
import { isHttp } from '@/utils/validate'; import { isHttp } from '@/utils/validate';
import usePermissionStore from '@/store/modules/permission'; import usePermissionStore from '@/store/modules/permission';
import { RouteOption } from 'vue-router'; import { RouteRecordRaw } from 'vue-router';
type Router = Array<{ type Router = Array<{
path: string; path: string;
icon: string; icon: string;
title: string[]; title: string[];
}> }>;
type SearchState<T = any> = { type SearchState<T = any> = {
isShowSearch: boolean; isShowSearch: boolean;
menuQuery: string; menuQuery: string;
menuList: T[]; menuList: T[];
}; };
// 定义变量内容 // 定义变量内容
const layoutMenuAutocompleteRef = ref(); const layoutMenuAutocompleteRef = ref();
const router = useRouter(); const router = useRouter();
const routes = computed(() => usePermissionStore().routes); const routes = computed(() => usePermissionStore().routes);
const state = reactive<SearchState>({ const state = reactive<SearchState>({
isShowSearch: false, isShowSearch: false,
menuQuery: '', menuQuery: '',
menuList: [], menuList: []
}); });
// 搜索弹窗打开 // 搜索弹窗打开
const openSearch = () => { const openSearch = () => {
state.menuQuery = ''; state.menuQuery = '';
state.isShowSearch = true; state.isShowSearch = true;
state.menuList = generateRoutes(routes.value); state.menuList = generateRoutes(routes.value);
nextTick(() => { nextTick(() => {
setTimeout(() => { setTimeout(() => {
layoutMenuAutocompleteRef.value.focus(); layoutMenuAutocompleteRef.value.focus();
}); });
}); });
}; };
// 搜索弹窗关闭 // 搜索弹窗关闭
const closeSearch = () => { const closeSearch = () => {
state.isShowSearch = false; state.isShowSearch = false;
}; };
// 菜单搜索数据过滤 // 菜单搜索数据过滤
const menuSearch = (queryString: string, cb: Function) => { const menuSearch = (queryString: string, cb: Function) => {
let options = state.menuList.filter((item) => { let options = state.menuList.filter((item) => {
return item.title.indexOf(queryString) > -1; return item.title.indexOf(queryString) > -1;
}); });
cb(options); cb(options);
}; };
// Filter out the routes that can be displayed in the sidebar // Filter out the routes that can be displayed in the sidebar
// And generate the internationalized title // And generate the internationalized title
const generateRoutes = (routes: RouteOption[], basePath = '', prefixTitle: string[] = []) => { const generateRoutes = (routes: RouteRecordRaw[], basePath = '', prefixTitle: string[] = []) => {
let res: Router = [] let res: Router = [];
routes.forEach(r => { routes.forEach((r) => {
// skip hidden router // skip hidden router
if (!r.hidden) { if (!r.hidden) {
const p = r.path.length > 0 && r.path[0] === '/' ? r.path : '/' + r.path; const p = r.path.length > 0 && r.path[0] === '/' ? r.path : '/' + r.path;
const data: any = { const data: any = {
path: !isHttp(r.path) ? getNormalPath(basePath + p) : r.path, path: !isHttp(r.path) ? getNormalPath(basePath + p) : r.path,
icon: r.meta?.icon, icon: r.meta?.icon,
title: [...prefixTitle] title: [...prefixTitle]
} };
if (r.meta && r.meta.title) { if (r.meta && r.meta.title) {
data.title = [...data.title, r.meta.title]; data.title = [...data.title, r.meta.title];
if (r.redirect !== 'noRedirect') { if (r.redirect !== 'noRedirect') {
// only push the routes with title // only push the routes with title
// special case: need to exclude parent router without redirect // special case: need to exclude parent router without redirect
res.push(data); res.push(data);
} }
} }
// recursive child routes // recursive child routes
if (r.children) { if (r.children) {
const tempRoutes = generateRoutes(r.children, data.path, data.title); const tempRoutes = generateRoutes(r.children, data.path, data.title);
if (tempRoutes.length >= 1) { if (tempRoutes.length >= 1) {
res = [...res, ...tempRoutes]; res = [...res, ...tempRoutes];
} }
} }
} }
}) });
res.forEach((item: any) => { res.forEach((item: any) => {
if (item.title instanceof Array) { if (item.title instanceof Array) {
item.title = item.title.join('/'); item.title = item.title.join('/');
} }
}); });
return res; return res;
} };
// 当前菜单选中时 // 当前菜单选中时
const onHandleSelect = (val: any) => { const onHandleSelect = (val: any) => {
const paths = val.path; const paths = val.path;
if (isHttp(paths)) { if (isHttp(paths)) {
// http(s):// 路径新窗口打开 // http(s):// 路径新窗口打开
const pindex = paths.indexOf("http"); const pindex = paths.indexOf('http');
window.open(paths.substring(pindex, paths.length), "_blank"); window.open(paths.substring(pindex, paths.length), '_blank');
} else { } else {
router.push(paths); router.push(paths);
} }
state.menuQuery = '' state.menuQuery = '';
closeSearch(); closeSearch();
}; };
// 暴露变量 // 暴露变量
defineExpose({ defineExpose({
openSearch openSearch
}); });
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.layout-search-dialog { .layout-search-dialog {
position: relative; position: relative;
:deep(.el-dialog) { :deep(.el-dialog) {
.el-dialog__header, .el-dialog__header,
.el-dialog__body { .el-dialog__body {
display: none; display: none;
} }
.el-dialog__footer { .el-dialog__footer {
width: 100%; width: 100%;
position: absolute; position: absolute;
left: 50%; left: 50%;
transform: translateX(-50%); transform: translateX(-50%);
top: -53vh; top: -53vh;
} }
} }
:deep(.el-autocomplete) { :deep(.el-autocomplete) {
width: 560px; width: 560px;
position: absolute; position: absolute;
top: 150px; top: 150px;
left: 50%; left: 50%;
transform: translateX(-50%); transform: translateX(-50%);
} }
} }
</style> </style>

View File

@ -1,12 +1,12 @@
<template> <template>
<div class="layout-navbars-breadcrumb-user-news" v-loading="state.loading"> <div v-loading="state.loading" class="layout-navbars-breadcrumb-user-news">
<div class="head-box"> <div class="head-box">
<div class="head-box-title">通知公告</div> <div class="head-box-title">通知公告</div>
<div class="head-box-btn" @click="readAll">全部已读</div> <div class="head-box-btn" @click="readAll">全部已读</div>
</div> </div>
<div class="content-box" v-loading="state.loading"> <div v-loading="state.loading" class="content-box">
<template v-if="newsList.length > 0"> <template v-if="newsList.length > 0">
<div class="content-box-item" v-for="(v, k) in newsList" :key="k" @click="onNewsClick(k)"> <div v-for="(v, k) in newsList" :key="k" class="content-box-item" @click="onNewsClick(k)">
<div class="item-conten"> <div class="item-conten">
<div>{{ v.message }}</div> <div>{{ v.message }}</div>
<div class="content-box-msg"></div> <div class="content-box-msg"></div>
@ -17,26 +17,24 @@
<span v-else class="el-tag el-tag--danger el-tag--mini read">未读</span> <span v-else class="el-tag el-tag--danger el-tag--mini read">未读</span>
</div> </div>
</template> </template>
<el-empty :description="'消息为空'" v-else></el-empty> <el-empty v-else :description="'消息为空'"></el-empty>
</div> </div>
<div class="foot-box" @click="onGoToGiteeClick" v-if="newsList.length > 0">前往gitee</div> <div v-if="newsList.length > 0" class="foot-box" @click="onGoToGiteeClick">前往gitee</div>
</div> </div>
</template> </template>
<script setup lang="ts" name="layoutBreadcrumbUserNews"> <script setup lang="ts" name="layoutBreadcrumbUserNews">
import { ref } from "vue"; import { storeToRefs } from 'pinia';
import { storeToRefs } from 'pinia'
import { nextTick, onMounted, reactive } from "vue";
import useNoticeStore from '@/store/modules/notice'; import useNoticeStore from '@/store/modules/notice';
const noticeStore = storeToRefs(useNoticeStore()); const noticeStore = storeToRefs(useNoticeStore());
const {readAll} = useNoticeStore(); const { readAll } = useNoticeStore();
// 定义变量内容 // 定义变量内容
const state = reactive({ const state = reactive({
loading: false, loading: false
}); });
const newsList =ref([]) as any; const newsList = ref([]) as any;
/** /**
* 初始化数据 * 初始化数据
@ -48,7 +46,6 @@ const getTableData = async () => {
state.loading = false; state.loading = false;
}; };
//点击消息,写入已读 //点击消息,写入已读
const onNewsClick = (item: any) => { const onNewsClick = (item: any) => {
newsList.value[item].read = true; newsList.value[item].read = true;
@ -58,7 +55,7 @@ const onNewsClick = (item: any) => {
// 前往通知中心点击 // 前往通知中心点击
const onGoToGiteeClick = () => { const onGoToGiteeClick = () => {
window.open("https://gitee.com/dromara/RuoYi-Vue-Plus/tree/5.X/"); window.open('https://gitee.com/dromara/RuoYi-Vue-Plus/tree/5.X/');
}; };
onMounted(() => { onMounted(() => {

View File

@ -12,7 +12,7 @@
<settings ref="settingRef" /> <settings ref="settingRef" />
</el-scrollbar> --> </el-scrollbar> -->
<div :class="{ 'fixed-header': fixedHeader }"> <div :class="{ 'fixed-header': fixedHeader }">
<navbar ref="navbarRef" @setLayout="setLayout" /> <navbar ref="navbarRef" @set-layout="setLayout" />
<tags-view v-if="needTagsView" /> <tags-view v-if="needTagsView" />
</div> </div>
<app-main /> <app-main />
@ -22,12 +22,12 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import SideBar from './components/Sidebar/index.vue' import SideBar from './components/Sidebar/index.vue';
import { AppMain, Navbar, Settings, TagsView } from './components' import { AppMain, Navbar, Settings, TagsView } from './components';
import useAppStore from '@/store/modules/app' import useAppStore from '@/store/modules/app';
import useSettingsStore from '@/store/modules/settings' import useSettingsStore from '@/store/modules/settings';
const settingsStore = useSettingsStore() const settingsStore = useSettingsStore();
const theme = computed(() => settingsStore.theme); const theme = computed(() => settingsStore.theme);
const sidebar = computed(() => useAppStore().sidebar); const sidebar = computed(() => useAppStore().sidebar);
const device = computed(() => useAppStore().device); const device = computed(() => useAppStore().device);
@ -35,48 +35,48 @@ const needTagsView = computed(() => settingsStore.tagsView);
const fixedHeader = computed(() => settingsStore.fixedHeader); const fixedHeader = computed(() => settingsStore.fixedHeader);
const classObj = computed(() => ({ const classObj = computed(() => ({
hideSidebar: !sidebar.value.opened, hideSidebar: !sidebar.value.opened,
openSidebar: sidebar.value.opened, openSidebar: sidebar.value.opened,
withoutAnimation: sidebar.value.withoutAnimation, withoutAnimation: sidebar.value.withoutAnimation,
mobile: device.value === 'mobile' mobile: device.value === 'mobile'
})) }));
const { width } = useWindowSize(); const { width } = useWindowSize();
const WIDTH = 992; // refer to Bootstrap's responsive design const WIDTH = 992; // refer to Bootstrap's responsive design
watchEffect(() => { watchEffect(() => {
if (device.value === 'mobile' && sidebar.value.opened) { if (device.value === 'mobile' && sidebar.value.opened) {
useAppStore().closeSideBar({ withoutAnimation: false }) useAppStore().closeSideBar({ withoutAnimation: false });
} }
if (width.value - 1 < WIDTH) { if (width.value - 1 < WIDTH) {
useAppStore().toggleDevice('mobile') useAppStore().toggleDevice('mobile');
useAppStore().closeSideBar({ withoutAnimation: true }) useAppStore().closeSideBar({ withoutAnimation: true });
} else { } else {
useAppStore().toggleDevice('desktop') useAppStore().toggleDevice('desktop');
} }
}) });
const navbarRef = ref(Navbar); const navbarRef = ref<InstanceType<typeof Navbar>>();
const settingRef = ref(Settings); const settingRef = ref<InstanceType<typeof Settings>>();
onMounted(() => { onMounted(() => {
nextTick(() => { nextTick(() => {
navbarRef.value.initTenantList(); navbarRef.value?.initTenantList();
}) });
}) });
const handleClickOutside = () => { const handleClickOutside = () => {
useAppStore().closeSideBar({ withoutAnimation: false }) useAppStore().closeSideBar({ withoutAnimation: false });
} };
const setLayout = () => { const setLayout = () => {
settingRef.value.openSetting(); settingRef.value?.openSetting();
} };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import "@/assets/styles/mixin.scss"; @import '@/assets/styles/mixin.scss';
@import "@/assets/styles/variables.module.scss"; @import '@/assets/styles/variables.module.scss';
.app-wrapper { .app-wrapper {
@include clearfix; @include clearfix;

View File

@ -8,6 +8,7 @@ import { isRelogin } from '@/utils/request';
import useUserStore from '@/store/modules/user'; import useUserStore from '@/store/modules/user';
import useSettingsStore from '@/store/modules/settings'; import useSettingsStore from '@/store/modules/settings';
import usePermissionStore from '@/store/modules/permission'; import usePermissionStore from '@/store/modules/permission';
import { RouteRecordRaw } from 'vue-router';
NProgress.configure({ showSpinner: false }); NProgress.configure({ showSpinner: false });
const whiteList = ['/login', '/register', '/social-callback']; const whiteList = ['/login', '/register', '/social-callback'];
@ -35,7 +36,7 @@ router.beforeEach(async (to, from, next) => {
isRelogin.show = false; isRelogin.show = false;
const accessRoutes = await usePermissionStore().generateRoutes(); const accessRoutes = await usePermissionStore().generateRoutes();
// 根据roles权限生成可访问的路由表 // 根据roles权限生成可访问的路由表
accessRoutes.forEach((route) => { accessRoutes.forEach((route: RouteRecordRaw) => {
if (!isHttp(route.path)) { if (!isHttp(route.path)) {
router.addRoute(route); // 动态添加可访问路由表 router.addRoute(route); // 动态添加可访问路由表
} }
@ -48,7 +49,7 @@ router.beforeEach(async (to, from, next) => {
} }
} else { } else {
// 没有token // 没有token
if (whiteList.indexOf(to.path) !== -1) { if (whiteList.indexOf(to.path as string) !== -1) {
// 在免登录白名单,直接进入 // 在免登录白名单,直接进入
next(); next();
} else { } else {

View File

@ -1,6 +1,6 @@
import { useTagsViewStore } from '@/store/modules/tagsView'; import { useTagsViewStore } from '@/store/modules/tagsView';
import router from '@/router'; import router from '@/router';
import { TagView, RouteLocationRaw } from 'vue-router'; import { TagView, RouteLocationMatched } from 'vue-router';
export default { export default {
/** /**
@ -10,7 +10,7 @@ export default {
async refreshPage(obj?: TagView): Promise<void> { async refreshPage(obj?: TagView): Promise<void> {
const { path, query, matched } = router.currentRoute.value; const { path, query, matched } = router.currentRoute.value;
if (obj === undefined) { if (obj === undefined) {
matched.forEach((m) => { matched.forEach((m: RouteLocationMatched) => {
if (m.components && m.components.default && m.components.default.name) { if (m.components && m.components.default && m.components.default.name) {
if (!['Layout', 'ParentView'].includes(m.components.default.name)) { if (!['Layout', 'ParentView'].includes(m.components.default.name)) {
obj = { name: m.components.default.name, path: path, query: query }; obj = { name: m.components.default.name, path: path, query: query };
@ -31,8 +31,8 @@ export default {
}); });
}, },
// 关闭当前tab页签打开新页签 // 关闭当前tab页签打开新页签
closeOpenPage(obj: RouteLocationRaw): void { closeOpenPage(obj: TagView): void {
useTagsViewStore().delView(router.currentRoute.value); useTagsViewStore().delView(router.currentRoute.value as any);
if (obj !== undefined) { if (obj !== undefined) {
router.push(obj); router.push(obj);
} }
@ -41,10 +41,10 @@ export default {
async closePage(obj?: TagView): Promise<{ visitedViews: TagView[]; cachedViews: string[] } | any> { async closePage(obj?: TagView): Promise<{ visitedViews: TagView[]; cachedViews: string[] } | any> {
if (obj === undefined) { if (obj === undefined) {
// prettier-ignore // prettier-ignore
const { visitedViews } = await useTagsViewStore().delView(router.currentRoute.value) as any const { visitedViews } = await useTagsViewStore().delView(router.currentRoute.value as any)
const latestView = visitedViews.slice(-1)[0]; const latestView = visitedViews.slice(-1)[0];
if (latestView) { if (latestView) {
return router.push(latestView.fullPath); return router.push(latestView.fullPath as any);
} }
return router.push('/'); return router.push('/');
} }
@ -56,15 +56,15 @@ export default {
}, },
// 关闭左侧tab页签 // 关闭左侧tab页签
closeLeftPage(obj?: TagView) { closeLeftPage(obj?: TagView) {
return useTagsViewStore().delLeftTags(obj || router.currentRoute.value); return useTagsViewStore().delLeftTags(obj || (router.currentRoute.value as any));
}, },
// 关闭右侧tab页签 // 关闭右侧tab页签
closeRightPage(obj?: TagView) { closeRightPage(obj?: TagView) {
return useTagsViewStore().delRightTags(obj || router.currentRoute.value); return useTagsViewStore().delRightTags(obj || (router.currentRoute.value as any));
}, },
// 关闭其他tab页签 // 关闭其他tab页签
closeOtherPage(obj?: TagView) { closeOtherPage(obj?: TagView) {
return useTagsViewStore().delOthersViews(obj || router.currentRoute.value); return useTagsViewStore().delOthersViews(obj || (router.currentRoute.value as any));
}, },
/** /**
* 打开tab页签 * 打开tab页签

View File

@ -1,4 +1,4 @@
import { createWebHistory, createRouter, RouteOption } from 'vue-router'; import { createWebHistory, createRouter, RouteRecordRaw } from 'vue-router';
/* Layout */ /* Layout */
import Layout from '@/layout/index.vue'; import Layout from '@/layout/index.vue';
@ -25,7 +25,7 @@ import Layout from '@/layout/index.vue';
*/ */
// 公共路由 // 公共路由
export const constantRoutes: RouteOption[] = [ export const constantRoutes: RouteRecordRaw[] = [
{ {
path: '/redirect', path: '/redirect',
component: Layout, component: Layout,
@ -92,7 +92,7 @@ export const constantRoutes: RouteOption[] = [
]; ];
// 动态路由,基于用户权限动态去加载 // 动态路由,基于用户权限动态去加载
export const dynamicRoutes: RouteOption[] = [ export const dynamicRoutes: RouteRecordRaw[] = [
{ {
path: '/system/user-auth', path: '/system/user-auth',
component: Layout, component: Layout,

View File

@ -22,7 +22,7 @@ export const useNoticeStore = defineStore('notice', () => {
//实现全部已读 //实现全部已读
const readAll = () => { const readAll = () => {
state.notices.forEach((item) => { state.notices.forEach((item: any) => {
item.read = true; item.read = true;
}); });
}; };

View File

@ -2,35 +2,36 @@ import { defineStore } from 'pinia';
import router, { constantRoutes, dynamicRoutes } from '@/router'; import router, { constantRoutes, dynamicRoutes } from '@/router';
import store from '@/store'; import store from '@/store';
import { getRouters } from '@/api/menu'; import { getRouters } from '@/api/menu';
import auth from '@/plugins/auth';
import { RouteRecordRaw } from 'vue-router';
import Layout from '@/layout/index.vue'; import Layout from '@/layout/index.vue';
import ParentView from '@/components/ParentView/index.vue'; import ParentView from '@/components/ParentView/index.vue';
import InnerLink from '@/layout/components/InnerLink/index.vue'; import InnerLink from '@/layout/components/InnerLink/index.vue';
import auth from '@/plugins/auth';
import { RouteOption } from 'vue-router';
// 匹配views里面所有的.vue文件 // 匹配views里面所有的.vue文件
const modules = import.meta.glob('./../../views/**/*.vue'); const modules = import.meta.glob('./../../views/**/*.vue');
export const usePermissionStore = defineStore('permission', () => { export const usePermissionStore = defineStore('permission', () => {
const routes = ref<RouteOption[]>([]); const routes = ref<RouteRecordRaw[]>([]);
const addRoutes = ref<RouteOption[]>([]); const addRoutes = ref<RouteRecordRaw[]>([]);
const defaultRoutes = ref<RouteOption[]>([]); const defaultRoutes = ref<RouteRecordRaw[]>([]);
const topbarRouters = ref<RouteOption[]>([]); const topbarRouters = ref<RouteRecordRaw[]>([]);
const sidebarRouters = ref<RouteOption[]>([]); const sidebarRouters = ref<RouteRecordRaw[]>([]);
const setRoutes = (newRoutes: RouteOption[]): void => { const setRoutes = (newRoutes: RouteRecordRaw[]): void => {
addRoutes.value = newRoutes; addRoutes.value = newRoutes;
routes.value = constantRoutes.concat(newRoutes); routes.value = constantRoutes.concat(newRoutes);
}; };
const setDefaultRoutes = (routes: RouteOption[]): void => { const setDefaultRoutes = (routes: RouteRecordRaw[]): void => {
defaultRoutes.value = constantRoutes.concat(routes); defaultRoutes.value = constantRoutes.concat(routes);
}; };
const setTopbarRoutes = (routes: RouteOption[]): void => { const setTopbarRoutes = (routes: RouteRecordRaw[]): void => {
topbarRouters.value = routes; topbarRouters.value = routes;
}; };
const setSidebarRouters = (routes: RouteOption[]): void => { const setSidebarRouters = (routes: RouteRecordRaw[]): void => {
sidebarRouters.value = routes; sidebarRouters.value = routes;
}; };
const generateRoutes = async (): Promise<RouteOption[]> => { const generateRoutes = async (): Promise<RouteRecordRaw[]> => {
const res = await getRouters(); const res = await getRouters();
const { data } = res; const { data } = res;
const sdata = JSON.parse(JSON.stringify(data)); const sdata = JSON.parse(JSON.stringify(data));
@ -47,7 +48,7 @@ export const usePermissionStore = defineStore('permission', () => {
setSidebarRouters(constantRoutes.concat(sidebarRoutes)); setSidebarRouters(constantRoutes.concat(sidebarRoutes));
setDefaultRoutes(sidebarRoutes); setDefaultRoutes(sidebarRoutes);
setTopbarRoutes(defaultRoutes); setTopbarRoutes(defaultRoutes);
return new Promise<RouteOption[]>((resolve) => resolve(rewriteRoutes)); return new Promise<RouteRecordRaw[]>((resolve) => resolve(rewriteRoutes));
}; };
/** /**
@ -56,22 +57,20 @@ export const usePermissionStore = defineStore('permission', () => {
* @param lastRouter 上一级路由 * @param lastRouter 上一级路由
* @param type 是否是重写路由 * @param type 是否是重写路由
*/ */
const filterAsyncRouter = (asyncRouterMap: RouteOption[], lastRouter?: RouteOption, type = false): RouteOption[] => { const filterAsyncRouter = (asyncRouterMap: RouteRecordRaw[], lastRouter?: RouteRecordRaw, type = false): RouteRecordRaw[] => {
return asyncRouterMap.filter((route) => { return asyncRouterMap.filter((route) => {
if (type && route.children) { if (type && route.children) {
route.children = filterChildren(route.children, undefined); route.children = filterChildren(route.children, undefined);
} }
if (route.component) { // Layout ParentView 组件特殊处理
// Layout ParentView 组件特殊处理 if (route.component?.toString() === 'Layout') {
if (route.component === 'Layout') { route.component = Layout;
route.component = Layout; } else if (route.component?.toString() === 'ParentView') {
} else if (route.component === 'ParentView') { route.component = ParentView;
route.component = ParentView; } else if (route.component?.toString() === 'InnerLink') {
} else if (route.component === 'InnerLink') { route.component = InnerLink;
route.component = InnerLink; } else {
} else { route.component = loadView(route.component);
route.component = loadView(route.component);
}
} }
if (route.children != null && route.children && route.children.length) { if (route.children != null && route.children && route.children.length) {
route.children = filterAsyncRouter(route.children, route, type); route.children = filterAsyncRouter(route.children, route, type);
@ -82,11 +81,11 @@ export const usePermissionStore = defineStore('permission', () => {
return true; return true;
}); });
}; };
const filterChildren = (childrenMap: RouteOption[], lastRouter?: RouteOption): RouteOption[] => { const filterChildren = (childrenMap: RouteRecordRaw[], lastRouter?: RouteRecordRaw): RouteRecordRaw[] => {
let children: RouteOption[] = []; let children: RouteRecordRaw[] = [];
childrenMap.forEach((el) => { childrenMap.forEach((el) => {
if (el.children && el.children.length) { if (el.children && el.children.length) {
if (el.component === 'ParentView' && !lastRouter) { if (el.component?.toString() === 'ParentView' && !lastRouter) {
el.children.forEach((c) => { el.children.forEach((c) => {
c.path = el.path + '/' + c.path; c.path = el.path + '/' + c.path;
if (c.children && c.children.length) { if (c.children && c.children.length) {
@ -101,8 +100,8 @@ export const usePermissionStore = defineStore('permission', () => {
if (lastRouter) { if (lastRouter) {
el.path = lastRouter.path + '/' + el.path; el.path = lastRouter.path + '/' + el.path;
if (el.children && el.children.length) { if (el.children && el.children.length) {
children = children.concat(filterChildren(el.children, el)) children = children.concat(filterChildren(el.children, el));
return return;
} }
} }
children = children.concat(el); children = children.concat(el);
@ -113,8 +112,8 @@ export const usePermissionStore = defineStore('permission', () => {
}); });
// 动态路由遍历,验证是否具备权限 // 动态路由遍历,验证是否具备权限
export const filterDynamicRoutes = (routes: RouteOption[]) => { export const filterDynamicRoutes = (routes: RouteRecordRaw[]) => {
const res: RouteOption[] = []; const res: RouteRecordRaw[] = [];
routes.forEach((route) => { routes.forEach((route) => {
if (route.permissions) { if (route.permissions) {
if (auth.hasPermiOr(route.permissions)) { if (auth.hasPermiOr(route.permissions)) {

View File

@ -2,27 +2,35 @@ import { defineStore } from 'pinia';
import defaultSettings from '@/settings'; import defaultSettings from '@/settings';
import { SettingTypeEnum } from '@/enums/SettingTypeEnum'; import { SettingTypeEnum } from '@/enums/SettingTypeEnum';
import { useDynamicTitle } from '@/utils/dynamicTitle'; import { useDynamicTitle } from '@/utils/dynamicTitle';
import { Ref } from 'vue';
export const useSettingsStore = defineStore('setting', () => { export const useSettingsStore = defineStore('setting', () => {
const storageSetting = JSON.parse(localStorage.getItem('layout-setting') || '{}'); const storageSetting = JSON.parse(localStorage.getItem('layout-setting') || '{}');
const title = ref<string>(defaultSettings.title);
const theme = ref<string>(storageSetting.theme || defaultSettings.theme);
const sideTheme = ref<string>(storageSetting.sideTheme || defaultSettings.sideTheme);
const showSettings = ref<boolean>(storageSetting.showSettings || defaultSettings.showSettings);
const topNav = ref<boolean>(storageSetting.topNav === undefined ? defaultSettings.topNav : storageSetting.topNav);
const tagsView = ref<boolean>(storageSetting.tagsView === undefined ? defaultSettings.tagsView : storageSetting.tagsView);
const fixedHeader = ref<boolean>(storageSetting.fixedHeader === undefined ? defaultSettings.fixedHeader : storageSetting.fixedHeader);
const sidebarLogo = ref<boolean>(storageSetting.sidebarLogo === undefined ? defaultSettings.sidebarLogo : storageSetting.sidebarLogo);
const dynamicTitle = ref<boolean>(storageSetting.dynamicTitle === undefined ? defaultSettings.dynamicTitle : storageSetting.dynamicTitle);
const animationEnable = ref<boolean>(
storageSetting.animationEnable === undefined ? defaultSettings.animationEnable : storageSetting.animationEnable
);
const dark = ref<boolean>(storageSetting.dark || defaultSettings.dark);
const prop: { [key: string]: Ref<any> } = { const prop: { [key: string]: Ref<any> } = {
title: ref<string>(''), theme,
theme: ref<string>(storageSetting.theme || defaultSettings.theme), sideTheme,
sideTheme: ref<string>(storageSetting.sideTheme || defaultSettings.sideTheme), showSettings,
showSettings: ref<boolean>(storageSetting.showSettings || defaultSettings.showSettings), topNav,
topNav: ref<boolean>(storageSetting.topNav === undefined ? defaultSettings.topNav : storageSetting.topNav), tagsView,
tagsView: ref<boolean>(storageSetting.tagsView === undefined ? defaultSettings.tagsView : storageSetting.tagsView), fixedHeader,
fixedHeader: ref<boolean>(storageSetting.fixedHeader === undefined ? defaultSettings.fixedHeader : storageSetting.fixedHeader), sidebarLogo,
sidebarLogo: ref<boolean>(storageSetting.sidebarLogo === undefined ? defaultSettings.sidebarLogo : storageSetting.sidebarLogo), dynamicTitle,
dynamicTitle: ref<boolean>(storageSetting.dynamicTitle === undefined ? defaultSettings.dynamicTitle : storageSetting.dynamicTitle), animationEnable,
animationEnable: ref<boolean>(storageSetting.animationEnable === undefined ? defaultSettings.animationEnable : storageSetting.animationEnable), dark
dark: ref<boolean>(storageSetting.dark || defaultSettings.dark)
}; };
const { title, theme, sideTheme, showSettings, topNav, tagsView, fixedHeader, sidebarLogo, dynamicTitle, animationEnable, dark } = prop;
// actions // actions
const changeSetting = (param: { key: SettingTypeEnum; value: any }) => { const changeSetting = (param: { key: SettingTypeEnum; value: any }) => {
const { key, value } = param; const { key, value } = param;

View File

@ -11,7 +11,7 @@ export const useTagsViewStore = defineStore('tagsView', () => {
}; };
const addIframeView = (view: TagView): void => { const addIframeView = (view: TagView): void => {
if (iframeViews.value.some((v) => v.path === view.path)) return; if (iframeViews.value.some((v: TagView) => v.path === view.path)) return;
iframeViews.value.push( iframeViews.value.push(
Object.assign({}, view, { Object.assign({}, view, {
title: view.meta?.title || 'no-name' title: view.meta?.title || 'no-name'
@ -20,12 +20,12 @@ export const useTagsViewStore = defineStore('tagsView', () => {
}; };
const delIframeView = (view: TagView): Promise<TagView[]> => { const delIframeView = (view: TagView): Promise<TagView[]> => {
return new Promise((resolve) => { return new Promise((resolve) => {
iframeViews.value = iframeViews.value.filter((item) => item.path !== view.path); iframeViews.value = iframeViews.value.filter((item: TagView) => item.path !== view.path);
resolve([...iframeViews.value]); resolve([...iframeViews.value]);
}); });
}; };
const addVisitedView = (view: TagView): void => { const addVisitedView = (view: TagView): void => {
if (visitedViews.value.some((v) => v.path === view.path)) return; if (visitedViews.value.some((v: TagView) => v.path === view.path)) return;
visitedViews.value.push( visitedViews.value.push(
Object.assign({}, view, { Object.assign({}, view, {
title: view.meta?.title || 'no-name' title: view.meta?.title || 'no-name'
@ -80,7 +80,7 @@ export const useTagsViewStore = defineStore('tagsView', () => {
const delOthersVisitedViews = (view: TagView): Promise<TagView[]> => { const delOthersVisitedViews = (view: TagView): Promise<TagView[]> => {
return new Promise((resolve) => { return new Promise((resolve) => {
visitedViews.value = visitedViews.value.filter((v) => { visitedViews.value = visitedViews.value.filter((v: TagView) => {
return v.meta?.affix || v.path === view.path; return v.meta?.affix || v.path === view.path;
}); });
resolve([...visitedViews.value]); resolve([...visitedViews.value]);
@ -111,7 +111,7 @@ export const useTagsViewStore = defineStore('tagsView', () => {
}; };
const delAllVisitedViews = (): Promise<TagView[]> => { const delAllVisitedViews = (): Promise<TagView[]> => {
return new Promise((resolve) => { return new Promise((resolve) => {
visitedViews.value = visitedViews.value.filter((tag) => tag.meta?.affix); visitedViews.value = visitedViews.value.filter((tag: TagView) => tag.meta?.affix);
resolve([...visitedViews.value]); resolve([...visitedViews.value]);
}); });
}; };
@ -123,7 +123,7 @@ export const useTagsViewStore = defineStore('tagsView', () => {
}); });
}; };
const updateVisitedView = (view: TagView): void => { const updateVisitedView = (view: TagView | RouteLocationNormalized): void => {
for (let v of visitedViews.value) { for (let v of visitedViews.value) {
if (v.path === view.path) { if (v.path === view.path) {
v = Object.assign(v, view); v = Object.assign(v, view);
@ -133,11 +133,11 @@ export const useTagsViewStore = defineStore('tagsView', () => {
}; };
const delRightTags = (view: TagView): Promise<TagView[]> => { const delRightTags = (view: TagView): Promise<TagView[]> => {
return new Promise((resolve) => { return new Promise((resolve) => {
const index = visitedViews.value.findIndex((v) => v.path === view.path); const index = visitedViews.value.findIndex((v: TagView) => v.path === view.path);
if (index === -1) { if (index === -1) {
return; return;
} }
visitedViews.value = visitedViews.value.filter((item, idx) => { visitedViews.value = visitedViews.value.filter((item: TagView, idx: number) => {
if (idx <= index || (item.meta && item.meta.affix)) { if (idx <= index || (item.meta && item.meta.affix)) {
return true; return true;
} }
@ -152,11 +152,11 @@ export const useTagsViewStore = defineStore('tagsView', () => {
}; };
const delLeftTags = (view: TagView): Promise<TagView[]> => { const delLeftTags = (view: TagView): Promise<TagView[]> => {
return new Promise((resolve) => { return new Promise((resolve) => {
const index = visitedViews.value.findIndex((v) => v.path === view.path); const index = visitedViews.value.findIndex((v: TagView) => v.path === view.path);
if (index === -1) { if (index === -1) {
return; return;
} }
visitedViews.value = visitedViews.value.filter((item, idx) => { visitedViews.value = visitedViews.value.filter((item: TagView, idx: number) => {
if (idx >= index || (item.meta && item.meta.affix)) { if (idx >= index || (item.meta && item.meta.affix)) {
return true; return true;
} }
@ -170,7 +170,7 @@ export const useTagsViewStore = defineStore('tagsView', () => {
}); });
}; };
const addCachedView = (view: TagView): void => { const addCachedView = (view: TagView | RouteLocationNormalized): void => {
const viewName = view.name as string; const viewName = view.name as string;
if (!viewName) return; if (!viewName) return;
if (cachedViews.value.includes(viewName)) return; if (cachedViews.value.includes(viewName)) return;

View File

@ -1,12 +1,12 @@
import type * as ep from 'element-plus'; import type * as ep from 'element-plus';
declare global { declare global {
declare type ElTagType = '' | 'success' | 'warning' | 'info' | 'danger' | 'default' | 'primary'; declare type ElTagType = '' | 'success' | 'warning' | 'info' | 'danger' | 'default' | 'primary';
declare type ElFormInstance = InstanceType<typeof ep.ElForm>; declare type ElFormInstance = ep.FormInstance;
declare type ElTableInstance = InstanceType<typeof ep.ElTable>; declare type ElTableInstance = ep.TableInstance;
declare type ElUploadInstance = ep.UploadInstance;
declare type ElTreeInstance = InstanceType<typeof ep.ElTree>; declare type ElTreeInstance = InstanceType<typeof ep.ElTree>;
declare type ElTreeSelectInstance = InstanceType<typeof ep.ElTreeSelect>; declare type ElTreeSelectInstance = InstanceType<typeof ep.ElTreeSelect>;
declare type ElSelectInstance = InstanceType<typeof ep.ElSelect>; declare type ElSelectInstance = InstanceType<typeof ep.ElSelect>;
declare type ElUploadInstance = InstanceType<typeof ep.ElUpload>;
declare type ElCardInstance = InstanceType<typeof ep.ElCard>; declare type ElCardInstance = InstanceType<typeof ep.ElCard>;
declare type ElDialogInstance = InstanceType<typeof ep.ElDialog>; declare type ElDialogInstance = InstanceType<typeof ep.ElDialog>;
declare type ElInputInstance = InstanceType<typeof ep.ElInput>; declare type ElInputInstance = InstanceType<typeof ep.ElInput>;

5
src/types/env.d.ts vendored
View File

@ -1,8 +1,9 @@
declare module '*.vue' { declare module '*.vue' {
import { DefineComponent } from 'vue'; import { DefineComponent } from 'vue';
const component: DefineComponent<{}, {}, any>; const Component: DefineComponent<{}, {}, any>;
export default component; export default Component;
} }
declare module '*.avif' { declare module '*.avif' {
const src: string; const src: string;
export default src; export default src;

73
src/types/global.d.ts vendored
View File

@ -1,4 +1,4 @@
import type { ComponentInternalInstance as ComponentInstance, PropType as VuePropType } from 'vue'; import type { ComponentInternalInstance as ComponentInstance, PropType as VuePropType } from 'vue/runtime-core';
declare global { declare global {
/** vue Instance */ /** vue Instance */
@ -87,5 +87,76 @@ declare global {
pageNum: number; pageNum: number;
pageSize: number; pageSize: number;
} }
declare type DefaultSettings = {
/**
* 网页标题
*/
title: string;
/**
* 侧边栏主题 theme-dark | theme-light
*/
sideTheme?: string;
/**
* 是否显示系统布局设置
*/
showSettings?: boolean;
/**
* 是否显示顶部导航
*/
topNav?: boolean;
/**
* 是否显示多标签导航
*/
tagsView?: boolean;
/**
* 是否固定头部
*/
fixedHeader?: boolean;
/**
* 是否显示侧边栏Logo
*/
sidebarLogo?: boolean;
/**
* 导航栏布局
*/
layout?: string;
/**
* 主题模式
*/
theme?: string;
/**
* 布局大小
*/
size?: string;
/**
* 语言
*/
language?: string;
/**
* 是否显示动态标题
*/
dynamicTitle?: boolean;
/**
* 是否启用动画效果
*/
animationEnable?: boolean;
/**
* 是否启用暗黑模式
*
* true:暗黑模式
* false: 明亮模式
*/
dark?: boolean;
errorLog?: string;
};
} }
export {}; export {};

48
src/types/router.d.ts vendored
View File

@ -1,36 +1,34 @@
import { RouteRecordRaw } from 'vue-router'; import { LocationQuery, type RouteMeta as VRouteMeta } from 'vue-router';
declare module 'vue-router' { declare module 'vue-router' {
declare type RouteOption = { interface RouteMeta extends VRouteMeta {
hidden?: boolean; link?: string;
title?: string;
affix?: boolean;
noCache?: boolean;
activeMenu?: string;
icon?: string;
breadcrumb?: boolean;
}
interface _RouteRecordBase {
hidden?: boolean | string | number;
permissions?: string[]; permissions?: string[];
roles?: string[]; roles?: string[];
component?: any;
children?: RouteOption[];
alwaysShow?: boolean; alwaysShow?: boolean;
parentPath?: string;
meta?: {
title: string;
icon: string;
};
query?: string; query?: string;
} & RouteRecordRaw;
declare interface _RouteLocationBase {
children?: RouteOption[];
} }
declare interface RouteLocationOptions { interface _RouteLocationBase {
children?: _RouteRecordBase[];
path?: string;
}
interface TagView {
fullPath?: string; fullPath?: string;
} name?: string;
path?: string;
declare interface TagView extends Partial<_RouteLocationBase> {
title?: string; title?: string;
meta?: { meta?: RouteMeta;
link?: string; query?: LocationQuery;
title?: string;
affix?: boolean;
noCache?: boolean;
};
} }
} }

View File

@ -1,70 +0,0 @@
declare type DefaultSettings = {
/**
* 网页标题
*/
title?: string;
/**
* 侧边栏主题 theme-dark | theme-light
*/
sideTheme?: string;
/**
* 是否显示系统布局设置
*/
showSettings?: boolean;
/**
* 是否显示顶部导航
*/
topNav?: boolean;
/**
* 是否显示多标签导航
*/
tagsView?: boolean;
/**
* 是否固定头部
*/
fixedHeader?: boolean;
/**
* 是否显示侧边栏Logo
*/
sidebarLogo?: boolean;
/**
* 导航栏布局
*/
layout?: string;
/**
* 主题模式
*/
theme?: string;
/**
* 布局大小
*/
size?: string;
/**
* 语言
*/
language?: string;
/**
* 是否显示动态标题
*/
dynamicTitle?: boolean;
/**
* 是否启用动画效果
*/
animationEnable?: boolean;
/**
* 是否启用暗黑模式
*
* true:暗黑模式
* false: 明亮模式
*/
dark?: boolean;
errorLog?: string;
};

View File

@ -3,6 +3,7 @@ import VueTypes, { createTypes, toValidableType, VueTypeValidableDef, VueTypesIn
type PropTypes = VueTypesInterface & { type PropTypes = VueTypesInterface & {
readonly style: VueTypeValidableDef<CSSProperties>; readonly style: VueTypeValidableDef<CSSProperties>;
readonly fieldOption: VueTypeValidableDef<Array<FieldOption>>;
}; };
const propTypes = createTypes({ const propTypes = createTypes({

View File

@ -89,7 +89,6 @@ service.interceptors.request.use(
return config; return config;
}, },
(error: any) => { (error: any) => {
console.log(error);
return Promise.reject(error); return Promise.reject(error);
} }
); );
@ -138,7 +137,6 @@ service.interceptors.response.use(
} }
return Promise.reject('无效的会话,或者会话已过期,请重新登录。'); return Promise.reject('无效的会话,或者会话已过期,请重新登录。');
} else if (code === HttpStatus.SERVER_ERROR) { } else if (code === HttpStatus.SERVER_ERROR) {
console.log(msg);
ElMessage({ message: msg, type: 'error' }); ElMessage({ message: msg, type: 'error' });
return Promise.reject(new Error(msg)); return Promise.reject(new Error(msg));
} else if (code === HttpStatus.WARN) { } else if (code === HttpStatus.WARN) {

View File

@ -20,7 +20,7 @@
import { getToken } from '@/utils/auth'; import { getToken } from '@/utils/auth';
import useNoticeStore from '@/store/modules/notice'; import useNoticeStore from '@/store/modules/notice';
import { ElNotification } from "element-plus"; import { ElNotification } from 'element-plus';
const { addNotice } = useNoticeStore(); const { addNotice } = useNoticeStore();
@ -135,7 +135,7 @@ export const websocketonmessage = () => {
message: e.data, message: e.data,
type: 'success', type: 'success',
duration: 3000 duration: 3000
}) });
return e.data; return e.data;
}; };
}; };

View File

@ -1,9 +1,9 @@
<template> <template>
<div class="p-2"> <div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="mb-[10px]" v-show="showSearch"> <div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover"> <el-card shadow="hover">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px"> <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px">
<el-form-item label="部门id" prop="deptId"> <el-form-item label="部门id" prop="deptId">
<el-input v-model="queryParams.deptId" placeholder="请输入部门id" clearable style="width: 240px" @keyup.enter="handleQuery" /> <el-input v-model="queryParams.deptId" placeholder="请输入部门id" clearable style="width: 240px" @keyup.enter="handleQuery" />
</el-form-item> </el-form-item>
@ -32,26 +32,26 @@
<template #header> <template #header>
<el-row :gutter="10" class="mb8"> <el-row :gutter="10" class="mb8">
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['demo:demo:add']">新增</el-button> <el-button v-hasPermi="['demo:demo:add']" type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['demo:demo:edit']">修改</el-button> <el-button v-hasPermi="['demo:demo:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()">修改</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['demo:demo:remove']" <el-button v-hasPermi="['demo:demo:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()"
>删除</el-button >删除</el-button
> >
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['demo:demo:export']">导出</el-button> <el-button v-hasPermi="['demo:demo:export']" type="warning" plain icon="Download" @click="handleExport">导出</el-button>
</el-col> </el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> <right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
</el-row> </el-row>
</template> </template>
<el-table v-loading="loading" :data="demoList" @selection-change="handleSelectionChange"> <el-table v-loading="loading" :data="demoList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" /> <el-table-column type="selection" width="55" align="center" />
<el-table-column label="主键" align="center" prop="id" v-if="true" /> <el-table-column v-if="true" label="主键" align="center" prop="id" />
<el-table-column label="部门id" align="center" prop="deptId" /> <el-table-column label="部门id" align="center" prop="deptId" />
<el-table-column label="用户id" align="center" prop="userId" /> <el-table-column label="用户id" align="center" prop="userId" />
<el-table-column label="排序号" align="center" prop="orderNum" /> <el-table-column label="排序号" align="center" prop="orderNum" />
@ -60,19 +60,19 @@
<el-table-column label="操作" align="center" class-name="small-padding fixed-width"> <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope"> <template #default="scope">
<el-tooltip content="修改" placement="top"> <el-tooltip content="修改" placement="top">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['demo:demo:edit']"></el-button> <el-button v-hasPermi="['demo:demo:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
</el-tooltip> </el-tooltip>
<el-tooltip content="删除" placement="top"> <el-tooltip content="删除" placement="top">
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['demo:demo:remove']"></el-button> <el-button v-hasPermi="['demo:demo:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
</el-tooltip> </el-tooltip>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
</el-card> </el-card>
<!-- 添加或修改测试单对话框 --> <!-- 添加或修改测试单对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body> <el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
<el-form ref="demoFormRef" :model="form" :rules="rules" label-width="80px"> <el-form ref="demoFormRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="部门id" prop="deptId"> <el-form-item label="部门id" prop="deptId">
<el-input v-model="form.deptId" placeholder="请输入部门id" /> <el-input v-model="form.deptId" placeholder="请输入部门id" />
@ -129,8 +129,8 @@ const initFormData: DemoForm = {
userId: undefined, userId: undefined,
orderNum: undefined, orderNum: undefined,
testKey: undefined, testKey: undefined,
value: undefined, value: undefined
} };
const data = reactive<PageData<DemoForm, DemoQuery>>({ const data = reactive<PageData<DemoForm, DemoQuery>>({
form: { ...initFormData }, form: { ...initFormData },
queryParams: { queryParams: {
@ -140,27 +140,15 @@ const data = reactive<PageData<DemoForm, DemoQuery>>({
userId: undefined, userId: undefined,
orderNum: undefined, orderNum: undefined,
testKey: undefined, testKey: undefined,
value: undefined, value: undefined
}, },
rules: { rules: {
id: [ id: [{ required: true, message: '主键不能为空', trigger: 'blur' }],
{ required: true, message: "主键不能为空", trigger: "blur" } deptId: [{ required: true, message: '部门id不能为空', trigger: 'blur' }],
], userId: [{ required: true, message: '用户id不能为空', trigger: 'blur' }],
deptId: [ orderNum: [{ required: true, message: '排序号不能为空', trigger: 'blur' }],
{ required: true, message: "部门id不能为空", trigger: "blur" } testKey: [{ required: true, message: 'key键不能为空', trigger: 'blur' }],
], value: [{ required: true, message: '值不能为空', trigger: 'blur' }]
userId: [
{ required: true, message: "用户id不能为空", trigger: "blur" }
],
orderNum: [
{ required: true, message: "排序号不能为空", trigger: "blur" }
],
testKey: [
{ required: true, message: "key键不能为空", trigger: "blur" }
],
value: [
{ required: true, message: "值不能为空", trigger: "blur" }
],
} }
}); });
@ -173,55 +161,55 @@ const getList = async () => {
demoList.value = res.rows; demoList.value = res.rows;
total.value = res.total; total.value = res.total;
loading.value = false; loading.value = false;
} };
/** 取消按钮 */ /** 取消按钮 */
const cancel = () => { const cancel = () => {
reset(); reset();
dialog.visible = false; dialog.visible = false;
} };
/** 表单重置 */ /** 表单重置 */
const reset = () => { const reset = () => {
form.value = { ...initFormData }; form.value = { ...initFormData };
demoFormRef.value?.resetFields(); demoFormRef.value?.resetFields();
} };
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
queryParams.value.pageNum = 1; queryParams.value.pageNum = 1;
getList(); getList();
} };
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
queryFormRef.value?.resetFields(); queryFormRef.value?.resetFields();
handleQuery(); handleQuery();
} };
/** 多选框选中数据 */ /** 多选框选中数据 */
const handleSelectionChange = (selection: DemoVO[]) => { const handleSelectionChange = (selection: DemoVO[]) => {
ids.value = selection.map(item => item.id); ids.value = selection.map((item) => item.id);
single.value = selection.length != 1; single.value = selection.length != 1;
multiple.value = !selection.length; multiple.value = !selection.length;
} };
/** 新增按钮操作 */ /** 新增按钮操作 */
const handleAdd = () => { const handleAdd = () => {
reset(); reset();
dialog.visible = true; dialog.visible = true;
dialog.title = "添加测试单"; dialog.title = '添加测试单';
} };
/** 修改按钮操作 */ /** 修改按钮操作 */
const handleUpdate = async (row?: DemoVO) => { const handleUpdate = async (row?: DemoVO) => {
reset(); reset();
const _id = row?.id || ids.value[0] const _id = row?.id || ids.value[0];
const res = await getDemo(_id); const res = await getDemo(_id);
Object.assign(form.value, res.data); Object.assign(form.value, res.data);
dialog.visible = true; dialog.visible = true;
dialog.title = "修改测试单"; dialog.title = '修改测试单';
} };
/** 提交按钮 */ /** 提交按钮 */
const submitForm = () => { const submitForm = () => {
@ -229,32 +217,36 @@ const submitForm = () => {
if (valid) { if (valid) {
buttonLoading.value = true; buttonLoading.value = true;
if (form.value.id) { if (form.value.id) {
await updateDemo(form.value).finally(() => buttonLoading.value = false); await updateDemo(form.value).finally(() => (buttonLoading.value = false));
} else { } else {
await addDemo(form.value).finally(() => buttonLoading.value = false); await addDemo(form.value).finally(() => (buttonLoading.value = false));
} }
proxy?.$modal.msgSuccess("修改成功"); proxy?.$modal.msgSuccess('修改成功');
dialog.visible = false; dialog.visible = false;
await getList(); await getList();
} }
}); });
} };
/** 删除按钮操作 */ /** 删除按钮操作 */
const handleDelete = async (row?: DemoVO) => { const handleDelete = async (row?: DemoVO) => {
const _ids = row?.id || ids.value; const _ids = row?.id || ids.value;
await proxy?.$modal.confirm('是否确认删除测试单编号为"' + _ids + '"的数据项?').finally(() => loading.value = false); await proxy?.$modal.confirm('是否确认删除测试单编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
await delDemo(_ids); await delDemo(_ids);
proxy?.$modal.msgSuccess("删除成功"); proxy?.$modal.msgSuccess('删除成功');
await getList(); await getList();
} };
/** 导出按钮操作 */ /** 导出按钮操作 */
const handleExport = () => { const handleExport = () => {
proxy?.download('demo/demo/export', { proxy?.download(
...queryParams.value 'demo/demo/export',
}, `demo_${new Date().getTime()}.xlsx`) {
} ...queryParams.value
},
`demo_${new Date().getTime()}.xlsx`
);
};
onMounted(() => { onMounted(() => {
getList(); getList();

View File

@ -1,9 +1,9 @@
<template> <template>
<div class="p-2"> <div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="mb-[10px]" v-show="showSearch"> <div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover"> <el-card shadow="hover">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px"> <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px">
<el-form-item label="树节点名" prop="treeName"> <el-form-item label="树节点名" prop="treeName">
<el-input v-model="queryParams.treeName" placeholder="请输入树节点名" clearable style="width: 240px" @keyup.enter="handleQuery" /> <el-input v-model="queryParams.treeName" placeholder="请输入树节点名" clearable style="width: 240px" @keyup.enter="handleQuery" />
</el-form-item> </el-form-item>
@ -20,21 +20,21 @@
<template #header> <template #header>
<el-row :gutter="10" class="mb8"> <el-row :gutter="10" class="mb8">
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd()" v-hasPermi="['demo:tree:add']">新增</el-button> <el-button v-hasPermi="['demo:tree:add']" type="primary" plain icon="Plus" @click="handleAdd()">新增</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="info" plain icon="Sort" @click="handleToggleExpandAll">展开/折叠</el-button> <el-button type="info" plain icon="Sort" @click="handleToggleExpandAll">展开/折叠</el-button>
</el-col> </el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> <right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
</el-row> </el-row>
</template> </template>
<el-table <el-table
ref="treeTableRef"
v-loading="loading" v-loading="loading"
:data="treeList" :data="treeList"
row-key="id" row-key="id"
:default-expand-all="isExpandAll" :default-expand-all="isExpandAll"
:tree-props="{children: 'children', hasChildren: 'hasChildren'}" :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
ref="treeTableRef"
> >
<el-table-column label="父id" align="center" prop="parentId" /> <el-table-column label="父id" align="center" prop="parentId" />
<el-table-column label="部门id" align="center" prop="deptId" /> <el-table-column label="部门id" align="center" prop="deptId" />
@ -43,20 +43,20 @@
<el-table-column label="操作" align="center" class-name="small-padding fixed-width"> <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope"> <template #default="scope">
<el-tooltip content="修改" placement="top"> <el-tooltip content="修改" placement="top">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['demo:tree:edit']" /> <el-button v-hasPermi="['demo:tree:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)" />
</el-tooltip> </el-tooltip>
<el-tooltip content="新增" placement="top"> <el-tooltip content="新增" placement="top">
<el-button link type="primary" icon="Plus" @click="handleAdd(scope.row)" v-hasPermi="['demo:tree:add']" /> <el-button v-hasPermi="['demo:tree:add']" link type="primary" icon="Plus" @click="handleAdd(scope.row)" />
</el-tooltip> </el-tooltip>
<el-tooltip content="删除" placement="top"> <el-tooltip content="删除" placement="top">
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['demo:tree:remove']" /> <el-button v-hasPermi="['demo:tree:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)" />
</el-tooltip> </el-tooltip>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
</el-card> </el-card>
<!-- 添加或修改测试树对话框 --> <!-- 添加或修改测试树对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body> <el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
<el-form ref="treeFormRef" :model="form" :rules="rules" label-width="80px"> <el-form ref="treeFormRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="父id" prop="parentId"> <el-form-item label="父id" prop="parentId">
<el-tree-select <el-tree-select
@ -89,18 +89,16 @@
</template> </template>
<script setup name="Tree" lang="ts"> <script setup name="Tree" lang="ts">
import { listTree, getTree, delTree, addTree, updateTree } from "@/api/demo/tree"; import { listTree, getTree, delTree, addTree, updateTree } from '@/api/demo/tree';
import { TreeVO, TreeQuery, TreeForm } from '@/api/demo/tree/types'; import { TreeVO, TreeQuery, TreeForm } from '@/api/demo/tree/types';
type TreeOption = { type TreeOption = {
id: number; id: number;
treeName: string; treeName: string;
children?: TreeOption[]; children?: TreeOption[];
} };
const { proxy } = getCurrentInstance() as ComponentInternalInstance;;
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const treeList = ref<TreeVO[]>([]); const treeList = ref<TreeVO[]>([]);
const treeOptions = ref<TreeOption[]>([]); const treeOptions = ref<TreeOption[]>([]);
@ -111,46 +109,35 @@ const loading = ref(false);
const queryFormRef = ref<ElFormInstance>(); const queryFormRef = ref<ElFormInstance>();
const treeFormRef = ref<ElFormInstance>(); const treeFormRef = ref<ElFormInstance>();
const treeTableRef = ref<ElTableInstance>() const treeTableRef = ref<ElTableInstance>();
const dialog = reactive<DialogOption>({ const dialog = reactive<DialogOption>({
visible: false, visible: false,
title: '' title: ''
}); });
const initFormData: TreeForm = { const initFormData: TreeForm = {
id: undefined, id: undefined,
parentId: undefined, parentId: undefined,
deptId: undefined, deptId: undefined,
userId: undefined, userId: undefined,
treeName: undefined, treeName: undefined
} };
const data = reactive<PageData<TreeForm, TreeQuery>>({ const data = reactive<PageData<TreeForm, TreeQuery>>({
form: {...initFormData}, form: { ...initFormData },
queryParams: { queryParams: {
parentId: undefined, parentId: undefined,
deptId: undefined, deptId: undefined,
userId: undefined, userId: undefined,
treeName: undefined, treeName: undefined
}, },
rules: { rules: {
id: [ id: [{ required: true, message: '主键不能为空', trigger: 'blur' }],
{ required: true, message: "主键不能为空", trigger: "blur" } parentId: [{ required: true, message: '父id不能为空', trigger: 'blur' }],
], deptId: [{ required: true, message: '部门id不能为空', trigger: 'blur' }],
parentId: [ userId: [{ required: true, message: '用户id不能为空', trigger: 'blur' }],
{ required: true, message: "父id不能为空", trigger: "blur" } treeName: [{ required: true, message: '值不能为空', trigger: 'blur' }]
],
deptId: [
{ required: true, message: "部门id不能为空", trigger: "blur" }
],
userId: [
{ required: true, message: "用户id不能为空", trigger: "blur" }
],
treeName: [
{ required: true, message: "值不能为空", trigger: "blur" }
],
} }
}); });
@ -160,44 +147,44 @@ const { queryParams, form, rules } = toRefs(data);
const getList = async () => { const getList = async () => {
loading.value = true; loading.value = true;
const res = await listTree(queryParams.value); const res = await listTree(queryParams.value);
const data = proxy?.handleTree<TreeVO>(res.data, "id", "parentId"); const data = proxy?.handleTree<TreeVO>(res.data, 'id', 'parentId');
if (data) { if (data) {
treeList.value = data; treeList.value = data;
loading.value = false; loading.value = false;
} }
} };
/** 查询测试树下拉树结构 */ /** 查询测试树下拉树结构 */
const getTreeselect = async () => { const getTreeselect = async () => {
const res = await listTree(); const res = await listTree();
treeOptions.value = []; treeOptions.value = [];
const data: TreeOption = { id: 0, treeName: '顶级节点', children: [] }; const data: TreeOption = { id: 0, treeName: '顶级节点', children: [] };
data.children = proxy?.handleTree<TreeOption>(res.data, "id", "parentId"); data.children = proxy?.handleTree<TreeOption>(res.data, 'id', 'parentId');
treeOptions.value.push(data); treeOptions.value.push(data);
} };
// 取消按钮 // 取消按钮
const cancel = () => { const cancel = () => {
reset(); reset();
dialog.visible = false; dialog.visible = false;
} };
// 表单重置 // 表单重置
const reset = () => { const reset = () => {
form.value = {...initFormData} form.value = { ...initFormData };
treeFormRef.value?.resetFields(); treeFormRef.value?.resetFields();
} };
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
getList(); getList();
} };
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
queryFormRef.value?.resetFields(); queryFormRef.value?.resetFields();
handleQuery(); handleQuery();
} };
/** 新增按钮操作 */ /** 新增按钮操作 */
const handleAdd = (row?: TreeVO) => { const handleAdd = (row?: TreeVO) => {
@ -209,22 +196,22 @@ const handleAdd = (row?: TreeVO) => {
form.value.parentId = 0; form.value.parentId = 0;
} }
dialog.visible = true; dialog.visible = true;
dialog.title = "添加测试树"; dialog.title = '添加测试树';
} };
/** 展开/折叠操作 */ /** 展开/折叠操作 */
const handleToggleExpandAll = () => { const handleToggleExpandAll = () => {
isExpandAll.value = !isExpandAll.value; isExpandAll.value = !isExpandAll.value;
toggleExpandAll(treeList.value, isExpandAll.value) toggleExpandAll(treeList.value, isExpandAll.value);
} };
/** 展开/折叠操作 */ /** 展开/折叠操作 */
const toggleExpandAll = (data: TreeVO[], status: boolean) => { const toggleExpandAll = (data: TreeVO[], status: boolean) => {
data.forEach((item) => { data.forEach((item) => {
treeTableRef.value?.toggleRowExpansion(item, status) treeTableRef.value?.toggleRowExpansion(item, status);
if (item.children && item.children.length > 0) toggleExpandAll(item.children, status) if (item.children && item.children.length > 0) toggleExpandAll(item.children, status);
}) });
} };
/** 修改按钮操作 */ /** 修改按钮操作 */
const handleUpdate = async (row: TreeVO) => { const handleUpdate = async (row: TreeVO) => {
@ -236,8 +223,8 @@ const handleUpdate = async (row: TreeVO) => {
const res = await getTree(row.id); const res = await getTree(row.id);
Object.assign(form.value, res.data); Object.assign(form.value, res.data);
dialog.visible = true; dialog.visible = true;
dialog.title = "修改测试树"; dialog.title = '修改测试树';
} };
/** 提交按钮 */ /** 提交按钮 */
const submitForm = () => { const submitForm = () => {
@ -245,25 +232,25 @@ const submitForm = () => {
if (valid) { if (valid) {
buttonLoading.value = true; buttonLoading.value = true;
if (form.value.id) { if (form.value.id) {
await updateTree(form.value).finally(() => buttonLoading.value = false); await updateTree(form.value).finally(() => (buttonLoading.value = false));
} else { } else {
await addTree(form.value).finally(() => buttonLoading.value = false); await addTree(form.value).finally(() => (buttonLoading.value = false));
} }
proxy?.$modal.msgSuccess("操作成功"); proxy?.$modal.msgSuccess('操作成功');
dialog.visible = false; dialog.visible = false;
await getList(); await getList();
} }
}); });
} };
/** 删除按钮操作 */ /** 删除按钮操作 */
const handleDelete = async (row: TreeVO) => { const handleDelete = async (row: TreeVO) => {
await proxy?.$modal.confirm('是否确认删除测试树编号为"' + row.id + '"的数据项?'); await proxy?.$modal.confirm('是否确认删除测试树编号为"' + row.id + '"的数据项?');
loading.value = true; loading.value = true;
await delTree(row.id).finally(() => loading.value = false); await delTree(row.id).finally(() => (loading.value = false));
await getList(); await getList();
proxy?.$modal.msgSuccess("删除成功"); proxy?.$modal.msgSuccess('删除成功');
} };
onMounted(() => { onMounted(() => {
getList(); getList();

View File

@ -24,11 +24,11 @@ import errImage from '@/assets/401_images/401.gif';
let { proxy } = getCurrentInstance() as ComponentInternalInstance; let { proxy } = getCurrentInstance() as ComponentInternalInstance;
const errGif = ref(errImage + "?" + +new Date()); const errGif = ref(errImage + '?' + +new Date());
function back() { function back() {
if (proxy?.$route.query.noGoBack) { if (proxy?.$route.query.noGoBack) {
proxy.$router.push({ path: "/" }); proxy.$router.push({ path: '/' });
} else { } else {
proxy?.$router.go(-1); proxy?.$router.go(-1);
} }

View File

@ -23,13 +23,13 @@
<script setup lang="ts"> <script setup lang="ts">
let message = computed(() => { let message = computed(() => {
return '找不到网页!' return '找不到网页!';
}) });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.wscn-http404-container{ .wscn-http404-container {
transform: translate(-50%,-50%); transform: translate(-50%, -50%);
position: absolute; position: absolute;
top: 40%; top: 40%;
left: 50%; left: 50%;

View File

@ -99,13 +99,13 @@
import { initWebSocket } from '@/utils/websocket'; import { initWebSocket } from '@/utils/websocket';
onMounted(() => { onMounted(() => {
let protocol = window.location.protocol === 'https:' ? 'wss://' : 'ws://' let protocol = window.location.protocol === 'https:' ? 'wss://' : 'ws://';
initWebSocket(protocol + window.location.host + import.meta.env.VITE_APP_BASE_API + "/resource/websocket"); initWebSocket(protocol + window.location.host + import.meta.env.VITE_APP_BASE_API + '/resource/websocket');
}); });
const goTarget = (url:string) => { const goTarget = (url: string) => {
window.open(url, '__blank') window.open(url, '__blank');
} };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@ -131,7 +131,7 @@ const goTarget = (url:string) => {
margin: 0; margin: 0;
} }
font-family: "open sans", "Helvetica Neue", Helvetica, Arial, sans-serif; font-family: 'open sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-size: 13px; font-size: 13px;
color: #676a6c; color: #676a6c;
overflow-x: hidden; overflow-x: hidden;

View File

@ -2,7 +2,7 @@
<div class="login"> <div class="login">
<el-form ref="loginRef" :model="loginForm" :rules="loginRules" class="login-form"> <el-form ref="loginRef" :model="loginForm" :rules="loginRules" class="login-form">
<h3 class="title">RuoYi-Vue-Plus多租户管理系统</h3> <h3 class="title">RuoYi-Vue-Plus多租户管理系统</h3>
<el-form-item prop="tenantId" v-if="tenantEnabled"> <el-form-item v-if="tenantEnabled" prop="tenantId">
<el-select v-model="loginForm.tenantId" filterable placeholder="请选择/输入公司名称" style="width: 100%"> <el-select v-model="loginForm.tenantId" filterable placeholder="请选择/输入公司名称" style="width: 100%">
<el-option v-for="item in tenantList" :key="item.tenantId" :label="item.companyName" :value="item.tenantId"></el-option> <el-option v-for="item in tenantList" :key="item.tenantId" :label="item.companyName" :value="item.tenantId"></el-option>
<template #prefix><svg-icon icon-class="company" class="el-input__icon input-icon" /></template> <template #prefix><svg-icon icon-class="company" class="el-input__icon input-icon" /></template>
@ -18,16 +18,16 @@
<template #prefix><svg-icon icon-class="password" class="el-input__icon input-icon" /></template> <template #prefix><svg-icon icon-class="password" class="el-input__icon input-icon" /></template>
</el-input> </el-input>
</el-form-item> </el-form-item>
<el-form-item prop="code" v-if="captchaEnabled"> <el-form-item v-if="captchaEnabled" prop="code">
<el-input v-model="loginForm.code" size="large" auto-complete="off" placeholder="验证码" style="width: 63%" @keyup.enter="handleLogin"> <el-input v-model="loginForm.code" size="large" auto-complete="off" placeholder="验证码" style="width: 63%" @keyup.enter="handleLogin">
<template #prefix><svg-icon icon-class="validCode" class="el-input__icon input-icon" /></template> <template #prefix><svg-icon icon-class="validCode" class="el-input__icon input-icon" /></template>
</el-input> </el-input>
<div class="login-code"> <div class="login-code">
<img :src="codeUrl" @click="getCode" class="login-code-img" /> <img :src="codeUrl" class="login-code-img" @click="getCode" />
</div> </div>
</el-form-item> </el-form-item>
<el-checkbox v-model="loginForm.rememberMe" style="margin:0px 0px 25px 0px;">记住密码</el-checkbox> <el-checkbox v-model="loginForm.rememberMe" style="margin: 0px 0px 25px 0px">记住密码</el-checkbox>
<el-form-item style="float: right;"> <el-form-item style="float: right">
<el-button circle title="微信登录" @click="doSocialLogin('wechat')"> <el-button circle title="微信登录" @click="doSocialLogin('wechat')">
<svg-icon icon-class="wechat" /> <svg-icon icon-class="wechat" />
</el-button> </el-button>
@ -41,12 +41,12 @@
<svg-icon icon-class="github" /> <svg-icon icon-class="github" />
</el-button> </el-button>
</el-form-item> </el-form-item>
<el-form-item style="width:100%;"> <el-form-item style="width: 100%">
<el-button :loading="loading" size="large" type="primary" style="width:100%;" @click.prevent="handleLogin"> <el-button :loading="loading" size="large" type="primary" style="width: 100%" @click.prevent="handleLogin">
<span v-if="!loading"> </span> <span v-if="!loading"> </span>
<span v-else> 中...</span> <span v-else> 中...</span>
</el-button> </el-button>
<div style="float: right;" v-if="register"> <div v-if="register" style="float: right">
<router-link class="link-type" :to="'/register'">立即注册</router-link> <router-link class="link-type" :to="'/register'">立即注册</router-link>
</div> </div>
</el-form-item> </el-form-item>
@ -64,7 +64,7 @@ import { authBinding } from '@/api/system/social/auth';
import { useUserStore } from '@/store/modules/user'; import { useUserStore } from '@/store/modules/user';
import { LoginData, TenantVO } from '@/api/types'; import { LoginData, TenantVO } from '@/api/types';
import { to } from 'await-to-js'; import { to } from 'await-to-js';
import { HttpStatus } from "@/enums/RespEnum"; import { HttpStatus } from '@/enums/RespEnum';
const userStore = useUserStore(); const userStore = useUserStore();
const router = useRouter(); const router = useRouter();
@ -79,7 +79,7 @@ const loginForm = ref<LoginData>({
} as LoginData); } as LoginData);
const loginRules: ElFormRules = { const loginRules: ElFormRules = {
tenantId: [{ required: true, trigger: "blur", message: "请输入您的租户编号" }], tenantId: [{ required: true, trigger: 'blur', message: '请输入您的租户编号' }],
username: [{ required: true, trigger: 'blur', message: '请输入您的账号' }], username: [{ required: true, trigger: 'blur', message: '请输入您的账号' }],
password: [{ required: true, trigger: 'blur', message: '请输入您的密码' }], password: [{ required: true, trigger: 'blur', message: '请输入您的密码' }],
code: [{ required: true, trigger: 'change', message: '请输入验证码' }] code: [{ required: true, trigger: 'change', message: '请输入验证码' }]
@ -92,7 +92,6 @@ const captchaEnabled = ref(true);
// 租户开关 // 租户开关
const tenantEnabled = ref(true); const tenantEnabled = ref(true);
// 注册开关 // 注册开关
const register = ref(false); const register = ref(false);
const redirect = ref(undefined); const redirect = ref(undefined);
@ -100,9 +99,13 @@ const loginRef = ref<ElFormInstance>();
// 租户列表 // 租户列表
const tenantList = ref<TenantVO[]>([]); const tenantList = ref<TenantVO[]>([]);
watch(() => router.currentRoute.value, (newRoute: any) => { watch(
redirect.value = newRoute.query && newRoute.query.redirect; () => router.currentRoute.value,
}, { immediate: true }); (newRoute: any) => {
redirect.value = newRoute.query && newRoute.query.redirect;
},
{ immediate: true }
);
const handleLogin = () => { const handleLogin = () => {
loginRef.value?.validate(async (valid: boolean, fields: any) => { loginRef.value?.validate(async (valid: boolean, fields: any) => {
@ -110,13 +113,13 @@ const handleLogin = () => {
loading.value = true; loading.value = true;
// 勾选了需要记住密码设置在 localStorage 中设置记住用户名和密码 // 勾选了需要记住密码设置在 localStorage 中设置记住用户名和密码
if (loginForm.value.rememberMe) { if (loginForm.value.rememberMe) {
localStorage.setItem("tenantId", String(loginForm.value.tenantId)); localStorage.setItem('tenantId', String(loginForm.value.tenantId));
localStorage.setItem('username', String(loginForm.value.username)); localStorage.setItem('username', String(loginForm.value.username));
localStorage.setItem('password', String(loginForm.value.password)); localStorage.setItem('password', String(loginForm.value.password));
localStorage.setItem('rememberMe', String(loginForm.value.rememberMe)); localStorage.setItem('rememberMe', String(loginForm.value.rememberMe));
} else { } else {
// 否则移除 // 否则移除
localStorage.removeItem("tenantId"); localStorage.removeItem('tenantId');
localStorage.removeItem('username'); localStorage.removeItem('username');
localStorage.removeItem('password'); localStorage.removeItem('password');
localStorage.removeItem('rememberMe'); localStorage.removeItem('rememberMe');
@ -153,7 +156,7 @@ const getCode = async () => {
}; };
const getLoginData = () => { const getLoginData = () => {
const tenantId = localStorage.getItem("tenantId"); const tenantId = localStorage.getItem('tenantId');
const username = localStorage.getItem('username'); const username = localStorage.getItem('username');
const password = localStorage.getItem('password'); const password = localStorage.getItem('password');
const rememberMe = localStorage.getItem('rememberMe'); const rememberMe = localStorage.getItem('rememberMe');
@ -163,8 +166,7 @@ const getLoginData = () => {
password: password === null ? String(loginForm.value.password) : String(password), password: password === null ? String(loginForm.value.password) : String(password),
rememberMe: rememberMe === null ? false : Boolean(rememberMe) rememberMe: rememberMe === null ? false : Boolean(rememberMe)
} as LoginData; } as LoginData;
} };
/** /**
* 获取租户列表 * 获取租户列表
@ -178,12 +180,15 @@ const initTenantList = async () => {
loginForm.value.tenantId = tenantList.value[0].tenantId; loginForm.value.tenantId = tenantList.value[0].tenantId;
} }
} }
} };
//检测租户选择框的变化 //检测租户选择框的变化
watch(() => loginForm.value.tenantId, () => { watch(
localStorage.setItem("tenantId", String(loginForm.value.tenantId)) () => loginForm.value.tenantId,
}); () => {
localStorage.setItem('tenantId', String(loginForm.value.tenantId));
}
);
/** /**
* 第三方登录 * 第三方登录
@ -200,8 +205,6 @@ const doSocialLogin = (type: string) => {
}); });
}; };
onMounted(() => { onMounted(() => {
getCode(); getCode();
initTenantList(); initTenantList();
@ -215,7 +218,7 @@ onMounted(() => {
justify-content: center; justify-content: center;
align-items: center; align-items: center;
height: 100%; height: 100%;
background-image: url("../assets/images/login-background.jpg"); background-image: url('../assets/images/login-background.jpg');
background-size: cover; background-size: cover;
} }

View File

@ -4,8 +4,8 @@
<el-col :span="24" class="card-box"> <el-col :span="24" class="card-box">
<el-card shadow="hover"> <el-card shadow="hover">
<template #header> <template #header>
<Monitor style="width: 1em; height: 1em; vertical-align: middle;" /> <Monitor style="width: 1em; height: 1em; vertical-align: middle" />
<span style="vertical-align: middle;">基本信息</span> <span style="vertical-align: middle">基本信息</span>
</template> </template>
<div class="el-table el-table--enable-row-hover el-table--medium"> <div class="el-table el-table--enable-row-hover el-table--medium">
@ -16,25 +16,25 @@
<div class="cell">Redis版本</div> <div class="cell">Redis版本</div>
</td> </td>
<td class="el-table__cell is-leaf"> <td class="el-table__cell is-leaf">
<div class="cell" v-if="cache.info">{{ cache.info.redis_version }}</div> <div v-if="cache.info" class="cell">{{ cache.info.redis_version }}</div>
</td> </td>
<td class="el-table__cell is-leaf"> <td class="el-table__cell is-leaf">
<div class="cell">运行模式</div> <div class="cell">运行模式</div>
</td> </td>
<td class="el-table__cell is-leaf"> <td class="el-table__cell is-leaf">
<div class="cell" v-if="cache.info">{{ cache.info.redis_mode === "standalone" ? "单机" : "集群" }}</div> <div v-if="cache.info" class="cell">{{ cache.info.redis_mode === 'standalone' ? '单机' : '集群' }}</div>
</td> </td>
<td class="el-table__cell is-leaf"> <td class="el-table__cell is-leaf">
<div class="cell">端口</div> <div class="cell">端口</div>
</td> </td>
<td class="el-table__cell is-leaf"> <td class="el-table__cell is-leaf">
<div class="cell" v-if="cache.info">{{ cache.info.tcp_port }}</div> <div v-if="cache.info" class="cell">{{ cache.info.tcp_port }}</div>
</td> </td>
<td class="el-table__cell is-leaf"> <td class="el-table__cell is-leaf">
<div class="cell">客户端数</div> <div class="cell">客户端数</div>
</td> </td>
<td class="el-table__cell is-leaf"> <td class="el-table__cell is-leaf">
<div class="cell" v-if="cache.info">{{ cache.info.connected_clients }}</div> <div v-if="cache.info" class="cell">{{ cache.info.connected_clients }}</div>
</td> </td>
</tr> </tr>
<tr> <tr>
@ -42,25 +42,25 @@
<div class="cell">运行时间()</div> <div class="cell">运行时间()</div>
</td> </td>
<td class="el-table__cell is-leaf"> <td class="el-table__cell is-leaf">
<div class="cell" v-if="cache.info">{{ cache.info.uptime_in_days }}</div> <div v-if="cache.info" class="cell">{{ cache.info.uptime_in_days }}</div>
</td> </td>
<td class="el-table__cell is-leaf"> <td class="el-table__cell is-leaf">
<div class="cell">使用内存</div> <div class="cell">使用内存</div>
</td> </td>
<td class="el-table__cell is-leaf"> <td class="el-table__cell is-leaf">
<div class="cell" v-if="cache.info">{{ cache.info.used_memory_human }}</div> <div v-if="cache.info" class="cell">{{ cache.info.used_memory_human }}</div>
</td> </td>
<td class="el-table__cell is-leaf"> <td class="el-table__cell is-leaf">
<div class="cell">使用CPU</div> <div class="cell">使用CPU</div>
</td> </td>
<td class="el-table__cell is-leaf"> <td class="el-table__cell is-leaf">
<div class="cell" v-if="cache.info">{{ parseFloat(cache.info.used_cpu_user_children).toFixed(2) }}</div> <div v-if="cache.info" class="cell">{{ parseFloat(cache.info.used_cpu_user_children).toFixed(2) }}</div>
</td> </td>
<td class="el-table__cell is-leaf"> <td class="el-table__cell is-leaf">
<div class="cell">内存配置</div> <div class="cell">内存配置</div>
</td> </td>
<td class="el-table__cell is-leaf"> <td class="el-table__cell is-leaf">
<div class="cell" v-if="cache.info">{{ cache.info.maxmemory_human }}</div> <div v-if="cache.info" class="cell">{{ cache.info.maxmemory_human }}</div>
</td> </td>
</tr> </tr>
<tr> <tr>
@ -68,25 +68,25 @@
<div class="cell">AOF是否开启</div> <div class="cell">AOF是否开启</div>
</td> </td>
<td class="el-table__cell is-leaf"> <td class="el-table__cell is-leaf">
<div class="cell" v-if="cache.info">{{ cache.info.aof_enabled === "0" ? "否" : "是" }}</div> <div v-if="cache.info" class="cell">{{ cache.info.aof_enabled === '0' ? '' : '' }}</div>
</td> </td>
<td class="el-table__cell is-leaf"> <td class="el-table__cell is-leaf">
<div class="cell">RDB是否成功</div> <div class="cell">RDB是否成功</div>
</td> </td>
<td class="el-table__cell is-leaf"> <td class="el-table__cell is-leaf">
<div class="cell" v-if="cache.info">{{ cache.info.rdb_last_bgsave_status }}</div> <div v-if="cache.info" class="cell">{{ cache.info.rdb_last_bgsave_status }}</div>
</td> </td>
<td class="el-table__cell is-leaf"> <td class="el-table__cell is-leaf">
<div class="cell">Key数量</div> <div class="cell">Key数量</div>
</td> </td>
<td class="el-table__cell is-leaf"> <td class="el-table__cell is-leaf">
<div class="cell" v-if="cache.dbSize">{{ cache.dbSize }}</div> <div v-if="cache.dbSize" class="cell">{{ cache.dbSize }}</div>
</td> </td>
<td class="el-table__cell is-leaf"> <td class="el-table__cell is-leaf">
<div class="cell">网络入口/出口</div> <div class="cell">网络入口/出口</div>
</td> </td>
<td class="el-table__cell is-leaf"> <td class="el-table__cell is-leaf">
<div class="cell" v-if="cache.info"> <div v-if="cache.info" class="cell">
{{ cache.info.instantaneous_input_kbps }}kps/{{ cache.info.instantaneous_output_kbps }}kps {{ cache.info.instantaneous_input_kbps }}kps/{{ cache.info.instantaneous_output_kbps }}kps
</div> </div>
</td> </td>
@ -100,8 +100,8 @@
<el-col :span="12" class="card-box"> <el-col :span="12" class="card-box">
<el-card shadow="hover"> <el-card shadow="hover">
<template #header> <template #header>
<PieChart style="width: 1em; height: 1em; vertical-align: middle;" /> <PieChart style="width: 1em; height: 1em; vertical-align: middle" />
<span style="vertical-align: middle;">命令统计</span> <span style="vertical-align: middle">命令统计</span>
</template> </template>
<div class="el-table el-table--enable-row-hover el-table--medium"> <div class="el-table el-table--enable-row-hover el-table--medium">
<div ref="commandstats" style="height: 420px" /> <div ref="commandstats" style="height: 420px" />
@ -112,7 +112,7 @@
<el-col :span="12" class="card-box"> <el-col :span="12" class="card-box">
<el-card shadow="hover"> <el-card shadow="hover">
<template #header> <template #header>
<Odometer style="width: 1em; height: 1em; vertical-align: middle;" /> <span style="vertical-align: middle;">内存信息</span> <Odometer style="width: 1em; height: 1em; vertical-align: middle" /> <span style="vertical-align: middle">内存信息</span>
</template> </template>
<div class="el-table el-table--enable-row-hover el-table--medium"> <div class="el-table el-table--enable-row-hover el-table--medium">
<div ref="usedmemory" style="height: 420px" /> <div ref="usedmemory" style="height: 420px" />
@ -133,38 +133,38 @@ const usedmemory = ref();
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const getList = async () => { const getList = async () => {
proxy?.$modal.loading("正在加载缓存监控数据,请稍候!"); proxy?.$modal.loading('正在加载缓存监控数据,请稍候!');
const res = await getCache(); const res = await getCache();
proxy?.$modal.closeLoading(); proxy?.$modal.closeLoading();
cache.value = res.data; cache.value = res.data;
const commandstatsIntance = echarts.init(commandstats.value, "macarons"); const commandstatsIntance = echarts.init(commandstats.value, 'macarons');
commandstatsIntance.setOption({ commandstatsIntance.setOption({
tooltip: { tooltip: {
trigger: "item", trigger: 'item',
formatter: "{a} <br/>{b} : {c} ({d}%)" formatter: '{a} <br/>{b} : {c} ({d}%)'
}, },
series: [ series: [
{ {
name: "命令", name: '命令',
type: "pie", type: 'pie',
roseType: "radius", roseType: 'radius',
radius: [15, 95], radius: [15, 95],
center: ["50%", "38%"], center: ['50%', '38%'],
data: res.data.commandStats, data: res.data.commandStats,
animationEasing: "cubicInOut", animationEasing: 'cubicInOut',
animationDuration: 1000 animationDuration: 1000
} }
] ]
}); });
const usedmemoryInstance = echarts.init(usedmemory.value, "macarons"); const usedmemoryInstance = echarts.init(usedmemory.value, 'macarons');
usedmemoryInstance.setOption({ usedmemoryInstance.setOption({
tooltip: { tooltip: {
formatter: "{b} <br/>{a} : " + cache.value.info.used_memory_human formatter: '{b} <br/>{a} : ' + cache.value.info.used_memory_human
}, },
series: [ series: [
{ {
name: "峰值", name: '峰值',
type: "gauge", type: 'gauge',
min: 0, min: 0,
max: 1000, max: 1000,
detail: { detail: {
@ -173,19 +173,19 @@ const getList = async () => {
data: [ data: [
{ {
value: parseFloat(cache.value.info.used_memory_human), value: parseFloat(cache.value.info.used_memory_human),
name: "内存消耗" name: '内存消耗'
} }
] ]
} }
] ]
})
window.addEventListener("resize",()=>{
commandstatsIntance.resize()
usedmemoryInstance.resize()
}); });
} window.addEventListener('resize', () => {
commandstatsIntance.resize();
usedmemoryInstance.resize();
});
};
onMounted(() => { onMounted(() => {
getList(); getList();
}) });
</script> </script>

View File

@ -1,14 +1,14 @@
<template> <template>
<div class="p-2"> <div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="mb-[10px]" v-show="showSearch"> <div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover"> <el-card shadow="hover">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px"> <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px">
<el-form-item label="登录地址" prop="ipaddr"> <el-form-item label="登录地址" prop="ipaddr">
<el-input v-model="queryParams.ipaddr" placeholder="请输入登录地址" clearable style="width: 240px;" @keyup.enter="handleQuery" /> <el-input v-model="queryParams.ipaddr" placeholder="请输入登录地址" clearable style="width: 240px" @keyup.enter="handleQuery" />
</el-form-item> </el-form-item>
<el-form-item label="用户名称" prop="userName"> <el-form-item label="用户名称" prop="userName">
<el-input v-model="queryParams.userName" placeholder="请输入用户名称" clearable style="width: 240px;" @keyup.enter="handleQuery" /> <el-input v-model="queryParams.userName" placeholder="请输入用户名称" clearable style="width: 240px" @keyup.enter="handleQuery" />
</el-form-item> </el-form-item>
<el-form-item label="状态" prop="status"> <el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="登录状态" clearable style="width: 240px"> <el-select v-model="queryParams.status" placeholder="登录状态" clearable style="width: 240px">
@ -39,22 +39,22 @@
<template #header> <template #header>
<el-row :gutter="10" class="mb8"> <el-row :gutter="10" class="mb8">
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['monitor:logininfor:remove']"> <el-button v-hasPermi="['monitor:logininfor:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">
删除 删除
</el-button> </el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="danger" plain icon="Delete" @click="handleClean" v-hasPermi="['monitor:logininfor:remove']">清空</el-button> <el-button v-hasPermi="['monitor:logininfor:remove']" type="danger" plain icon="Delete" @click="handleClean">清空</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="primary" plain icon="Unlock" :disabled="single" @click="handleUnlock" v-hasPermi="['monitor:logininfor:unlock']"> <el-button v-hasPermi="['monitor:logininfor:unlock']" type="primary" plain icon="Unlock" :disabled="single" @click="handleUnlock">
解锁 解锁
</el-button> </el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['monitor:logininfor:export']">导出</el-button> <el-button v-hasPermi="['monitor:logininfor:export']" type="warning" plain icon="Download" @click="handleExport">导出</el-button>
</el-col> </el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> <right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
</el-row> </el-row>
</template> </template>
@ -62,8 +62,8 @@
ref="loginInfoTableRef" ref="loginInfoTableRef"
v-loading="loading" v-loading="loading"
:data="loginInfoList" :data="loginInfoList"
@selection-change="handleSelectionChange"
:default-sort="defaultSort" :default-sort="defaultSort"
@selection-change="handleSelectionChange"
@sort-change="handleSortChange" @sort-change="handleSortChange"
> >
<el-table-column type="selection" width="55" align="center" /> <el-table-column type="selection" width="55" align="center" />
@ -99,18 +99,18 @@
</el-table-column> </el-table-column>
</el-table> </el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
</el-card> </el-card>
</div> </div>
</template> </template>
<script setup name="Logininfor" lang="ts"> <script setup name="Logininfor" lang="ts">
import { list, delLoginInfo, cleanLoginInfo, unlockLoginInfo } from "@/api/monitor/loginInfo"; import { list, delLoginInfo, cleanLoginInfo, unlockLoginInfo } from '@/api/monitor/loginInfo';
import { LoginInfoQuery, LoginInfoVO } from "@/api/monitor/loginInfo/types"; import { LoginInfoQuery, LoginInfoVO } from '@/api/monitor/loginInfo/types';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { sys_device_type } = toRefs<any>(proxy?.useDict("sys_device_type")); const { sys_device_type } = toRefs<any>(proxy?.useDict('sys_device_type'));
const { sys_common_status } = toRefs<any>(proxy?.useDict("sys_common_status")); const { sys_common_status } = toRefs<any>(proxy?.useDict('sys_common_status'));
const loginInfoList = ref<LoginInfoVO[]>([]); const loginInfoList = ref<LoginInfoVO[]>([]);
const loading = ref(true); const loading = ref(true);
@ -120,85 +120,89 @@ const single = ref(true);
const multiple = ref(true); const multiple = ref(true);
const selectName = ref<Array<string>>([]); const selectName = ref<Array<string>>([]);
const total = ref(0); const total = ref(0);
const dateRange = ref<[DateModelType,DateModelType]>(['', '']); const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
const defaultSort = ref<any>({ prop: "loginTime", order: "descending" }); const defaultSort = ref<any>({ prop: 'loginTime', order: 'descending' });
const queryFormRef = ref<ElFormInstance>(); const queryFormRef = ref<ElFormInstance>();
const loginInfoTableRef = ref<ElTableInstance>(); const loginInfoTableRef = ref<ElTableInstance>();
// 查询参数 // 查询参数
const queryParams = ref<LoginInfoQuery>({ const queryParams = ref<LoginInfoQuery>({
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
ipaddr: '', ipaddr: '',
userName: '', userName: '',
status: '', status: '',
orderByColumn: defaultSort.value.prop, orderByColumn: defaultSort.value.prop,
isAsc: defaultSort.value.order isAsc: defaultSort.value.order
}); });
/** 查询登录日志列表 */ /** 查询登录日志列表 */
const getList = async () => { const getList = async () => {
loading.value = true; loading.value = true;
const res = await list(proxy?.addDateRange(queryParams.value, dateRange.value)); const res = await list(proxy?.addDateRange(queryParams.value, dateRange.value));
loginInfoList.value = res.rows; loginInfoList.value = res.rows;
total.value = res.total; total.value = res.total;
loading.value = false; loading.value = false;
} };
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
queryParams.value.pageNum = 1; queryParams.value.pageNum = 1;
getList(); getList();
} };
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
dateRange.value = ['', '']; dateRange.value = ['', ''];
queryFormRef.value?.resetFields(); queryFormRef.value?.resetFields();
queryParams.value.pageNum = 1; queryParams.value.pageNum = 1;
loginInfoTableRef.value?.sort(defaultSort.value.prop, defaultSort.value.order); loginInfoTableRef.value?.sort(defaultSort.value.prop, defaultSort.value.order);
} };
/** 多选框选中数据 */ /** 多选框选中数据 */
const handleSelectionChange = (selection: LoginInfoVO[]) => { const handleSelectionChange = (selection: LoginInfoVO[]) => {
ids.value = selection.map(item => item.infoId); ids.value = selection.map((item) => item.infoId);
multiple.value = !selection.length; multiple.value = !selection.length;
single.value = selection.length != 1; single.value = selection.length != 1;
selectName.value = selection.map(item => item.userName); selectName.value = selection.map((item) => item.userName);
} };
/** 排序触发事件 */ /** 排序触发事件 */
const handleSortChange = (column: any) => { const handleSortChange = (column: any) => {
queryParams.value.orderByColumn = column.prop; queryParams.value.orderByColumn = column.prop;
queryParams.value.isAsc = column.order; queryParams.value.isAsc = column.order;
getList(); getList();
} };
/** 删除按钮操作 */ /** 删除按钮操作 */
const handleDelete = async (row?: LoginInfoVO) => { const handleDelete = async (row?: LoginInfoVO) => {
const infoIds = row?.infoId || ids.value; const infoIds = row?.infoId || ids.value;
await proxy?.$modal.confirm('是否确认删除访问编号为"' + infoIds + '"的数据项?'); await proxy?.$modal.confirm('是否确认删除访问编号为"' + infoIds + '"的数据项?');
await delLoginInfo(infoIds); await delLoginInfo(infoIds);
await getList(); await getList();
proxy?.$modal.msgSuccess("删除成功"); proxy?.$modal.msgSuccess('删除成功');
} };
/** 清空按钮操作 */ /** 清空按钮操作 */
const handleClean = async () => { const handleClean = async () => {
await proxy?.$modal.confirm("是否确认清空所有登录日志数据项?"); await proxy?.$modal.confirm('是否确认清空所有登录日志数据项?');
await cleanLoginInfo(); await cleanLoginInfo();
await getList(); await getList();
proxy?.$modal.msgSuccess("清空成功"); proxy?.$modal.msgSuccess('清空成功');
} };
/** 解锁按钮操作 */ /** 解锁按钮操作 */
const handleUnlock = async () => { const handleUnlock = async () => {
const username = selectName.value; const username = selectName.value;
await proxy?.$modal.confirm('是否确认解锁用户"' + username + '"数据项?'); await proxy?.$modal.confirm('是否确认解锁用户"' + username + '"数据项?');
await unlockLoginInfo(username); await unlockLoginInfo(username);
proxy?.$modal.msgSuccess("用户" + username + "解锁成功"); proxy?.$modal.msgSuccess('用户' + username + '解锁成功');
} };
/** 导出按钮操作 */ /** 导出按钮操作 */
const handleExport = () => { const handleExport = () => {
proxy?.download("monitor/logininfor/export", { proxy?.download(
...queryParams.value, 'monitor/logininfor/export',
}, `config_${new Date().getTime()}.xlsx`); {
} ...queryParams.value
},
`config_${new Date().getTime()}.xlsx`
);
};
onMounted(() => { onMounted(() => {
getList(); getList();
}) });
</script> </script>

View File

@ -2,7 +2,7 @@
<div class="p-2"> <div class="p-2">
<div class="mb-[10px]"> <div class="mb-[10px]">
<el-card shadow="hover"> <el-card shadow="hover">
<el-form :model="queryParams" ref="queryFormRef" :inline="true"> <el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="登录地址" prop="ipaddr"> <el-form-item label="登录地址" prop="ipaddr">
<el-input v-model="queryParams.ipaddr" placeholder="请输入登录地址" clearable style="width: 200px" @keyup.enter="handleQuery" /> <el-input v-model="queryParams.ipaddr" placeholder="请输入登录地址" clearable style="width: 200px" @keyup.enter="handleQuery" />
</el-form-item> </el-form-item>
@ -20,7 +20,7 @@
<el-table <el-table
v-loading="loading" v-loading="loading"
:data="onlineList.slice((queryParams.pageNum - 1) * queryParams.pageSize, queryParams.pageNum * queryParams.pageSize)" :data="onlineList.slice((queryParams.pageNum - 1) * queryParams.pageSize, queryParams.pageNum * queryParams.pageSize)"
style="width: 100%;" style="width: 100%"
> >
<el-table-column label="序号" width="50" type="index" align="center"> <el-table-column label="序号" width="50" type="index" align="center">
<template #default="scope"> <template #default="scope">
@ -48,14 +48,14 @@
<el-table-column label="操作" align="center" class-name="small-padding fixed-width"> <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope"> <template #default="scope">
<el-tooltip content="强退" placement="top"> <el-tooltip content="强退" placement="top">
<el-button link type="primary" icon="Delete" @click="handleForceLogout(scope.row)" v-hasPermi="['monitor:online:forceLogout']"> <el-button v-hasPermi="['monitor:online:forceLogout']" link type="primary" icon="Delete" @click="handleForceLogout(scope.row)">
</el-button> </el-button>
</el-tooltip> </el-tooltip>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" /> <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" />
</el-card> </el-card>
</div> </div>
</template> </template>
@ -67,7 +67,7 @@ import api from "@/api/system/user";
import {to} from "await-to-js"; import {to} from "await-to-js";
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { sys_device_type } = toRefs<any>(proxy?.useDict("sys_device_type")); const { sys_device_type } = toRefs<any>(proxy?.useDict('sys_device_type'));
const onlineList = ref<OnlineVO[]>([]); const onlineList = ref<OnlineVO[]>([]);
const loading = ref(true); const loading = ref(true);
@ -89,17 +89,17 @@ const getList = async () => {
onlineList.value = res.rows; onlineList.value = res.rows;
total.value = res.total; total.value = res.total;
loading.value = false; loading.value = false;
} };
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
queryParams.value.pageNum = 1; queryParams.value.pageNum = 1;
getList(); getList();
} };
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
queryFormRef.value?.resetFields(); queryFormRef.value?.resetFields();
handleQuery(); handleQuery();
} };
/** 强退按钮操作 */ /** 强退按钮操作 */
const handleForceLogout = async (row: OnlineVO) => { const handleForceLogout = async (row: OnlineVO) => {
const [err] = await to(proxy?.$modal.confirm('是否确认强退名称为"' + row.userName + '"的用户?') as any); const [err] = await to(proxy?.$modal.confirm('是否确认强退名称为"' + row.userName + '"的用户?') as any);
@ -112,5 +112,5 @@ const handleForceLogout = async (row: OnlineVO) => {
onMounted(() => { onMounted(() => {
getList(); getList();
}) });
</script> </script>

View File

@ -3,15 +3,15 @@
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="mb-[10px]"> <div class="mb-[10px]">
<el-card shadow="hover"> <el-card shadow="hover">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px"> <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px">
<el-form-item label="操作地址" prop="operIp"> <el-form-item label="操作地址" prop="operIp">
<el-input v-model="queryParams.operIp" placeholder="请输入操作地址" clearable style="width: 240px;" @keyup.enter="handleQuery"/> <el-input v-model="queryParams.operIp" placeholder="请输入操作地址" clearable style="width: 240px" @keyup.enter="handleQuery" />
</el-form-item> </el-form-item>
<el-form-item label="系统模块" prop="title"> <el-form-item label="系统模块" prop="title">
<el-input v-model="queryParams.title" placeholder="请输入系统模块" clearable style="width: 240px;" @keyup.enter="handleQuery" /> <el-input v-model="queryParams.title" placeholder="请输入系统模块" clearable style="width: 240px" @keyup.enter="handleQuery" />
</el-form-item> </el-form-item>
<el-form-item label="操作人员" prop="operName"> <el-form-item label="操作人员" prop="operName">
<el-input v-model="queryParams.operName" placeholder="请输入操作人员" clearable style="width: 240px;" @keyup.enter="handleQuery" /> <el-input v-model="queryParams.operName" placeholder="请输入操作人员" clearable style="width: 240px" @keyup.enter="handleQuery" />
</el-form-item> </el-form-item>
<el-form-item label="类型" prop="businessType"> <el-form-item label="类型" prop="businessType">
<el-select v-model="queryParams.businessType" placeholder="操作类型" clearable style="width: 240px"> <el-select v-model="queryParams.businessType" placeholder="操作类型" clearable style="width: 240px">
@ -47,17 +47,17 @@
<template #header> <template #header>
<el-row :gutter="10" class="mb8"> <el-row :gutter="10" class="mb8">
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['monitor:operlog:remove']"> <el-button v-hasPermi="['monitor:operlog:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">
删除 删除
</el-button> </el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="danger" plain icon="WarnTriangleFilled" @click="handleClean" v-hasPermi="['monitor:operlog:remove']">清空</el-button> <el-button v-hasPermi="['monitor:operlog:remove']" type="danger" plain icon="WarnTriangleFilled" @click="handleClean">清空</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['monitor:operlog:export']">导出</el-button> <el-button v-hasPermi="['monitor:operlog:export']" type="warning" plain icon="Download" @click="handleExport">导出</el-button>
</el-col> </el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> <right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
</el-row> </el-row>
</template> </template>
@ -65,8 +65,8 @@
ref="operLogTableRef" ref="operLogTableRef"
v-loading="loading" v-loading="loading"
:data="operlogList" :data="operlogList"
@selection-change="handleSelectionChange"
:default-sort="defaultSort" :default-sort="defaultSort"
@selection-change="handleSelectionChange"
@sort-change="handleSortChange" @sort-change="handleSortChange"
> >
<el-table-column type="selection" width="50" align="center" /> <el-table-column type="selection" width="50" align="center" />
@ -114,20 +114,20 @@
<el-table-column label="操作" fixed="right" align="center" class-name="small-padding fixed-width"> <el-table-column label="操作" fixed="right" align="center" class-name="small-padding fixed-width">
<template #default="scope"> <template #default="scope">
<el-tooltip content="详细" placement="top"> <el-tooltip content="详细" placement="top">
<el-button link type="primary" icon="View" @click="handleView(scope.row)" v-hasPermi="['monitor:operlog:query']"> </el-button> <el-button v-hasPermi="['monitor:operlog:query']" link type="primary" icon="View" @click="handleView(scope.row)"> </el-button>
</el-tooltip> </el-tooltip>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
</el-card> </el-card>
<!-- 操作日志详细 --> <!-- 操作日志详细 -->
<el-dialog title="操作日志详细" v-model="dialog.visible" width="700px" append-to-body> <el-dialog v-model="dialog.visible" title="操作日志详细" width="700px" append-to-body>
<el-form :model="form" label-width="100px"> <el-form :model="form" label-width="100px">
<el-row> <el-row>
<el-col :span="24"> <el-col :span="24">
<el-form-item label="登录信息:">{{ form.operName }} / {{form.deptName}} / {{ form.operIp }} / {{ form.operLocation }}</el-form-item> <el-form-item label="登录信息:">{{ form.operName }} / {{ form.deptName }} / {{ form.operIp }} / {{ form.operLocation }}</el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="请求信息:">{{ form.requestMethod }} {{ form.operUrl }}</el-form-item> <el-form-item label="请求信息:">{{ form.requestMethod }} {{ form.operUrl }}</el-form-item>
@ -157,7 +157,7 @@
<el-form-item label="操作时间:">{{ parseTime(form.operTime) }}</el-form-item> <el-form-item label="操作时间:">{{ parseTime(form.operTime) }}</el-form-item>
</el-col> </el-col>
<el-col :span="24"> <el-col :span="24">
<el-form-item label="异常信息:" v-if="form.status === 1">{{ form.errorMsg }}</el-form-item> <el-form-item v-if="form.status === 1" label="异常信息:">{{ form.errorMsg }}</el-form-item>
</el-col> </el-col>
</el-row> </el-row>
</el-form> </el-form>
@ -175,7 +175,7 @@ import { list, delOperlog, cleanOperlog } from '@/api/monitor/operlog';
import { OperLogForm, OperLogQuery, OperLogVO } from '@/api/monitor/operlog/types'; import { OperLogForm, OperLogQuery, OperLogVO } from '@/api/monitor/operlog/types';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { sys_oper_type, sys_common_status } = toRefs<any>(proxy?.useDict("sys_oper_type", "sys_common_status")); const { sys_oper_type, sys_common_status } = toRefs<any>(proxy?.useDict('sys_oper_type', 'sys_common_status'));
const operlogList = ref<OperLogVO[]>([]); const operlogList = ref<OperLogVO[]>([]);
const loading = ref(true); const loading = ref(true);
@ -184,7 +184,7 @@ const ids = ref<Array<number | string>>([]);
const multiple = ref(true); const multiple = ref(true);
const total = ref(0); const total = ref(0);
const dateRange = ref<[DateModelType, DateModelType]>(['', '']); const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
const defaultSort = ref<any>({ prop: "operTime", order: "descending" }); const defaultSort = ref<any>({ prop: 'operTime', order: 'descending' });
const operLogTableRef = ref<ElTableInstance>(); const operLogTableRef = ref<ElTableInstance>();
const queryFormRef = ref<ElFormInstance>(); const queryFormRef = ref<ElFormInstance>();
@ -194,7 +194,6 @@ const dialog = reactive<DialogOption>({
title: '' title: ''
}); });
const data = reactive<PageData<OperLogForm, OperLogQuery>>({ const data = reactive<PageData<OperLogForm, OperLogQuery>>({
form: { form: {
operId: undefined, operId: undefined,
@ -240,63 +239,67 @@ const getList = async () => {
operlogList.value = res.rows; operlogList.value = res.rows;
total.value = res.total; total.value = res.total;
loading.value = false; loading.value = false;
} };
/** 操作日志类型字典翻译 */ /** 操作日志类型字典翻译 */
const typeFormat = (row: OperLogForm) => { const typeFormat = (row: OperLogForm) => {
return proxy?.selectDictLabel(sys_oper_type.value, row.businessType); return proxy?.selectDictLabel(sys_oper_type.value, row.businessType);
} };
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
queryParams.value.pageNum = 1; queryParams.value.pageNum = 1;
getList(); getList();
} };
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
dateRange.value = ['', '']; dateRange.value = ['', ''];
queryFormRef.value?.resetFields(); queryFormRef.value?.resetFields();
queryParams.value.pageNum = 1; queryParams.value.pageNum = 1;
operLogTableRef.value?.sort(defaultSort.value.prop, defaultSort.value.order); operLogTableRef.value?.sort(defaultSort.value.prop, defaultSort.value.order);
} };
/** 多选框选中数据 */ /** 多选框选中数据 */
const handleSelectionChange = (selection: OperLogVO[]) => { const handleSelectionChange = (selection: OperLogVO[]) => {
ids.value = selection.map(item => item.operId); ids.value = selection.map((item) => item.operId);
multiple.value = !selection.length; multiple.value = !selection.length;
} };
/** 排序触发事件 */ /** 排序触发事件 */
const handleSortChange = (column: any) => { const handleSortChange = (column: any) => {
queryParams.value.orderByColumn = column.prop; queryParams.value.orderByColumn = column.prop;
queryParams.value.isAsc = column.order; queryParams.value.isAsc = column.order;
getList(); getList();
} };
/** 详细按钮操作 */ /** 详细按钮操作 */
const handleView = (row: OperLogVO) => { const handleView = (row: OperLogVO) => {
dialog.visible = true; dialog.visible = true;
form.value = row; form.value = row;
} };
/** 删除按钮操作 */ /** 删除按钮操作 */
const handleDelete = async (row?: OperLogVO) => { const handleDelete = async (row?: OperLogVO) => {
const operIds = row?.operId || ids.value; const operIds = row?.operId || ids.value;
await proxy?.$modal.confirm('是否确认删除日志编号为"' + operIds + '"的数据项?'); await proxy?.$modal.confirm('是否确认删除日志编号为"' + operIds + '"的数据项?');
await delOperlog(operIds); await delOperlog(operIds);
await getList(); await getList();
proxy?.$modal.msgSuccess("删除成功"); proxy?.$modal.msgSuccess('删除成功');
} };
/** 清空按钮操作 */ /** 清空按钮操作 */
const handleClean = async () => { const handleClean = async () => {
await proxy?.$modal.confirm("是否确认清空所有操作日志数据项?"); await proxy?.$modal.confirm('是否确认清空所有操作日志数据项?');
await cleanOperlog(); await cleanOperlog();
await getList(); await getList();
proxy?.$modal.msgSuccess("清空成功"); proxy?.$modal.msgSuccess('清空成功');
} };
/** 导出按钮操作 */ /** 导出按钮操作 */
const handleExport = () => { const handleExport = () => {
proxy?.download("monitor/operlog/export", { proxy?.download(
...queryParams.value, 'monitor/operlog/export',
}, `config_${new Date().getTime()}.xlsx`); {
} ...queryParams.value
},
`config_${new Date().getTime()}.xlsx`
);
};
onMounted(() => { onMounted(() => {
getList(); getList();
}) });
</script> </script>

View File

@ -3,12 +3,12 @@
</template> </template>
<script setup> <script setup>
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router';
const route = useRoute(); const route = useRoute();
const router = useRouter(); const router = useRouter();
const { params, query } = route const { params, query } = route;
const { path } = params const { path } = params;
router.replace({ path: '/' + path, query }) router.replace({ path: '/' + path, query });
</script> </script>

View File

@ -2,7 +2,7 @@
<div class="register"> <div class="register">
<el-form ref="registerRef" :model="registerForm" :rules="registerRules" class="register-form"> <el-form ref="registerRef" :model="registerForm" :rules="registerRules" class="register-form">
<h3 class="title">RuoYi-Vue-Plus多租户管理系统</h3> <h3 class="title">RuoYi-Vue-Plus多租户管理系统</h3>
<el-form-item prop="tenantId" v-if="tenantEnabled"> <el-form-item v-if="tenantEnabled" prop="tenantId">
<el-select v-model="registerForm.tenantId" filterable placeholder="请选择/输入公司名称" style="width: 100%"> <el-select v-model="registerForm.tenantId" filterable placeholder="请选择/输入公司名称" style="width: 100%">
<el-option v-for="item in tenantList" :key="item.tenantId" :label="item.companyName" :value="item.tenantId"> </el-option> <el-option v-for="item in tenantList" :key="item.tenantId" :label="item.companyName" :value="item.tenantId"> </el-option>
<template #prefix><svg-icon icon-class="company" class="el-input__icon input-icon" /></template> <template #prefix><svg-icon icon-class="company" class="el-input__icon input-icon" /></template>
@ -30,20 +30,20 @@
<template #prefix><svg-icon icon-class="password" class="el-input__icon input-icon" /></template> <template #prefix><svg-icon icon-class="password" class="el-input__icon input-icon" /></template>
</el-input> </el-input>
</el-form-item> </el-form-item>
<el-form-item prop="code" v-if="captchaEnabled"> <el-form-item v-if="captchaEnabled" prop="code">
<el-input size="large" v-model="registerForm.code" auto-complete="off" placeholder="验证码" style="width: 63%" @keyup.enter="handleRegister"> <el-input v-model="registerForm.code" size="large" auto-complete="off" placeholder="验证码" style="width: 63%" @keyup.enter="handleRegister">
<template #prefix><svg-icon icon-class="validCode" class="el-input__icon input-icon" /></template> <template #prefix><svg-icon icon-class="validCode" class="el-input__icon input-icon" /></template>
</el-input> </el-input>
<div class="register-code"> <div class="register-code">
<img :src="codeUrl" @click="getCode" class="register-code-img" /> <img :src="codeUrl" class="register-code-img" @click="getCode" />
</div> </div>
</el-form-item> </el-form-item>
<el-form-item style="width:100%;"> <el-form-item style="width: 100%">
<el-button :loading="loading" size="large" type="primary" style="width:100%;" @click.prevent="handleRegister"> <el-button :loading="loading" size="large" type="primary" style="width: 100%" @click.prevent="handleRegister">
<span v-if="!loading"> </span> <span v-if="!loading"> </span>
<span v-else> 中...</span> <span v-else> 中...</span>
</el-button> </el-button>
<div style="float: right;"> <div style="float: right">
<router-link class="link-type" :to="'/login'">使用已有账户登录</router-link> <router-link class="link-type" :to="'/login'">使用已有账户登录</router-link>
</div> </div>
</el-form-item> </el-form-item>
@ -63,46 +63,43 @@ import { to } from 'await-to-js';
const router = useRouter(); const router = useRouter();
const registerForm = ref<RegisterForm>({ const registerForm = ref<RegisterForm>({
tenantId: "", tenantId: '',
username: "", username: '',
password: "", password: '',
confirmPassword: "", confirmPassword: '',
code: "", code: '',
uuid: "", uuid: '',
userType: "sys_user" userType: 'sys_user'
}); });
// 租户开关 // 租户开关
const tenantEnabled = ref(true); const tenantEnabled = ref(true);
const equalToPassword = (rule: any, value: string, callback: any) => { const equalToPassword = (rule: any, value: string, callback: any) => {
if (registerForm.value.password !== value) { if (registerForm.value.password !== value) {
callback(new Error("两次输入的密码不一致")); callback(new Error('两次输入的密码不一致'));
} else { } else {
callback(); callback();
} }
}; };
const registerRules: ElFormRules = { const registerRules: ElFormRules = {
tenantId: [ tenantId: [{ required: true, trigger: 'blur', message: '请输入您的租户编号' }],
{ required: true, trigger: "blur", message: "请输入您的租户编号" }
],
username: [ username: [
{ required: true, trigger: "blur", message: "请输入您的账号" }, { required: true, trigger: 'blur', message: '请输入您的账号' },
{ min: 2, max: 20, message: "用户账号长度必须介于 2 和 20 之间", trigger: "blur" } { min: 2, max: 20, message: '用户账号长度必须介于 2 和 20 之间', trigger: 'blur' }
], ],
password: [ password: [
{ required: true, trigger: "blur", message: "请输入您的密码" }, { required: true, trigger: 'blur', message: '请输入您的密码' },
{ min: 5, max: 20, message: "用户密码长度必须介于 5 和 20 之间", trigger: "blur" } { min: 5, max: 20, message: '用户密码长度必须介于 5 和 20 之间', trigger: 'blur' }
], ],
confirmPassword: [ confirmPassword: [
{ required: true, trigger: "blur", message: "请再次输入您的密码" }, { required: true, trigger: 'blur', message: '请再次输入您的密码' },
{ required: true, validator: equalToPassword, trigger: "blur" } { required: true, validator: equalToPassword, trigger: 'blur' }
], ],
code: [{ required: true, trigger: "change", message: "请输入验证码" }] code: [{ required: true, trigger: 'change', message: '请输入验证码' }]
}; };
const codeUrl = ref(""); const codeUrl = ref('');
const loading = ref(false); const loading = ref(false);
const captchaEnabled = ref(true); const captchaEnabled = ref(true);
const registerRef = ref<ElFormInstance>(); const registerRef = ref<ElFormInstance>();
@ -116,11 +113,11 @@ const handleRegister = () => {
const [err] = await to(register(registerForm.value)); const [err] = await to(register(registerForm.value));
if (!err) { if (!err) {
const username = registerForm.value.username; const username = registerForm.value.username;
await ElMessageBox.alert("<font color='red'>恭喜你,您的账号 " + username + " 注册成功!</font>", "系统提示", { await ElMessageBox.alert("<font color='red'>恭喜你,您的账号 " + username + ' 注册成功!</font>', '系统提示', {
dangerouslyUseHTMLString: true, dangerouslyUseHTMLString: true,
type: "success", type: 'success'
}); });
await router.push("/login"); await router.push('/login');
} else { } else {
loading.value = false; loading.value = false;
if (captchaEnabled) { if (captchaEnabled) {
@ -129,7 +126,7 @@ const handleRegister = () => {
} }
} }
}); });
} };
const getCode = async () => { const getCode = async () => {
const res = await getCodeImg(); const res = await getCodeImg();
@ -150,12 +147,12 @@ const initTenantList = async () => {
registerForm.value.tenantId = tenantList.value[0].tenantId; registerForm.value.tenantId = tenantList.value[0].tenantId;
} }
} }
} };
onMounted(() => { onMounted(() => {
getCode(); getCode();
initTenantList(); initTenantList();
}) });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -164,7 +161,7 @@ onMounted(() => {
justify-content: center; justify-content: center;
align-items: center; align-items: center;
height: 100%; height: 100%;
background-image: url("../assets/images/login-background.jpg"); background-image: url('../assets/images/login-background.jpg');
background-size: cover; background-size: cover;
} }

View File

@ -1,8 +1,8 @@
<template> <template>
<div class="p-2"> <div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="search" v-show="showSearch"> <div v-show="showSearch" class="search">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="100px"> <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="100px">
<el-form-item label="客户端key" prop="clientKey"> <el-form-item label="客户端key" prop="clientKey">
<el-input v-model="queryParams.clientKey" placeholder="请输入客户端key" clearable style="width: 240px" @keyup.enter="handleQuery" /> <el-input v-model="queryParams.clientKey" placeholder="请输入客户端key" clearable style="width: 240px" @keyup.enter="handleQuery" />
</el-form-item> </el-form-item>
@ -26,28 +26,28 @@
<template #header> <template #header>
<el-row :gutter="10" class="mb8"> <el-row :gutter="10" class="mb8">
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:client:add']">新增</el-button> <el-button v-hasPermi="['system:client:add']" type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['system:client:edit']"> <el-button v-hasPermi="['system:client:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()">
修改 修改
</el-button> </el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:client:remove']"> <el-button v-hasPermi="['system:client:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">
删除 删除
</el-button> </el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:client:export']">导出</el-button> <el-button v-hasPermi="['system:client:export']" type="warning" plain icon="Download" @click="handleExport">导出</el-button>
</el-col> </el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> <right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
</el-row> </el-row>
</template> </template>
<el-table v-loading="loading" :data="clientList" @selection-change="handleSelectionChange"> <el-table v-loading="loading" :data="clientList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" /> <el-table-column type="selection" width="55" align="center" />
<el-table-column label="id" align="center" prop="id" v-if="true" /> <el-table-column v-if="true" label="id" align="center" prop="id" />
<el-table-column label="客户端id" align="center" prop="clientId" /> <el-table-column label="客户端id" align="center" prop="clientId" />
<el-table-column label="客户端key" align="center" prop="clientKey" /> <el-table-column label="客户端key" align="center" prop="clientKey" />
<el-table-column label="客户端秘钥" align="center" prop="clientSecret" /> <el-table-column label="客户端秘钥" align="center" prop="clientSecret" />
@ -63,7 +63,7 @@
</el-table-column> </el-table-column>
<el-table-column label="Token活跃超时时间" align="center" prop="activeTimeout" /> <el-table-column label="Token活跃超时时间" align="center" prop="activeTimeout" />
<el-table-column label="Token固定超时时间" align="center" prop="timeout" /> <el-table-column label="Token固定超时时间" align="center" prop="timeout" />
<el-table-column label="状态" align="center" key="status"> <el-table-column key="status" label="状态" align="center">
<template #default="scope"> <template #default="scope">
<el-switch v-model="scope.row.status" active-value="0" inactive-value="1" @change="handleStatusChange(scope.row)"></el-switch> <el-switch v-model="scope.row.status" active-value="0" inactive-value="1" @change="handleStatusChange(scope.row)"></el-switch>
</template> </template>
@ -71,19 +71,19 @@
<el-table-column label="操作" align="center" class-name="small-padding fixed-width"> <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope"> <template #default="scope">
<el-tooltip content="修改" placement="top"> <el-tooltip content="修改" placement="top">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:client:edit']"></el-button> <el-button v-hasPermi="['system:client:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
</el-tooltip> </el-tooltip>
<el-tooltip content="删除" placement="top"> <el-tooltip content="删除" placement="top">
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:client:remove']"></el-button> <el-button v-hasPermi="['system:client:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
</el-tooltip> </el-tooltip>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<pagination v-show="total>0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
</el-card> </el-card>
<!-- 添加或修改客户端管理对话框 --> <!-- 添加或修改客户端管理对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body> <el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
<el-form ref="clientFormRef" :model="form" :rules="rules" label-width="100px"> <el-form ref="clientFormRef" :model="form" :rules="rules" label-width="100px">
<el-form-item label="客户端key" prop="clientKey"> <el-form-item label="客户端key" prop="clientKey">
<el-input v-model="form.clientKey" :disabled="form.id != null" placeholder="请输入客户端key" /> <el-input v-model="form.clientKey" :disabled="form.id != null" placeholder="请输入客户端key" />
@ -146,9 +146,9 @@ import { listClient, getClient, delClient, addClient, updateClient, changeStatus
import { ClientVO, ClientQuery, ClientForm } from '@/api/system/client/types'; import { ClientVO, ClientQuery, ClientForm } from '@/api/system/client/types';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { sys_normal_disable } = toRefs<any>(proxy?.useDict("sys_normal_disable")); const { sys_normal_disable } = toRefs<any>(proxy?.useDict('sys_normal_disable'));
const { sys_grant_type } = toRefs<any>(proxy?.useDict("sys_grant_type")); const { sys_grant_type } = toRefs<any>(proxy?.useDict('sys_grant_type'));
const { sys_device_type } = toRefs<any>(proxy?.useDict("sys_device_type")); const { sys_device_type } = toRefs<any>(proxy?.useDict('sys_device_type'));
const clientList = ref<ClientVO[]>([]); const clientList = ref<ClientVO[]>([]);
const buttonLoading = ref(false); const buttonLoading = ref(false);
@ -176,10 +176,10 @@ const initFormData: ClientForm = {
deviceType: undefined, deviceType: undefined,
activeTimeout: undefined, activeTimeout: undefined,
timeout: undefined, timeout: undefined,
status: undefined, status: undefined
} };
const data = reactive<PageData<ClientForm, ClientQuery>>({ const data = reactive<PageData<ClientForm, ClientQuery>>({
form: {...initFormData}, form: { ...initFormData },
queryParams: { queryParams: {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
@ -190,27 +190,15 @@ const data = reactive<PageData<ClientForm, ClientQuery>>({
deviceType: undefined, deviceType: undefined,
activeTimeout: undefined, activeTimeout: undefined,
timeout: undefined, timeout: undefined,
status: undefined, status: undefined
}, },
rules: { rules: {
id: [ id: [{ required: true, message: 'id不能为空', trigger: 'blur' }],
{ required: true, message: "id不能为空", trigger: "blur" } clientId: [{ required: true, message: '客户端id不能为空', trigger: 'blur' }],
], clientKey: [{ required: true, message: '客户端key不能为空', trigger: 'blur' }],
clientId: [ clientSecret: [{ required: true, message: '客户端秘钥不能为空', trigger: 'blur' }],
{ required: true, message: "客户端id不能为空", trigger: "blur" } grantTypeList: [{ required: true, message: '授权类型不能为空', trigger: 'change' }],
], deviceType: [{ required: true, message: '设备类型不能为空', trigger: 'change' }]
clientKey: [
{ required: true, message: "客户端key不能为空", trigger: "blur" }
],
clientSecret: [
{ required: true, message: "客户端秘钥不能为空", trigger: "blur" }
],
grantTypeList: [
{ required: true, message: "授权类型不能为空", trigger: "change" }
],
deviceType: [
{ required: true, message: "设备类型不能为空", trigger: "change" }
],
} }
}); });
@ -223,55 +211,55 @@ const getList = async () => {
clientList.value = res.rows; clientList.value = res.rows;
total.value = res.total; total.value = res.total;
loading.value = false; loading.value = false;
} };
/** 取消按钮 */ /** 取消按钮 */
const cancel = () => { const cancel = () => {
reset(); reset();
dialog.visible = false; dialog.visible = false;
} };
/** 表单重置 */ /** 表单重置 */
const reset = () => { const reset = () => {
form.value = {...initFormData}; form.value = { ...initFormData };
clientFormRef.value?.resetFields(); clientFormRef.value?.resetFields();
} };
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
queryParams.value.pageNum = 1; queryParams.value.pageNum = 1;
getList(); getList();
} };
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
queryFormRef.value?.resetFields(); queryFormRef.value?.resetFields();
handleQuery(); handleQuery();
} };
/** 多选框选中数据 */ /** 多选框选中数据 */
const handleSelectionChange = (selection: ClientVO[]) => { const handleSelectionChange = (selection: ClientVO[]) => {
ids.value = selection.map(item => item.id); ids.value = selection.map((item) => item.id);
single.value = selection.length != 1; single.value = selection.length != 1;
multiple.value = !selection.length; multiple.value = !selection.length;
} };
/** 新增按钮操作 */ /** 新增按钮操作 */
const handleAdd = () => { const handleAdd = () => {
reset(); reset();
dialog.visible = true; dialog.visible = true;
dialog.title = "添加客户端管理"; dialog.title = '添加客户端管理';
} };
/** 修改按钮操作 */ /** 修改按钮操作 */
const handleUpdate = async (row?: ClientVO) => { const handleUpdate = async (row?: ClientVO) => {
reset(); reset();
const _id = row?.id || ids.value[0] const _id = row?.id || ids.value[0];
const res = await getClient(_id); const res = await getClient(_id);
Object.assign(form.value, res.data); Object.assign(form.value, res.data);
dialog.visible = true; dialog.visible = true;
dialog.title = "修改客户端管理"; dialog.title = '修改客户端管理';
} };
/** 提交按钮 */ /** 提交按钮 */
const submitForm = () => { const submitForm = () => {
@ -279,44 +267,48 @@ const submitForm = () => {
if (valid) { if (valid) {
buttonLoading.value = true; buttonLoading.value = true;
if (form.value.id) { if (form.value.id) {
await updateClient(form.value).finally(() => buttonLoading.value = false); await updateClient(form.value).finally(() => (buttonLoading.value = false));
} else { } else {
await addClient(form.value).finally(() => buttonLoading.value = false); await addClient(form.value).finally(() => (buttonLoading.value = false));
} }
proxy?.$modal.msgSuccess("修改成功"); proxy?.$modal.msgSuccess('修改成功');
dialog.visible = false; dialog.visible = false;
await getList(); await getList();
} }
}); });
} };
/** 删除按钮操作 */ /** 删除按钮操作 */
const handleDelete = async (row?: ClientVO) => { const handleDelete = async (row?: ClientVO) => {
const _ids = row?.id || ids.value; const _ids = row?.id || ids.value;
await proxy?.$modal.confirm('是否确认删除客户端管理编号为"' + _ids + '"的数据项?').finally(() => loading.value = false); await proxy?.$modal.confirm('是否确认删除客户端管理编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
await delClient(_ids); await delClient(_ids);
proxy?.$modal.msgSuccess("删除成功"); proxy?.$modal.msgSuccess('删除成功');
await getList(); await getList();
} };
/** 导出按钮操作 */ /** 导出按钮操作 */
const handleExport = () => { const handleExport = () => {
proxy?.download('system/client/export', { proxy?.download(
...queryParams.value 'system/client/export',
}, `client_${new Date().getTime()}.xlsx`) {
} ...queryParams.value
},
`client_${new Date().getTime()}.xlsx`
);
};
/** 状态修改 */ /** 状态修改 */
const handleStatusChange = async (row: ClientVO) => { const handleStatusChange = async (row: ClientVO) => {
let text = row.status === "0" ? "启用" : "停用" let text = row.status === '0' ? '启用' : '停用';
try { try {
await proxy?.$modal.confirm('确认要"' + text + '"吗?'); await proxy?.$modal.confirm('确认要"' + text + '"吗?');
await changeStatus(row.id, row.status); await changeStatus(row.id, row.status);
proxy?.$modal.msgSuccess(text + "成功"); proxy?.$modal.msgSuccess(text + '成功');
} catch (err) { } catch (err) {
row.status = row.status === "0" ? "1" : "0"; row.status = row.status === '0' ? '1' : '0';
} }
} };
onMounted(() => { onMounted(() => {
getList(); getList();

View File

@ -1,9 +1,9 @@
<template> <template>
<div class="p-2"> <div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="mb-[10px]" v-show="showSearch"> <div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover"> <el-card shadow="hover">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px"> <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px">
<el-form-item label="参数名称" prop="configName"> <el-form-item label="参数名称" prop="configName">
<el-input v-model="queryParams.configName" placeholder="请输入参数名称" clearable style="width: 240px" @keyup.enter="handleQuery" /> <el-input v-model="queryParams.configName" placeholder="请输入参数名称" clearable style="width: 240px" @keyup.enter="handleQuery" />
</el-form-item> </el-form-item>
@ -15,7 +15,7 @@
<el-option v-for="dict in sys_yes_no" :key="dict.value" :label="dict.label" :value="dict.value" /> <el-option v-for="dict in sys_yes_no" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="创建时间" style="width: 308px;"> <el-form-item label="创建时间" style="width: 308px">
<el-date-picker <el-date-picker
v-model="dateRange" v-model="dateRange"
value-format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss"
@ -38,31 +38,31 @@
<template #header> <template #header>
<el-row :gutter="10" class="mb8"> <el-row :gutter="10" class="mb8">
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:config:add']">新增</el-button> <el-button v-hasPermi="['system:config:add']" type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['system:config:edit']"> <el-button v-hasPermi="['system:config:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()">
修改 修改
</el-button> </el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:config:remove']"> <el-button v-hasPermi="['system:config:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">
删除 删除
</el-button> </el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:config:export']">导出</el-button> <el-button v-hasPermi="['system:config:export']" type="warning" plain icon="Download" @click="handleExport">导出</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="danger" plain icon="Refresh" @click="handleRefreshCache" v-hasPermi="['system:config:remove']">刷新缓存</el-button> <el-button v-hasPermi="['system:config:remove']" type="danger" plain icon="Refresh" @click="handleRefreshCache">刷新缓存</el-button>
</el-col> </el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> <right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
</el-row> </el-row>
</template> </template>
<el-table v-loading="loading" :data="configList" @selection-change="handleSelectionChange"> <el-table v-loading="loading" :data="configList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" /> <el-table-column type="selection" width="55" align="center" />
<el-table-column label="参数主键" align="center" prop="configId" v-if="false" /> <el-table-column v-if="false" label="参数主键" align="center" prop="configId" />
<el-table-column label="参数名称" align="center" prop="configName" :show-overflow-tooltip="true" /> <el-table-column label="参数名称" align="center" prop="configName" :show-overflow-tooltip="true" />
<el-table-column label="参数键名" align="center" prop="configKey" :show-overflow-tooltip="true" /> <el-table-column label="参数键名" align="center" prop="configKey" :show-overflow-tooltip="true" />
<el-table-column label="参数键值" align="center" prop="configValue" :show-overflow-tooltip="true" /> <el-table-column label="参数键值" align="center" prop="configValue" :show-overflow-tooltip="true" />
@ -80,19 +80,19 @@
<el-table-column label="操作" align="center" width="150" class-name="small-padding fixed-width"> <el-table-column label="操作" align="center" width="150" class-name="small-padding fixed-width">
<template #default="scope"> <template #default="scope">
<el-tooltip content="修改" placement="top"> <el-tooltip content="修改" placement="top">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:config:edit']"></el-button> <el-button v-hasPermi="['system:config:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
</el-tooltip> </el-tooltip>
<el-tooltip content="删除" placement="top"> <el-tooltip content="删除" placement="top">
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:config:remove']"></el-button> <el-button v-hasPermi="['system:config:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
</el-tooltip> </el-tooltip>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
</el-card> </el-card>
<!-- 添加或修改参数配置对话框 --> <!-- 添加或修改参数配置对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body> <el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
<el-form ref="configFormRef" :model="form" :rules="rules" label-width="80px"> <el-form ref="configFormRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="参数名称" prop="configName"> <el-form-item label="参数名称" prop="configName">
<el-input v-model="form.configName" placeholder="请输入参数名称" /> <el-input v-model="form.configName" placeholder="请输入参数名称" />
@ -123,11 +123,11 @@
</template> </template>
<script setup name="Config" lang="ts"> <script setup name="Config" lang="ts">
import { listConfig, getConfig, delConfig, addConfig, updateConfig, refreshCache } from "@/api/system/config"; import { listConfig, getConfig, delConfig, addConfig, updateConfig, refreshCache } from '@/api/system/config';
import { ConfigForm, ConfigQuery, ConfigVO } from "@/api/system/config/types"; import { ConfigForm, ConfigQuery, ConfigVO } from '@/api/system/config/types';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { sys_yes_no } = toRefs<any>(proxy?.useDict("sys_yes_no")); const { sys_yes_no } = toRefs<any>(proxy?.useDict('sys_yes_no'));
const configList = ref<ConfigVO[]>([]); const configList = ref<ConfigVO[]>([]);
const loading = ref(true); const loading = ref(true);
@ -149,9 +149,9 @@ const initFormData: ConfigForm = {
configName: '', configName: '',
configKey: '', configKey: '',
configValue: '', configValue: '',
configType: "Y", configType: 'Y',
remark: '' remark: ''
} };
const data = reactive<PageData<ConfigForm, ConfigQuery>>({ const data = reactive<PageData<ConfigForm, ConfigQuery>>({
form: { ...initFormData }, form: { ...initFormData },
queryParams: { queryParams: {
@ -159,12 +159,12 @@ const data = reactive<PageData<ConfigForm, ConfigQuery>>({
pageSize: 10, pageSize: 10,
configName: '', configName: '',
configKey: '', configKey: '',
configType: '', configType: ''
}, },
rules: { rules: {
configName: [{ required: true, message: "参数名称不能为空", trigger: "blur" }], configName: [{ required: true, message: '参数名称不能为空', trigger: 'blur' }],
configKey: [{ required: true, message: "参数键名不能为空", trigger: "blur" }], configKey: [{ required: true, message: '参数键名不能为空', trigger: 'blur' }],
configValue: [{ required: true, message: "参数键值不能为空", trigger: "blur" }] configValue: [{ required: true, message: '参数键值不能为空', trigger: 'blur' }]
} }
}); });
@ -177,40 +177,40 @@ const getList = async () => {
configList.value = res.rows; configList.value = res.rows;
total.value = res.total; total.value = res.total;
loading.value = false; loading.value = false;
} };
/** 取消按钮 */ /** 取消按钮 */
const cancel = () => { const cancel = () => {
reset(); reset();
dialog.visible = false; dialog.visible = false;
} };
/** 表单重置 */ /** 表单重置 */
const reset = () => { const reset = () => {
form.value = { ...initFormData }; form.value = { ...initFormData };
configFormRef.value?.resetFields(); configFormRef.value?.resetFields();
} };
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
queryParams.value.pageNum = 1; queryParams.value.pageNum = 1;
getList(); getList();
} };
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
dateRange.value = ['', '']; dateRange.value = ['', ''];
queryFormRef.value?.resetFields(); queryFormRef.value?.resetFields();
handleQuery(); handleQuery();
} };
/** 多选框选中数据 */ /** 多选框选中数据 */
const handleSelectionChange = (selection: ConfigVO[]) => { const handleSelectionChange = (selection: ConfigVO[]) => {
ids.value = selection.map(item => item.configId); ids.value = selection.map((item) => item.configId);
single.value = selection.length != 1; single.value = selection.length != 1;
multiple.value = !selection.length; multiple.value = !selection.length;
} };
/** 新增按钮操作 */ /** 新增按钮操作 */
const handleAdd = () => { const handleAdd = () => {
reset(); reset();
dialog.visible = true; dialog.visible = true;
dialog.title = "添加参数"; dialog.title = '添加参数';
} };
/** 修改按钮操作 */ /** 修改按钮操作 */
const handleUpdate = async (row?: ConfigVO) => { const handleUpdate = async (row?: ConfigVO) => {
reset(); reset();
@ -218,40 +218,44 @@ const handleUpdate = async (row?: ConfigVO) => {
const res = await getConfig(configId); const res = await getConfig(configId);
Object.assign(form.value, res.data); Object.assign(form.value, res.data);
dialog.visible = true; dialog.visible = true;
dialog.title = "修改参数"; dialog.title = '修改参数';
} };
/** 提交按钮 */ /** 提交按钮 */
const submitForm = () => { const submitForm = () => {
configFormRef.value?.validate(async (valid: boolean) => { configFormRef.value?.validate(async (valid: boolean) => {
if (valid) { if (valid) {
form.value.configId ? await updateConfig(form.value) : await addConfig(form.value); form.value.configId ? await updateConfig(form.value) : await addConfig(form.value);
proxy?.$modal.msgSuccess("操作成功"); proxy?.$modal.msgSuccess('操作成功');
dialog.visible = false; dialog.visible = false;
await getList(); await getList();
} }
}); });
} };
/** 删除按钮操作 */ /** 删除按钮操作 */
const handleDelete = async (row?: ConfigVO) => { const handleDelete = async (row?: ConfigVO) => {
const configIds = row?.configId || ids.value; const configIds = row?.configId || ids.value;
await proxy?.$modal.confirm('是否确认删除参数编号为"' + configIds + '"的数据项?'); await proxy?.$modal.confirm('是否确认删除参数编号为"' + configIds + '"的数据项?');
await delConfig(configIds); await delConfig(configIds);
await getList(); await getList();
proxy?.$modal.msgSuccess("删除成功"); proxy?.$modal.msgSuccess('删除成功');
} };
/** 导出按钮操作 */ /** 导出按钮操作 */
const handleExport = () => { const handleExport = () => {
proxy?.download("system/config/export", { proxy?.download(
...queryParams.value 'system/config/export',
}, `config_${new Date().getTime()}.xlsx`); {
} ...queryParams.value
},
`config_${new Date().getTime()}.xlsx`
);
};
/** 刷新缓存按钮操作 */ /** 刷新缓存按钮操作 */
const handleRefreshCache = async () => { const handleRefreshCache = async () => {
await refreshCache(); await refreshCache();
proxy?.$modal.msgSuccess("刷新缓存成功"); proxy?.$modal.msgSuccess('刷新缓存成功');
} };
onMounted(() => { onMounted(() => {
getList(); getList();
}) });
</script> </script>

View File

@ -1,7 +1,7 @@
<template> <template>
<div class="p-2"> <div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="mb-[10px]" v-show="showSearch"> <div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover"> <el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px"> <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px">
<el-form-item label="部门名称" prop="deptName"> <el-form-item label="部门名称" prop="deptName">
@ -25,21 +25,21 @@
<template #header> <template #header>
<el-row :gutter="10"> <el-row :gutter="10">
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd()" v-hasPermi="['system:dept:add']">新增 </el-button> <el-button v-hasPermi="['system:dept:add']" type="primary" plain icon="Plus" @click="handleAdd()">新增 </el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="info" plain icon="Sort" @click="handleToggleExpandAll">展开/折叠</el-button> <el-button type="info" plain icon="Sort" @click="handleToggleExpandAll">展开/折叠</el-button>
</el-col> </el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> <right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
</el-row> </el-row>
</template> </template>
<el-table <el-table
ref="deptTableRef"
v-loading="loading" v-loading="loading"
:data="deptList" :data="deptList"
row-key="deptId" row-key="deptId"
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }" :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
ref="deptTableRef"
:default-expand-all="isExpandAll" :default-expand-all="isExpandAll"
> >
<el-table-column prop="deptName" label="部门名称" width="260"></el-table-column> <el-table-column prop="deptName" label="部门名称" width="260"></el-table-column>
@ -57,23 +57,23 @@
<el-table-column fixed="right" align="center" label="操作"> <el-table-column fixed="right" align="center" label="操作">
<template #default="scope"> <template #default="scope">
<el-tooltip content="修改" placement="top"> <el-tooltip content="修改" placement="top">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:dept:edit']" /> <el-button v-hasPermi="['system:dept:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)" />
</el-tooltip> </el-tooltip>
<el-tooltip content="新增" placement="top"> <el-tooltip content="新增" placement="top">
<el-button link type="primary" icon="Plus" @click="handleAdd(scope.row)" v-hasPermi="['system:dept:add']" /> <el-button v-hasPermi="['system:dept:add']" link type="primary" icon="Plus" @click="handleAdd(scope.row)" />
</el-tooltip> </el-tooltip>
<el-tooltip content="删除" placement="top"> <el-tooltip content="删除" placement="top">
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:dept:remove']" /> <el-button v-hasPermi="['system:dept:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)" />
</el-tooltip> </el-tooltip>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
</el-card> </el-card>
<el-dialog :title="dialog.title" v-model="dialog.visible" destroy-on-close append-to-bod width="600px"> <el-dialog v-model="dialog.visible" :title="dialog.title" destroy-on-close append-to-bod width="600px">
<el-form ref="deptFormRef" :model="form" :rules="rules" label-width="80px"> <el-form ref="deptFormRef" :model="form" :rules="rules" label-width="80px">
<el-row> <el-row>
<el-col :span="24" v-if="form.parentId !== 0"> <el-col v-if="form.parentId !== 0" :span="24">
<el-form-item label="上级部门" prop="parentId"> <el-form-item label="上级部门" prop="parentId">
<el-tree-select <el-tree-select
v-model="form.parentId" v-model="form.parentId"
@ -115,8 +115,7 @@
<el-col :span="12"> <el-col :span="12">
<el-form-item label="部门状态"> <el-form-item label="部门状态">
<el-radio-group v-model="form.status"> <el-radio-group v-model="form.status">
<el-radio v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.value">{{ dict.label <el-radio v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.value">{{ dict.label }}</el-radio>
}}</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
</el-col> </el-col>
@ -133,26 +132,25 @@
</template> </template>
<script setup name="Dept" lang="ts"> <script setup name="Dept" lang="ts">
import { listDept, getDept, delDept, addDept, updateDept, listDeptExcludeChild } from "@/api/system/dept" import { listDept, getDept, delDept, addDept, updateDept, listDeptExcludeChild } from '@/api/system/dept';
import { DeptForm, DeptQuery, DeptVO } from "@/api/system/dept/types"; import { DeptForm, DeptQuery, DeptVO } from '@/api/system/dept/types';
import {UserVO} from "@/api/system/user/types"; import { UserVO } from '@/api/system/user/types';
import {listUserByDeptId} from "@/api/system/user"; import { listUserByDeptId } from '@/api/system/user';
interface DeptOptionsType { interface DeptOptionsType {
deptId: number | string; deptId: number | string;
deptName: string; deptName: string;
children: DeptOptionsType[]; children: DeptOptionsType[];
} }
const { proxy } = getCurrentInstance() as ComponentInternalInstance const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { sys_normal_disable } = toRefs<any>(proxy?.useDict("sys_normal_disable")); const { sys_normal_disable } = toRefs<any>(proxy?.useDict('sys_normal_disable'));
const deptList = ref<DeptVO[]>([]) const deptList = ref<DeptVO[]>([]);
const loading = ref(true) const loading = ref(true);
const showSearch = ref(true) const showSearch = ref(true);
const deptOptions = ref<DeptOptionsType[]>([]) const deptOptions = ref<DeptOptionsType[]>([]);
const isExpandAll = ref(true) const isExpandAll = ref(true);
const deptUserList = ref<UserVO[]>([]); const deptUserList = ref<UserVO[]>([]);
const dialog = reactive<DialogOption>({ const dialog = reactive<DialogOption>({
@ -172,8 +170,8 @@ const initFormData: DeptForm = {
leader: undefined, leader: undefined,
phone: undefined, phone: undefined,
email: undefined, email: undefined,
status: "0" status: '0'
} };
const data = reactive<PageData<DeptForm, DeptQuery>>({ const data = reactive<PageData<DeptForm, DeptQuery>>({
form: { ...initFormData }, form: { ...initFormData },
queryParams: { queryParams: {
@ -183,30 +181,30 @@ const data = reactive<PageData<DeptForm, DeptQuery>>({
status: undefined status: undefined
}, },
rules: { rules: {
parentId: [{ required: true, message: "上级部门不能为空", trigger: "blur" }], parentId: [{ required: true, message: '上级部门不能为空', trigger: 'blur' }],
deptName: [{ required: true, message: "部门名称不能为空", trigger: "blur" }], deptName: [{ required: true, message: '部门名称不能为空', trigger: 'blur' }],
orderNum: [{ required: true, message: "显示排序不能为空", trigger: "blur" }], orderNum: [{ required: true, message: '显示排序不能为空', trigger: 'blur' }],
email: [{ type: "email", message: "请输入正确的邮箱地址", trigger: ["blur", "change"] }], email: [{ type: 'email', message: '请输入正确的邮箱地址', trigger: ['blur', 'change'] }],
phone: [{ pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: "请输入正确的手机号码", trigger: "blur" }] phone: [{ pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: '请输入正确的手机号码', trigger: 'blur' }]
}, }
}) });
const { queryParams, form, rules } = toRefs<PageData<DeptForm, DeptQuery>>(data) const { queryParams, form, rules } = toRefs<PageData<DeptForm, DeptQuery>>(data);
/** 查询菜单列表 */ /** 查询菜单列表 */
const getList = async () => { const getList = async () => {
loading.value = true; loading.value = true;
const res = await listDept(queryParams.value); const res = await listDept(queryParams.value);
const data = proxy?.handleTree<DeptVO>(res.data, "deptId") const data = proxy?.handleTree<DeptVO>(res.data, 'deptId');
if (data) { if (data) {
deptList.value = data deptList.value = data;
} }
loading.value = false loading.value = false;
} };
/** 查询当前部门的所有用户 */ /** 查询当前部门的所有用户 */
async function getDeptAllUser(deptId: any) { async function getDeptAllUser(deptId: any) {
if (deptId !== null && deptId !== "" && deptId !== undefined) { if (deptId !== null && deptId !== '' && deptId !== undefined) {
const res = await listUserByDeptId(deptId); const res = await listUserByDeptId(deptId);
deptUserList.value = res.data; deptUserList.value = res.data;
} }
@ -214,52 +212,52 @@ async function getDeptAllUser(deptId: any) {
/** 取消按钮 */ /** 取消按钮 */
const cancel = () => { const cancel = () => {
reset() reset();
dialog.visible = false dialog.visible = false;
} };
/** 表单重置 */ /** 表单重置 */
const reset = () => { const reset = () => {
form.value = { ...initFormData }; form.value = { ...initFormData };
deptFormRef.value?.resetFields(); deptFormRef.value?.resetFields();
} };
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
getList(); getList();
} };
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
queryFormRef.value?.resetFields(); queryFormRef.value?.resetFields();
handleQuery() handleQuery();
} };
/** 展开/折叠操作 */ /** 展开/折叠操作 */
const handleToggleExpandAll = () => { const handleToggleExpandAll = () => {
isExpandAll.value = !isExpandAll.value; isExpandAll.value = !isExpandAll.value;
toggleExpandAll(deptList.value, isExpandAll.value) toggleExpandAll(deptList.value, isExpandAll.value);
} };
/** 展开/折叠所有 */ /** 展开/折叠所有 */
const toggleExpandAll = (data: DeptVO[], status: boolean) => { const toggleExpandAll = (data: DeptVO[], status: boolean) => {
data.forEach((item) => { data.forEach((item) => {
deptTableRef.value?.toggleRowExpansion(item, status) deptTableRef.value?.toggleRowExpansion(item, status);
if (item.children && item.children.length > 0) toggleExpandAll(item.children, status) if (item.children && item.children.length > 0) toggleExpandAll(item.children, status);
}) });
} };
/** 新增按钮操作 */ /** 新增按钮操作 */
const handleAdd = async (row?: DeptVO) => { const handleAdd = async (row?: DeptVO) => {
reset(); reset();
const res = await listDept(); const res = await listDept();
const data = proxy?.handleTree<DeptOptionsType>(res.data, "deptId"); const data = proxy?.handleTree<DeptOptionsType>(res.data, 'deptId');
if (data) { if (data) {
deptOptions.value = data deptOptions.value = data;
if (row && row.deptId) { if (row && row.deptId) {
form.value.parentId = row?.deptId; form.value.parentId = row?.deptId;
} }
dialog.visible = true; dialog.visible = true;
dialog.title = "添加部门"; dialog.title = '添加部门';
} }
} };
/** 修改按钮操作 */ /** 修改按钮操作 */
const handleUpdate = async (row: DeptVO) => { const handleUpdate = async (row: DeptVO) => {
@ -267,9 +265,9 @@ const handleUpdate = async (row: DeptVO) => {
//查询当前部门所有用户 //查询当前部门所有用户
getDeptAllUser(row.deptId); getDeptAllUser(row.deptId);
const res = await getDept(row.deptId); const res = await getDept(row.deptId);
form.value = res.data form.value = res.data;
const response = await listDeptExcludeChild(row.deptId); const response = await listDeptExcludeChild(row.deptId);
const data = proxy?.handleTree<DeptOptionsType>(response.data, "deptId") const data = proxy?.handleTree<DeptOptionsType>(response.data, 'deptId');
if (data) { if (data) {
deptOptions.value = data; deptOptions.value = data;
if (data.length === 0) { if (data.length === 0) {
@ -282,26 +280,26 @@ const handleUpdate = async (row: DeptVO) => {
} }
} }
dialog.visible = true; dialog.visible = true;
dialog.title = "修改部门"; dialog.title = '修改部门';
} };
/** 提交按钮 */ /** 提交按钮 */
const submitForm = () => { const submitForm = () => {
deptFormRef.value?.validate(async (valid: boolean) => { deptFormRef.value?.validate(async (valid: boolean) => {
if (valid) { if (valid) {
form.value.deptId ? await updateDept(form.value) : await addDept(form.value); form.value.deptId ? await updateDept(form.value) : await addDept(form.value);
proxy?.$modal.msgSuccess("操作成功"); proxy?.$modal.msgSuccess('操作成功');
dialog.visible = false; dialog.visible = false;
await getList(); await getList();
} }
}) });
} };
/** 删除按钮操作 */ /** 删除按钮操作 */
const handleDelete = async (row: DeptVO) => { const handleDelete = async (row: DeptVO) => {
await proxy?.$modal.confirm('是否确认删除名称为"' + row.deptName + '"的数据项?'); await proxy?.$modal.confirm('是否确认删除名称为"' + row.deptName + '"的数据项?');
await delDept(row.deptId); await delDept(row.deptId);
await getList(); await getList();
proxy?.$modal.msgSuccess("删除成功"); proxy?.$modal.msgSuccess('删除成功');
} };
onMounted(() => { onMounted(() => {
getList(); getList();

View File

@ -1,9 +1,9 @@
<template> <template>
<div class="p-2"> <div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="mb-[10px]" v-show="showSearch"> <div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover"> <el-card shadow="hover">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px"> <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px">
<el-form-item label="字典名称" prop="dictType"> <el-form-item label="字典名称" prop="dictType">
<el-select v-model="queryParams.dictType" style="width: 200px"> <el-select v-model="queryParams.dictType" style="width: 200px">
<el-option v-for="item in typeOptions" :key="item.dictId" :label="item.dictName" :value="item.dictType" /> <el-option v-for="item in typeOptions" :key="item.dictId" :label="item.dictName" :value="item.dictType" />
@ -24,29 +24,29 @@
<template #header> <template #header>
<el-row :gutter="10" class="mb8"> <el-row :gutter="10" class="mb8">
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:dict:add']">新增</el-button> <el-button v-hasPermi="['system:dict:add']" type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['system:dict:edit']">修改</el-button> <el-button v-hasPermi="['system:dict:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()">修改</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:dict:remove']"> <el-button v-hasPermi="['system:dict:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">
删除 删除
</el-button> </el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:dict:export']">导出</el-button> <el-button v-hasPermi="['system:dict:export']" type="warning" plain icon="Download" @click="handleExport">导出</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="warning" plain icon="Close" @click="handleClose">关闭</el-button> <el-button type="warning" plain icon="Close" @click="handleClose">关闭</el-button>
</el-col> </el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> <right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
</el-row> </el-row>
</template> </template>
<el-table v-loading="loading" :data="dataList" @selection-change="handleSelectionChange"> <el-table v-loading="loading" :data="dataList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" /> <el-table-column type="selection" width="55" align="center" />
<el-table-column label="字典编码" align="center" prop="dictCode" v-if="false" /> <el-table-column v-if="false" label="字典编码" align="center" prop="dictCode" />
<el-table-column label="字典标签" align="center" prop="dictLabel"> <el-table-column label="字典标签" align="center" prop="dictLabel">
<template #default="scope"> <template #default="scope">
<span v-if="(scope.row.listClass === '' || scope.row.listClass === 'default') && (scope.row.cssClass === '' || scope.row.cssClass == null)">{{ scope.row.dictLabel }}</span> <span v-if="(scope.row.listClass === '' || scope.row.listClass === 'default') && (scope.row.cssClass === '' || scope.row.cssClass == null)">{{ scope.row.dictLabel }}</span>
@ -64,19 +64,19 @@
<el-table-column label="操作" align="center" width="160" class-name="small-padding fixed-width"> <el-table-column label="操作" align="center" width="160" class-name="small-padding fixed-width">
<template #default="scope"> <template #default="scope">
<el-tooltip content="修改" placement="top"> <el-tooltip content="修改" placement="top">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:dict:edit']"></el-button> <el-button v-hasPermi="['system:dict:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
</el-tooltip> </el-tooltip>
<el-tooltip content="删除" placement="top"> <el-tooltip content="删除" placement="top">
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:dict:remove']"></el-button> <el-button v-hasPermi="['system:dict:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
</el-tooltip> </el-tooltip>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
</el-card> </el-card>
<!-- 添加或修改参数配置对话框 --> <!-- 添加或修改参数配置对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body> <el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
<el-form ref="dataFormRef" :model="form" :rules="rules" label-width="80px"> <el-form ref="dataFormRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="字典类型"> <el-form-item label="字典类型">
<el-input v-model="form.dictType" :disabled="true" /> <el-input v-model="form.dictType" :disabled="true" />
@ -118,13 +118,13 @@
</template> </template>
<script setup name="Data" lang="ts"> <script setup name="Data" lang="ts">
import useDictStore from '@/store/modules/dict' import useDictStore from '@/store/modules/dict';
import { optionselect as getDictOptionselect, getType } from "@/api/system/dict/type"; import { optionselect as getDictOptionselect, getType } from '@/api/system/dict/type';
import { listData, getData, delData, addData, updateData } from "@/api/system/dict/data"; import { listData, getData, delData, addData, updateData } from '@/api/system/dict/data';
import { DictTypeVO } from '@/api/system/dict/type/types'; import { DictTypeVO } from '@/api/system/dict/type/types';
import { DictDataForm, DictDataQuery, DictDataVO } from "@/api/system/dict/data/types"; import { DictDataForm, DictDataQuery, DictDataVO } from '@/api/system/dict/data/types';
const { proxy } = getCurrentInstance() as ComponentInternalInstance const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const route = useRoute(); const route = useRoute();
const dataList = ref<DictDataVO[]>([]); const dataList = ref<DictDataVO[]>([]);
@ -134,26 +134,25 @@ const ids = ref<Array<string | number>>([]);
const single = ref(true); const single = ref(true);
const multiple = ref(true); const multiple = ref(true);
const total = ref(0); const total = ref(0);
const defaultDictType = ref(""); const defaultDictType = ref('');
const typeOptions = ref<DictTypeVO[]>([]); const typeOptions = ref<DictTypeVO[]>([]);
const dataFormRef = ref<ElFormInstance>(); const dataFormRef = ref<ElFormInstance>();
const queryFormRef = ref<ElFormInstance>(); const queryFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({ const dialog = reactive<DialogOption>({
visible: false, visible: false,
title: '' title: ''
}); });
// 数据标签回显样式 // 数据标签回显样式
const listClassOptions = ref<Array<{ value: string, label: string }>>([ const listClassOptions = ref<Array<{ value: string; label: string }>>([
{ value: "default", label: "默认" }, { value: 'default', label: '默认' },
{ value: "primary", label: "主要" }, { value: 'primary', label: '主要' },
{ value: "success", label: "成功" }, { value: 'success', label: '成功' },
{ value: "info", label: "信息" }, { value: 'info', label: '信息' },
{ value: "warning", label: "警告" }, { value: 'warning', label: '警告' },
{ value: "danger", label: "危险" } { value: 'danger', label: '危险' }
]); ]);
const initFormData: DictDataForm = { const initFormData: DictDataForm = {
@ -161,10 +160,10 @@ const initFormData: DictDataForm = {
dictLabel: '', dictLabel: '',
dictValue: '', dictValue: '',
cssClass: '', cssClass: '',
listClass: "default", listClass: 'default',
dictSort: 0, dictSort: 0,
remark: '' remark: ''
} };
const data = reactive<PageData<DictDataForm, DictDataQuery>>({ const data = reactive<PageData<DictDataForm, DictDataQuery>>({
form: { ...initFormData }, form: { ...initFormData },
queryParams: { queryParams: {
@ -175,9 +174,9 @@ const data = reactive<PageData<DictDataForm, DictDataQuery>>({
dictLabel: '' dictLabel: ''
}, },
rules: { rules: {
dictLabel: [{ required: true, message: "数据标签不能为空", trigger: "blur" }], dictLabel: [{ required: true, message: '数据标签不能为空', trigger: 'blur' }],
dictValue: [{ required: true, message: "数据键值不能为空", trigger: "blur" }], dictValue: [{ required: true, message: '数据键值不能为空', trigger: 'blur' }],
dictSort: [{ required: true, message: "数据顺序不能为空", trigger: "blur" }] dictSort: [{ required: true, message: '数据顺序不能为空', trigger: 'blur' }]
} }
}); });
@ -189,13 +188,13 @@ const getTypes = async (dictId: string | number) => {
queryParams.value.dictType = data.dictType; queryParams.value.dictType = data.dictType;
defaultDictType.value = data.dictType; defaultDictType.value = data.dictType;
getList(); getList();
} };
/** 查询字典类型列表 */ /** 查询字典类型列表 */
const getTypeList = async () => { const getTypeList = async () => {
const res = await getDictOptionselect() const res = await getDictOptionselect();
typeOptions.value = res.data; typeOptions.value = res.data;
} };
/** 查询字典数据列表 */ /** 查询字典数据列表 */
const getList = async () => { const getList = async () => {
loading.value = true; loading.value = true;
@ -203,46 +202,46 @@ const getList = async () => {
dataList.value = res.rows; dataList.value = res.rows;
total.value = res.total; total.value = res.total;
loading.value = false; loading.value = false;
} };
/** 取消按钮 */ /** 取消按钮 */
const cancel = () => { const cancel = () => {
dialog.visible = false; dialog.visible = false;
reset(); reset();
} };
/** 表单重置 */ /** 表单重置 */
const reset = () => { const reset = () => {
form.value = { ...initFormData }; form.value = { ...initFormData };
dataFormRef.value?.resetFields(); dataFormRef.value?.resetFields();
} };
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
queryParams.value.pageNum = 1; queryParams.value.pageNum = 1;
getList(); getList();
} };
/** 返回按钮操作 */ /** 返回按钮操作 */
const handleClose = () => { const handleClose = () => {
const obj = { path: "/system/dict" }; const obj = { path: '/system/dict' };
proxy?.$tab.closeOpenPage(obj); proxy?.$tab.closeOpenPage(obj);
} };
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
queryFormRef.value?.resetFields(); queryFormRef.value?.resetFields();
queryParams.value.dictType = defaultDictType.value; queryParams.value.dictType = defaultDictType.value;
handleQuery(); handleQuery();
} };
/** 新增按钮操作 */ /** 新增按钮操作 */
const handleAdd = () => { const handleAdd = () => {
reset(); reset();
form.value.dictType = queryParams.value.dictType; form.value.dictType = queryParams.value.dictType;
dialog.visible = true; dialog.visible = true;
dialog.title = "添加字典数据"; dialog.title = '添加字典数据';
} };
/** 多选框选中数据 */ /** 多选框选中数据 */
const handleSelectionChange = (selection: DictDataVO[]) => { const handleSelectionChange = (selection: DictDataVO[]) => {
ids.value = selection.map(item => item.dictCode); ids.value = selection.map((item) => item.dictCode);
single.value = selection.length != 1; single.value = selection.length != 1;
multiple.value = !selection.length; multiple.value = !selection.length;
} };
/** 修改按钮操作 */ /** 修改按钮操作 */
const handleUpdate = async (row?: DictDataVO) => { const handleUpdate = async (row?: DictDataVO) => {
reset(); reset();
@ -250,40 +249,42 @@ const handleUpdate = async (row?: DictDataVO) => {
const res = await getData(dictCode); const res = await getData(dictCode);
Object.assign(form.value, res.data); Object.assign(form.value, res.data);
dialog.visible = true; dialog.visible = true;
dialog.title = "修改字典数据"; dialog.title = '修改字典数据';
} };
/** 提交按钮 */ /** 提交按钮 */
const submitForm = () => { const submitForm = () => {
dataFormRef.value?.validate(async (valid: boolean) => { dataFormRef.value?.validate(async (valid: boolean) => {
if (valid) { if (valid) {
form.value.dictCode ? await updateData(form.value) : await addData(form.value); form.value.dictCode ? await updateData(form.value) : await addData(form.value);
useDictStore().removeDict(queryParams.value.dictType); useDictStore().removeDict(queryParams.value.dictType);
proxy?.$modal.msgSuccess("操作成功"); proxy?.$modal.msgSuccess('操作成功');
dialog.visible = false; dialog.visible = false;
await getList(); await getList();
} }
}); });
} };
/** 删除按钮操作 */ /** 删除按钮操作 */
const handleDelete = async (row?: DictDataVO) => { const handleDelete = async (row?: DictDataVO) => {
const dictCodes = row?.dictCode || ids.value; const dictCodes = row?.dictCode || ids.value;
await proxy?.$modal.confirm('是否确认删除字典编码为"' + dictCodes + '"的数据项?'); await proxy?.$modal.confirm('是否确认删除字典编码为"' + dictCodes + '"的数据项?');
await delData(dictCodes); await delData(dictCodes);
await getList(); await getList();
proxy?.$modal.msgSuccess("删除成功"); proxy?.$modal.msgSuccess('删除成功');
useDictStore().removeDict(queryParams.value.dictType); useDictStore().removeDict(queryParams.value.dictType);
};
}
/** 导出按钮操作 */ /** 导出按钮操作 */
const handleExport = () => { const handleExport = () => {
proxy?.download("system/dict/data/export", { proxy?.download(
...queryParams.value 'system/dict/data/export',
}, `dict_data_${new Date().getTime()}.xlsx`); {
} ...queryParams.value
},
`dict_data_${new Date().getTime()}.xlsx`
);
};
onMounted(() => { onMounted(() => {
getTypes(route.params && route.params.dictId as string); getTypes(route.params && (route.params.dictId as string));
getTypeList(); getTypeList();
}) });
</script> </script>

View File

@ -1,9 +1,9 @@
<template> <template>
<div class="p-2"> <div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="mb-[10px]" v-show="showSearch"> <div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover"> <el-card shadow="hover">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px"> <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px">
<el-form-item label="字典名称" prop="dictName"> <el-form-item label="字典名称" prop="dictName">
<el-input v-model="queryParams.dictName" placeholder="请输入字典名称" clearable style="width: 240px" @keyup.enter="handleQuery" /> <el-input v-model="queryParams.dictName" placeholder="请输入字典名称" clearable style="width: 240px" @keyup.enter="handleQuery" />
</el-form-item> </el-form-item>
@ -33,29 +33,29 @@
<template #header> <template #header>
<el-row :gutter="10" class="mb8"> <el-row :gutter="10" class="mb8">
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:dict:add']">新增</el-button> <el-button v-hasPermi="['system:dict:add']" type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['system:dict:edit']">修改</el-button> <el-button v-hasPermi="['system:dict:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()">修改</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:dict:remove']"> <el-button v-hasPermi="['system:dict:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">
删除 删除
</el-button> </el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:dict:export']">导出</el-button> <el-button v-hasPermi="['system:dict:export']" type="warning" plain icon="Download" @click="handleExport">导出</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="danger" plain icon="Refresh" @click="handleRefreshCache" v-hasPermi="['system:dict:remove']">刷新缓存</el-button> <el-button v-hasPermi="['system:dict:remove']" type="danger" plain icon="Refresh" @click="handleRefreshCache">刷新缓存</el-button>
</el-col> </el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> <right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
</el-row> </el-row>
</template> </template>
<el-table v-loading="loading" :data="typeList" @selection-change="handleSelectionChange"> <el-table v-loading="loading" :data="typeList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" /> <el-table-column type="selection" width="55" align="center" />
<el-table-column label="字典编号" align="center" prop="dictId" v-if="false" /> <el-table-column v-if="false" label="字典编号" align="center" prop="dictId" />
<el-table-column label="字典名称" align="center" prop="dictName" :show-overflow-tooltip="true" /> <el-table-column label="字典名称" align="center" prop="dictName" :show-overflow-tooltip="true" />
<el-table-column label="字典类型" align="center" :show-overflow-tooltip="true"> <el-table-column label="字典类型" align="center" :show-overflow-tooltip="true">
<template #default="scope"> <template #default="scope">
@ -73,19 +73,19 @@
<el-table-column label="操作" align="center" width="160" class-name="small-padding fixed-width"> <el-table-column label="操作" align="center" width="160" class-name="small-padding fixed-width">
<template #default="scope"> <template #default="scope">
<el-tooltip content="修改" placement="top"> <el-tooltip content="修改" placement="top">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:dict:edit']"></el-button> <el-button v-hasPermi="['system:dict:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
</el-tooltip> </el-tooltip>
<el-tooltip content="删除" placement="top"> <el-tooltip content="删除" placement="top">
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:dict:remove']"></el-button> <el-button v-hasPermi="['system:dict:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
</el-tooltip> </el-tooltip>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
</el-card> </el-card>
<!-- 添加或修改参数配置对话框 --> <!-- 添加或修改参数配置对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body> <el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
<el-form ref="dictFormRef" :model="form" :rules="rules" label-width="80px"> <el-form ref="dictFormRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="字典名称" prop="dictName"> <el-form-item label="字典名称" prop="dictName">
<el-input v-model="form.dictName" placeholder="请输入字典名称" /> <el-input v-model="form.dictName" placeholder="请输入字典名称" />
@ -108,9 +108,9 @@
</template> </template>
<script setup name="Dict" lang="ts"> <script setup name="Dict" lang="ts">
import useDictStore from '@/store/modules/dict' import useDictStore from '@/store/modules/dict';
import { listType, getType, delType, addType, updateType, refreshCache } from "@/api/system/dict/type"; import { listType, getType, delType, addType, updateType, refreshCache } from '@/api/system/dict/type';
import { DictTypeForm, DictTypeQuery, DictTypeVO } from "@/api/system/dict/type/types"; import { DictTypeForm, DictTypeQuery, DictTypeVO } from '@/api/system/dict/type/types';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@ -126,7 +126,6 @@ const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
const dictFormRef = ref<ElFormInstance>(); const dictFormRef = ref<ElFormInstance>();
const queryFormRef = ref<ElFormInstance>(); const queryFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({ const dialog = reactive<DialogOption>({
visible: false, visible: false,
title: '' title: ''
@ -137,7 +136,7 @@ const initFormData: DictTypeForm = {
dictName: '', dictName: '',
dictType: '', dictType: '',
remark: '' remark: ''
} };
const data = reactive<PageData<DictTypeForm, DictTypeQuery>>({ const data = reactive<PageData<DictTypeForm, DictTypeQuery>>({
form: { ...initFormData }, form: { ...initFormData },
queryParams: { queryParams: {
@ -147,9 +146,9 @@ const data = reactive<PageData<DictTypeForm, DictTypeQuery>>({
dictType: '' dictType: ''
}, },
rules: { rules: {
dictName: [{ required: true, message: "字典名称不能为空", trigger: "blur" }], dictName: [{ required: true, message: '字典名称不能为空', trigger: 'blur' }],
dictType: [{ required: true, message: "字典类型不能为空", trigger: "blur" }] dictType: [{ required: true, message: '字典类型不能为空', trigger: 'blur' }]
}, }
}); });
const { queryParams, form, rules } = toRefs(data); const { queryParams, form, rules } = toRefs(data);
@ -157,45 +156,45 @@ const { queryParams, form, rules } = toRefs(data);
/** 查询字典类型列表 */ /** 查询字典类型列表 */
const getList = () => { const getList = () => {
loading.value = true; loading.value = true;
listType(proxy?.addDateRange(queryParams.value, dateRange.value)).then(res => { listType(proxy?.addDateRange(queryParams.value, dateRange.value)).then((res) => {
typeList.value = res.rows; typeList.value = res.rows;
total.value = res.total; total.value = res.total;
loading.value = false; loading.value = false;
}); });
} };
/** 取消按钮 */ /** 取消按钮 */
const cancel = () => { const cancel = () => {
reset(); reset();
dialog.visible = false; dialog.visible = false;
} };
/** 表单重置 */ /** 表单重置 */
const reset = () => { const reset = () => {
form.value = { ...initFormData }; form.value = { ...initFormData };
dictFormRef.value?.resetFields(); dictFormRef.value?.resetFields();
} };
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
queryParams.value.pageNum = 1; queryParams.value.pageNum = 1;
getList(); getList();
} };
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
dateRange.value = ['', '']; dateRange.value = ['', ''];
queryFormRef.value?.resetFields(); queryFormRef.value?.resetFields();
handleQuery(); handleQuery();
} };
/** 新增按钮操作 */ /** 新增按钮操作 */
const handleAdd = () => { const handleAdd = () => {
reset(); reset();
dialog.visible = true; dialog.visible = true;
dialog.title = "添加字典类型"; dialog.title = '添加字典类型';
} };
/** 多选框选中数据 */ /** 多选框选中数据 */
const handleSelectionChange = (selection: DictTypeVO[]) => { const handleSelectionChange = (selection: DictTypeVO[]) => {
ids.value = selection.map(item => item.dictId); ids.value = selection.map((item) => item.dictId);
single.value = selection.length != 1; single.value = selection.length != 1;
multiple.value = !selection.length; multiple.value = !selection.length;
} };
/** 修改按钮操作 */ /** 修改按钮操作 */
const handleUpdate = async (row?: DictTypeVO) => { const handleUpdate = async (row?: DictTypeVO) => {
reset(); reset();
@ -203,41 +202,45 @@ const handleUpdate = async (row?: DictTypeVO) => {
const res = await getType(dictId); const res = await getType(dictId);
Object.assign(form.value, res.data); Object.assign(form.value, res.data);
dialog.visible = true; dialog.visible = true;
dialog.title = "修改字典类型"; dialog.title = '修改字典类型';
} };
/** 提交按钮 */ /** 提交按钮 */
const submitForm = () => { const submitForm = () => {
dictFormRef.value?.validate(async (valid: boolean) => { dictFormRef.value?.validate(async (valid: boolean) => {
if (valid) { if (valid) {
form.value.dictId ? await updateType(form.value) : await addType(form.value); form.value.dictId ? await updateType(form.value) : await addType(form.value);
proxy?.$modal.msgSuccess("操作成功"); proxy?.$modal.msgSuccess('操作成功');
dialog.visible = false; dialog.visible = false;
getList(); getList();
} }
}); });
} };
/** 删除按钮操作 */ /** 删除按钮操作 */
const handleDelete = async (row?: DictTypeVO) => { const handleDelete = async (row?: DictTypeVO) => {
const dictIds = row?.dictId || ids.value; const dictIds = row?.dictId || ids.value;
await proxy?.$modal.confirm('是否确认删除字典编号为"' + dictIds + '"的数据项?'); await proxy?.$modal.confirm('是否确认删除字典编号为"' + dictIds + '"的数据项?');
await delType(dictIds); await delType(dictIds);
getList(); getList();
proxy?.$modal.msgSuccess("删除成功"); proxy?.$modal.msgSuccess('删除成功');
} };
/** 导出按钮操作 */ /** 导出按钮操作 */
const handleExport = () => { const handleExport = () => {
proxy?.download("system/dict/type/export", { proxy?.download(
...queryParams.value 'system/dict/type/export',
}, `dict_${new Date().getTime()}.xlsx`); {
} ...queryParams.value
},
`dict_${new Date().getTime()}.xlsx`
);
};
/** 刷新缓存按钮操作 */ /** 刷新缓存按钮操作 */
const handleRefreshCache = async () => { const handleRefreshCache = async () => {
await refreshCache(); await refreshCache();
proxy?.$modal.msgSuccess("刷新成功"); proxy?.$modal.msgSuccess('刷新成功');
useDictStore().cleanDict(); useDictStore().cleanDict();
} };
onMounted(() => { onMounted(() => {
getList(); getList();
}) });
</script> </script>

View File

@ -1,7 +1,7 @@
<template> <template>
<div class="p-2"> <div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="mb-[10px]" v-show="showSearch"> <div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover"> <el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px"> <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px">
<el-form-item label="菜单名称" prop="menuName"> <el-form-item label="菜单名称" prop="menuName">
@ -25,21 +25,21 @@
<template #header> <template #header>
<el-row :gutter="10"> <el-row :gutter="10">
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd()" v-hasPermi="['system:menu:add']">新增 </el-button> <el-button v-hasPermi="['system:menu:add']" type="primary" plain icon="Plus" @click="handleAdd()">新增 </el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="info" plain icon="Sort" @click="handleToggleExpandAll">展开/折叠</el-button> <el-button type="info" plain icon="Sort" @click="handleToggleExpandAll">展开/折叠</el-button>
</el-col> </el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> <right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
</el-row> </el-row>
</template> </template>
<el-table <el-table
ref="menuTableRef"
v-loading="loading" v-loading="loading"
:data="menuList" :data="menuList"
row-key="menuId" row-key="menuId"
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }" :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
ref="menuTableRef"
:default-expand-all="isExpandAll" :default-expand-all="isExpandAll"
> >
<el-table-column prop="menuName" label="菜单名称" :show-overflow-tooltip="true" width="160"></el-table-column> <el-table-column prop="menuName" label="菜单名称" :show-overflow-tooltip="true" width="160"></el-table-column>
@ -64,20 +64,20 @@
<el-table-column fixed="right" label="操作" width="180"> <el-table-column fixed="right" label="操作" width="180">
<template #default="scope"> <template #default="scope">
<el-tooltip content="修改" placement="top"> <el-tooltip content="修改" placement="top">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:menu:edit']" /> <el-button v-hasPermi="['system:menu:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)" />
</el-tooltip> </el-tooltip>
<el-tooltip content="新增" placement="top"> <el-tooltip content="新增" placement="top">
<el-button link type="primary" icon="Plus" @click="handleAdd(scope.row)" v-hasPermi="['system:menu:add']" /> <el-button v-hasPermi="['system:menu:add']" link type="primary" icon="Plus" @click="handleAdd(scope.row)" />
</el-tooltip> </el-tooltip>
<el-tooltip content="删除" placement="top"> <el-tooltip content="删除" placement="top">
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:menu:remove']" /> <el-button v-hasPermi="['system:menu:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)" />
</el-tooltip> </el-tooltip>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
</el-card> </el-card>
<el-dialog :title="dialog.title" v-model="dialog.visible" destroy-on-close append-to-bod width="750px"> <el-dialog v-model="dialog.visible" :title="dialog.title" destroy-on-close append-to-bod width="750px">
<el-form ref="menuFormRef" :model="form" :rules="rules" label-width="100px"> <el-form ref="menuFormRef" :model="form" :rules="rules" label-width="100px">
<el-row> <el-row>
<el-col :span="24"> <el-col :span="24">
@ -101,7 +101,7 @@
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="24" v-if="form.menuType !== 'F'"> <el-col v-if="form.menuType !== 'F'" :span="24">
<el-form-item label="菜单图标" prop="icon"> <el-form-item label="菜单图标" prop="icon">
<!-- 图标选择器 --> <!-- 图标选择器 -->
<icon-select v-model="form.icon" /> <icon-select v-model="form.icon" />
@ -117,7 +117,7 @@
<el-input-number v-model="form.orderNum" controls-position="right" :min="0" /> <el-input-number v-model="form.orderNum" controls-position="right" :min="0" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12" v-if="form.menuType !== 'F'"> <el-col v-if="form.menuType !== 'F'" :span="12">
<el-form-item> <el-form-item>
<template #label> <template #label>
<span> <span>
@ -134,7 +134,7 @@
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12" v-if="form.menuType !== 'F'"> <el-col v-if="form.menuType !== 'F'" :span="12">
<el-form-item prop="path"> <el-form-item prop="path">
<template #label> <template #label>
<span> <span>
@ -149,7 +149,7 @@
<el-input v-model="form.path" placeholder="请输入路由地址" /> <el-input v-model="form.path" placeholder="请输入路由地址" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12" v-if="form.menuType === 'C'"> <el-col v-if="form.menuType === 'C'" :span="12">
<el-form-item prop="component"> <el-form-item prop="component">
<template #label> <template #label>
<span> <span>
@ -164,7 +164,7 @@
<el-input v-model="form.component" placeholder="请输入组件路径" /> <el-input v-model="form.component" placeholder="请输入组件路径" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12" v-if="form.menuType !== 'M'"> <el-col v-if="form.menuType !== 'M'" :span="12">
<el-form-item> <el-form-item>
<el-input v-model="form.perms" placeholder="请输入权限标识" maxlength="100" /> <el-input v-model="form.perms" placeholder="请输入权限标识" maxlength="100" />
<template #label> <template #label>
@ -179,7 +179,7 @@
</template> </template>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12" v-if="form.menuType === 'C'"> <el-col v-if="form.menuType === 'C'" :span="12">
<el-form-item> <el-form-item>
<el-input v-model="form.queryParam" placeholder="请输入路由参数" maxlength="255" /> <el-input v-model="form.queryParam" placeholder="请输入路由参数" maxlength="255" />
<template #label> <template #label>
@ -194,7 +194,7 @@
</template> </template>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12" v-if="form.menuType === 'C'"> <el-col v-if="form.menuType === 'C'" :span="12">
<el-form-item> <el-form-item>
<template #label> <template #label>
<span> <span>
@ -212,7 +212,7 @@
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12" v-if="form.menuType !== 'F'"> <el-col v-if="form.menuType !== 'F'" :span="12">
<el-form-item> <el-form-item>
<template #label> <template #label>
<span> <span>
@ -271,14 +271,14 @@ interface MenuOptionsType {
children: MenuOptionsType[] | undefined; children: MenuOptionsType[] | undefined;
} }
const { proxy } = getCurrentInstance() as ComponentInternalInstance const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { sys_show_hide, sys_normal_disable } = toRefs<any>(proxy?.useDict("sys_show_hide", "sys_normal_disable")); const { sys_show_hide, sys_normal_disable } = toRefs<any>(proxy?.useDict('sys_show_hide', 'sys_normal_disable'));
const menuList = ref<MenuVO[]>([]) const menuList = ref<MenuVO[]>([]);
const loading = ref(true) const loading = ref(true);
const showSearch = ref(true) const showSearch = ref(true);
const menuOptions = ref<MenuOptionsType[]>([]) const menuOptions = ref<MenuOptionsType[]>([]);
const isExpandAll = ref(false) const isExpandAll = ref(false);
const dialog = reactive<DialogOption>({ const dialog = reactive<DialogOption>({
visible: false, visible: false,
@ -295,11 +295,11 @@ const initFormData = {
icon: '', icon: '',
menuType: MenuTypeEnum.M, menuType: MenuTypeEnum.M,
orderNum: 1, orderNum: 1,
isFrame: "1", isFrame: '1',
isCache: "0", isCache: '0',
visible: "0", visible: '0',
status: "0" status: '0'
} };
const data = reactive<PageData<MenuForm, MenuQuery>>({ const data = reactive<PageData<MenuForm, MenuQuery>>({
form: { ...initFormData }, form: { ...initFormData },
queryParams: { queryParams: {
@ -307,73 +307,73 @@ const data = reactive<PageData<MenuForm, MenuQuery>>({
status: undefined status: undefined
}, },
rules: { rules: {
menuName: [{ required: true, message: "菜单名称不能为空", trigger: "blur" }], menuName: [{ required: true, message: '菜单名称不能为空', trigger: 'blur' }],
orderNum: [{ required: true, message: "菜单顺序不能为空", trigger: "blur" }], orderNum: [{ required: true, message: '菜单顺序不能为空', trigger: 'blur' }],
path: [{ required: true, message: "路由地址不能为空", trigger: "blur" }] path: [{ required: true, message: '路由地址不能为空', trigger: 'blur' }]
}, }
}) });
const menuTableRef = ref<ElTableInstance>(); const menuTableRef = ref<ElTableInstance>();
const { queryParams, form, rules } = toRefs<PageData<MenuForm, MenuQuery>>(data) const { queryParams, form, rules } = toRefs<PageData<MenuForm, MenuQuery>>(data);
/** 查询菜单列表 */ /** 查询菜单列表 */
const getList = async () => { const getList = async () => {
loading.value = true loading.value = true;
const res = await listMenu(queryParams.value); const res = await listMenu(queryParams.value);
const data = proxy?.handleTree<MenuVO>(res.data, "menuId") const data = proxy?.handleTree<MenuVO>(res.data, 'menuId');
if (data) { if (data) {
menuList.value = data menuList.value = data;
} }
loading.value = false loading.value = false;
} };
/** 查询菜单下拉树结构 */ /** 查询菜单下拉树结构 */
const getTreeselect = async () => { const getTreeselect = async () => {
menuOptions.value = [] menuOptions.value = [];
const response = await listMenu(); const response = await listMenu();
const menu: MenuOptionsType = { menuId: 0, menuName: "主类目", children: [] } const menu: MenuOptionsType = { menuId: 0, menuName: '主类目', children: [] };
menu.children = proxy?.handleTree<MenuOptionsType>(response.data, "menuId") menu.children = proxy?.handleTree<MenuOptionsType>(response.data, 'menuId');
menuOptions.value.push(menu) menuOptions.value.push(menu);
} };
/** 取消按钮 */ /** 取消按钮 */
const cancel = () => { const cancel = () => {
reset() reset();
dialog.visible = false dialog.visible = false;
} };
/** 表单重置 */ /** 表单重置 */
const reset = () => { const reset = () => {
form.value = { ...initFormData }; form.value = { ...initFormData };
menuFormRef.value?.resetFields(); menuFormRef.value?.resetFields();
} };
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
getList(); getList();
} };
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
queryFormRef.value?.resetFields(); queryFormRef.value?.resetFields();
handleQuery(); handleQuery();
} };
/** 新增按钮操作 */ /** 新增按钮操作 */
const handleAdd = (row?: MenuVO) => { const handleAdd = (row?: MenuVO) => {
reset(); reset();
getTreeselect(); getTreeselect();
row && row.menuId ? form.value.parentId = row.menuId : form.value.parentId = 0; row && row.menuId ? (form.value.parentId = row.menuId) : (form.value.parentId = 0);
dialog.visible = true; dialog.visible = true;
dialog.title = "添加菜单"; dialog.title = '添加菜单';
} };
/** 展开/折叠操作 */ /** 展开/折叠操作 */
const handleToggleExpandAll = () => { const handleToggleExpandAll = () => {
isExpandAll.value = !isExpandAll.value; isExpandAll.value = !isExpandAll.value;
toggleExpandAll(menuList.value, isExpandAll.value) toggleExpandAll(menuList.value, isExpandAll.value);
} };
/** 展开/折叠所有 */ /** 展开/折叠所有 */
const toggleExpandAll = (data: MenuVO[], status: boolean) => { const toggleExpandAll = (data: MenuVO[], status: boolean) => {
data.forEach((item: MenuVO) => { data.forEach((item: MenuVO) => {
menuTableRef.value?.toggleRowExpansion(item, status) menuTableRef.value?.toggleRowExpansion(item, status);
if (item.children && item.children.length > 0) toggleExpandAll(item.children, status) if (item.children && item.children.length > 0) toggleExpandAll(item.children, status);
}) });
} };
/** 修改按钮操作 */ /** 修改按钮操作 */
const handleUpdate = async (row: MenuVO) => { const handleUpdate = async (row: MenuVO) => {
reset(); reset();
@ -383,26 +383,26 @@ const handleUpdate = async (row: MenuVO) => {
form.value = data; form.value = data;
} }
dialog.visible = true; dialog.visible = true;
dialog.title = "修改菜单"; dialog.title = '修改菜单';
} };
/** 提交按钮 */ /** 提交按钮 */
const submitForm = () => { const submitForm = () => {
menuFormRef.value?.validate(async (valid: boolean) => { menuFormRef.value?.validate(async (valid: boolean) => {
if (valid) { if (valid) {
form.value.menuId ? await updateMenu(form.value) : await addMenu(form.value); form.value.menuId ? await updateMenu(form.value) : await addMenu(form.value);
proxy?.$modal.msgSuccess("操作成功"); proxy?.$modal.msgSuccess('操作成功');
dialog.visible = false; dialog.visible = false;
await getList(); await getList();
} }
}) });
} };
/** 删除按钮操作 */ /** 删除按钮操作 */
const handleDelete = async (row: MenuVO) => { const handleDelete = async (row: MenuVO) => {
await proxy?.$modal.confirm('是否确认删除名称为"' + row.menuName + '"的数据项?'); await proxy?.$modal.confirm('是否确认删除名称为"' + row.menuName + '"的数据项?');
await delMenu(row.menuId); await delMenu(row.menuId);
await getList(); await getList();
proxy?.$modal.msgSuccess("删除成功"); proxy?.$modal.msgSuccess('删除成功');
} };
onMounted(() => { onMounted(() => {
getList(); getList();

View File

@ -1,9 +1,9 @@
<template> <template>
<div class="p-2"> <div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="mb-[10px]" v-show="showSearch"> <div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover"> <el-card shadow="hover">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px"> <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px">
<el-form-item label="公告标题" prop="noticeTitle"> <el-form-item label="公告标题" prop="noticeTitle">
<el-input v-model="queryParams.noticeTitle" placeholder="请输入公告标题" clearable style="width: 200px" @keyup.enter="handleQuery" /> <el-input v-model="queryParams.noticeTitle" placeholder="请输入公告标题" clearable style="width: 200px" @keyup.enter="handleQuery" />
</el-form-item> </el-form-item>
@ -28,25 +28,25 @@
<template #header> <template #header>
<el-row :gutter="10" class="mb8"> <el-row :gutter="10" class="mb8">
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:notice:add']">新增</el-button> <el-button v-hasPermi="['system:notice:add']" type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['system:notice:edit']" <el-button v-hasPermi="['system:notice:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()"
>修改</el-button >修改</el-button
> >
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:notice:remove']"> <el-button v-hasPermi="['system:notice:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">
删除 删除
</el-button> </el-button>
</el-col> </el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> <right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
</el-row> </el-row>
</template> </template>
<el-table v-loading="loading" :data="noticeList" @selection-change="handleSelectionChange"> <el-table v-loading="loading" :data="noticeList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" /> <el-table-column type="selection" width="55" align="center" />
<el-table-column label="序号" align="center" prop="noticeId" width="100" v-if="false" /> <el-table-column v-if="false" label="序号" align="center" prop="noticeId" width="100" />
<el-table-column label="公告标题" align="center" prop="noticeTitle" :show-overflow-tooltip="true" /> <el-table-column label="公告标题" align="center" prop="noticeTitle" :show-overflow-tooltip="true" />
<el-table-column label="公告类型" align="center" prop="noticeType" width="100"> <el-table-column label="公告类型" align="center" prop="noticeType" width="100">
<template #default="scope"> <template #default="scope">
@ -67,19 +67,19 @@
<el-table-column label="操作" align="center" class-name="small-padding fixed-width"> <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope"> <template #default="scope">
<el-tooltip content="修改" placement="top"> <el-tooltip content="修改" placement="top">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:notice:edit']"></el-button> <el-button v-hasPermi="['system:notice:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
</el-tooltip> </el-tooltip>
<el-tooltip content="删除" placement="top"> <el-tooltip content="删除" placement="top">
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:notice:remove']"></el-button> <el-button v-hasPermi="['system:notice:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
</el-tooltip> </el-tooltip>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
</el-card> </el-card>
<!-- 添加或修改公告对话框 --> <!-- 添加或修改公告对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="780px" append-to-body> <el-dialog v-model="dialog.visible" :title="dialog.title" width="780px" append-to-body>
<el-form ref="noticeFormRef" :model="form" :rules="rules" label-width="80px"> <el-form ref="noticeFormRef" :model="form" :rules="rules" label-width="80px">
<el-row> <el-row>
<el-col :span="12"> <el-col :span="12">
@ -97,8 +97,7 @@
<el-col :span="24"> <el-col :span="24">
<el-form-item label="状态"> <el-form-item label="状态">
<el-radio-group v-model="form.status"> <el-radio-group v-model="form.status">
<el-radio v-for="dict in sys_notice_status" :key="dict.value" :label="dict.value">{{ dict.label <el-radio v-for="dict in sys_notice_status" :key="dict.value" :label="dict.value">{{ dict.label }}</el-radio>
}}</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
</el-col> </el-col>
@ -120,11 +119,11 @@
</template> </template>
<script setup name="Notice" lang="ts"> <script setup name="Notice" lang="ts">
import { listNotice, getNotice, delNotice, addNotice, updateNotice } from "@/api/system/notice"; import { listNotice, getNotice, delNotice, addNotice, updateNotice } from '@/api/system/notice';
import { NoticeForm, NoticeQuery, NoticeVO } from "@/api/system/notice/types"; import { NoticeForm, NoticeQuery, NoticeVO } from '@/api/system/notice/types';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { sys_notice_status, sys_notice_type } = toRefs<any>(proxy?.useDict("sys_notice_status", "sys_notice_type")); const { sys_notice_status, sys_notice_type } = toRefs<any>(proxy?.useDict('sys_notice_status', 'sys_notice_type'));
const noticeList = ref<NoticeVO[]>([]); const noticeList = ref<NoticeVO[]>([]);
const loading = ref(true); const loading = ref(true);
@ -137,7 +136,6 @@ const total = ref(0);
const queryFormRef = ref<ElFormInstance>(); const queryFormRef = ref<ElFormInstance>();
const noticeFormRef = ref<ElFormInstance>(); const noticeFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({ const dialog = reactive<DialogOption>({
visible: false, visible: false,
title: '' title: ''
@ -148,10 +146,10 @@ const initFormData: NoticeForm = {
noticeTitle: '', noticeTitle: '',
noticeType: '', noticeType: '',
noticeContent: '', noticeContent: '',
status: "0", status: '0',
remark: '', remark: '',
createByName: '' createByName: ''
} };
const data = reactive<PageData<NoticeForm, NoticeQuery>>({ const data = reactive<PageData<NoticeForm, NoticeQuery>>({
form: { ...initFormData }, form: { ...initFormData },
queryParams: { queryParams: {
@ -163,9 +161,9 @@ const data = reactive<PageData<NoticeForm, NoticeQuery>>({
noticeType: '' noticeType: ''
}, },
rules: { rules: {
noticeTitle: [{ required: true, message: "公告标题不能为空", trigger: "blur" }], noticeTitle: [{ required: true, message: '公告标题不能为空', trigger: 'blur' }],
noticeType: [{ required: true, message: "公告类型不能为空", trigger: "change" }] noticeType: [{ required: true, message: '公告类型不能为空', trigger: 'change' }]
}, }
}); });
const { queryParams, form, rules } = toRefs(data); const { queryParams, form, rules } = toRefs(data);
@ -177,39 +175,39 @@ const getList = async () => {
noticeList.value = res.rows; noticeList.value = res.rows;
total.value = res.total; total.value = res.total;
loading.value = false; loading.value = false;
} };
/** 取消按钮 */ /** 取消按钮 */
const cancel = () => { const cancel = () => {
reset(); reset();
dialog.visible = false; dialog.visible = false;
} };
/** 表单重置 */ /** 表单重置 */
const reset = () => { const reset = () => {
form.value = { ...initFormData }; form.value = { ...initFormData };
noticeFormRef.value?.resetFields(); noticeFormRef.value?.resetFields();
} };
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
queryParams.value.pageNum = 1; queryParams.value.pageNum = 1;
getList(); getList();
} };
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
queryFormRef.value?.resetFields(); queryFormRef.value?.resetFields();
handleQuery(); handleQuery();
} };
/** 多选框选中数据 */ /** 多选框选中数据 */
const handleSelectionChange = (selection: NoticeVO[]) => { const handleSelectionChange = (selection: NoticeVO[]) => {
ids.value = selection.map(item => item.noticeId); ids.value = selection.map((item) => item.noticeId);
single.value = selection.length != 1; single.value = selection.length != 1;
multiple.value = !selection.length; multiple.value = !selection.length;
} };
/** 新增按钮操作 */ /** 新增按钮操作 */
const handleAdd = () => { const handleAdd = () => {
reset(); reset();
dialog.visible = true; dialog.visible = true;
dialog.title = "添加公告"; dialog.title = '添加公告';
} };
/**修改按钮操作 */ /**修改按钮操作 */
const handleUpdate = async (row?: NoticeVO) => { const handleUpdate = async (row?: NoticeVO) => {
reset(); reset();
@ -217,29 +215,29 @@ const handleUpdate = async (row?: NoticeVO) => {
const { data } = await getNotice(noticeId); const { data } = await getNotice(noticeId);
Object.assign(form.value, data); Object.assign(form.value, data);
dialog.visible = true; dialog.visible = true;
dialog.title = "修改公告"; dialog.title = '修改公告';
} };
/** 提交按钮 */ /** 提交按钮 */
const submitForm = () => { const submitForm = () => {
noticeFormRef.value?.validate(async (valid: boolean) => { noticeFormRef.value?.validate(async (valid: boolean) => {
if (valid) { if (valid) {
form.value.noticeId ? await updateNotice(form.value) : await addNotice(form.value); form.value.noticeId ? await updateNotice(form.value) : await addNotice(form.value);
proxy?.$modal.msgSuccess("修改成功"); proxy?.$modal.msgSuccess('修改成功');
dialog.visible = false; dialog.visible = false;
await getList(); await getList();
} }
}); });
} };
/** 删除按钮操作 */ /** 删除按钮操作 */
const handleDelete = async (row?: NoticeVO) => { const handleDelete = async (row?: NoticeVO) => {
const noticeIds = row?.noticeId || ids.value const noticeIds = row?.noticeId || ids.value;
await proxy?.$modal.confirm('是否确认删除公告编号为"' + noticeIds + '"的数据项?'); await proxy?.$modal.confirm('是否确认删除公告编号为"' + noticeIds + '"的数据项?');
await delNotice(noticeIds); await delNotice(noticeIds);
await getList(); await getList();
proxy?.$modal.msgSuccess("删除成功"); proxy?.$modal.msgSuccess('删除成功');
} };
onMounted(() => { onMounted(() => {
getList(); getList();
}) });
</script> </script>

View File

@ -1,9 +1,9 @@
<template> <template>
<div class="p-2"> <div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="mb-[10px]" v-show="showSearch"> <div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover"> <el-card shadow="hover">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px"> <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px">
<el-form-item label="配置key" prop="configKey"> <el-form-item label="配置key" prop="configKey">
<el-input v-model="queryParams.configKey" placeholder="配置key" clearable style="width: 200px" @keyup.enter="handleQuery" /> <el-input v-model="queryParams.configKey" placeholder="配置key" clearable style="width: 200px" @keyup.enter="handleQuery" />
</el-form-item> </el-form-item>
@ -39,27 +39,27 @@
删除 删除
</el-button> </el-button>
</el-col> </el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> <right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
</el-row> </el-row>
</template> </template>
<el-table v-loading="loading" :data="ossConfigList" @selection-change="handleSelectionChange"> <el-table v-loading="loading" :data="ossConfigList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" /> <el-table-column type="selection" width="55" align="center" />
<el-table-column label="主建" align="center" prop="ossConfigId" v-if="columns[0].visible" /> <el-table-column v-if="columns[0].visible" label="主建" align="center" prop="ossConfigId" />
<el-table-column label="配置key" align="center" prop="configKey" v-if="columns[1].visible" /> <el-table-column v-if="columns[1].visible" label="配置key" align="center" prop="configKey" />
<el-table-column label="访问站点" align="center" prop="endpoint" v-if="columns[2].visible" width="200" /> <el-table-column v-if="columns[2].visible" label="访问站点" align="center" prop="endpoint" width="200" />
<el-table-column label="自定义域名" align="center" prop="domain" v-if="columns[3].visible" width="200" /> <el-table-column v-if="columns[3].visible" label="自定义域名" align="center" prop="domain" width="200" />
<el-table-column label="桶名称" align="center" prop="bucketName" v-if="columns[4].visible" /> <el-table-column v-if="columns[4].visible" label="桶名称" align="center" prop="bucketName" />
<el-table-column label="前缀" align="center" prop="prefix" v-if="columns[5].visible" /> <el-table-column v-if="columns[5].visible" label="前缀" align="center" prop="prefix" />
<el-table-column label="域" align="center" prop="region" v-if="columns[6].visible" /> <el-table-column v-if="columns[6].visible" label="域" align="center" prop="region" />
<el-table-column label="桶权限类型" align="center" prop="accessPolicy" v-if="columns[7].visible"> <el-table-column v-if="columns[7].visible" label="桶权限类型" align="center" prop="accessPolicy">
<template #default="scope"> <template #default="scope">
<el-tag type="warning" v-if="scope.row.accessPolicy === '0'">private</el-tag> <el-tag v-if="scope.row.accessPolicy === '0'" type="warning">private</el-tag>
<el-tag type="success" v-if="scope.row.accessPolicy === '1'">public</el-tag> <el-tag v-if="scope.row.accessPolicy === '1'" type="success">public</el-tag>
<el-tag type="info" v-if="scope.row.accessPolicy === '2'">custom</el-tag> <el-tag v-if="scope.row.accessPolicy === '2'" type="info">custom</el-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="是否默认" align="center" prop="status" v-if="columns[8].visible"> <el-table-column v-if="columns[8].visible" label="是否默认" align="center" prop="status">
<template #default="scope"> <template #default="scope">
<el-switch v-model="scope.row.status" active-value="0" inactive-value="1" @change="handleStatusChange(scope.row)"></el-switch> <el-switch v-model="scope.row.status" active-value="0" inactive-value="1" @change="handleStatusChange(scope.row)"></el-switch>
</template> </template>
@ -76,10 +76,10 @@
</el-table-column> </el-table-column>
</el-table> </el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
</el-card> </el-card>
<!-- 添加或修改对象存储配置对话框 --> <!-- 添加或修改对象存储配置对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="800px" append-to-body> <el-dialog v-model="dialog.visible" :title="dialog.title" width="800px" append-to-body>
<el-form ref="ossConfigFormRef" :model="form" :rules="rules" label-width="120px"> <el-form ref="ossConfigFormRef" :model="form" :rules="rules" label-width="120px">
<el-form-item label="配置key" prop="configKey"> <el-form-item label="配置key" prop="configKey">
<el-input v-model="form.configKey" placeholder="请输入配置key" /> <el-input v-model="form.configKey" placeholder="请输入配置key" />
@ -132,19 +132,11 @@
</template> </template>
<script setup name="OssConfig" lang="ts"> <script setup name="OssConfig" lang="ts">
import { import { listOssConfig, getOssConfig, delOssConfig, addOssConfig, updateOssConfig, changeOssConfigStatus } from '@/api/system/ossConfig';
listOssConfig, import { OssConfigForm, OssConfigQuery, OssConfigVO } from '@/api/system/ossConfig/types';
getOssConfig,
delOssConfig,
addOssConfig,
updateOssConfig,
changeOssConfigStatus
} from "@/api/system/ossConfig";
import { OssConfigForm, OssConfigQuery, OssConfigVO } from "@/api/system/ossConfig/types";
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { proxy } = getCurrentInstance() as ComponentInternalInstance const { sys_yes_no } = toRefs<any>(proxy?.useDict('sys_yes_no'));
const { sys_yes_no } = toRefs<any>(proxy?.useDict("sys_yes_no"));
const ossConfigList = ref<OssConfigVO[]>([]); const ossConfigList = ref<OssConfigVO[]>([]);
const buttonLoading = ref(false); const buttonLoading = ref(false);
@ -176,7 +168,6 @@ const columns = ref<FieldOption[]>([
{ key: 8, label: `状态`, visible: true } { key: 8, label: `状态`, visible: true }
]); ]);
const initFormData: OssConfigForm = { const initFormData: OssConfigForm = {
ossConfigId: undefined, ossConfigId: undefined,
configKey: '', configKey: '',
@ -186,12 +177,12 @@ const initFormData: OssConfigForm = {
prefix: '', prefix: '',
endpoint: '', endpoint: '',
domain: '', domain: '',
isHttps: "N", isHttps: 'N',
accessPolicy: "1", accessPolicy: '1',
region: '', region: '',
status: "1", status: '1',
remark: '', remark: ''
} };
const data = reactive<PageData<OssConfigForm, OssConfigQuery>>({ const data = reactive<PageData<OssConfigForm, OssConfigQuery>>({
form: { ...initFormData }, form: { ...initFormData },
// 查询参数 // 查询参数
@ -200,47 +191,47 @@ const data = reactive<PageData<OssConfigForm, OssConfigQuery>>({
pageSize: 10, pageSize: 10,
configKey: '', configKey: '',
bucketName: '', bucketName: '',
status: '', status: ''
}, },
rules: { rules: {
configKey: [{ required: true, message: "configKey不能为空", trigger: "blur" },], configKey: [{ required: true, message: 'configKey不能为空', trigger: 'blur' }],
accessKey: [ accessKey: [
{ required: true, message: "accessKey不能为空", trigger: "blur" }, { required: true, message: 'accessKey不能为空', trigger: 'blur' },
{ {
min: 2, min: 2,
max: 200, max: 200,
message: "accessKey长度必须介于 2 和 100 之间", message: 'accessKey长度必须介于 2 和 100 之间',
trigger: "blur", trigger: 'blur'
}, }
], ],
secretKey: [ secretKey: [
{ required: true, message: "secretKey不能为空", trigger: "blur" }, { required: true, message: 'secretKey不能为空', trigger: 'blur' },
{ {
min: 2, min: 2,
max: 100, max: 100,
message: "secretKey长度必须介于 2 和 100 之间", message: 'secretKey长度必须介于 2 和 100 之间',
trigger: "blur", trigger: 'blur'
}, }
], ],
bucketName: [ bucketName: [
{ required: true, message: "bucketName不能为空", trigger: "blur" }, { required: true, message: 'bucketName不能为空', trigger: 'blur' },
{ {
min: 2, min: 2,
max: 100, max: 100,
message: "bucketName长度必须介于 2 和 100 之间", message: 'bucketName长度必须介于 2 和 100 之间',
trigger: "blur", trigger: 'blur'
}, }
], ],
endpoint: [ endpoint: [
{ required: true, message: "endpoint不能为空", trigger: "blur" }, { required: true, message: 'endpoint不能为空', trigger: 'blur' },
{ {
min: 2, min: 2,
max: 100, max: 100,
message: "endpoint名称长度必须介于 2 和 100 之间", message: 'endpoint名称长度必须介于 2 和 100 之间',
trigger: "blur", trigger: 'blur'
}, }
], ],
accessPolicy: [{ required: true, message: "accessPolicy不能为空", trigger: "blur" }] accessPolicy: [{ required: true, message: 'accessPolicy不能为空', trigger: 'blur' }]
} }
}); });
@ -253,39 +244,39 @@ const getList = async () => {
ossConfigList.value = res.rows; ossConfigList.value = res.rows;
total.value = res.total; total.value = res.total;
loading.value = false; loading.value = false;
} };
/** 取消按钮 */ /** 取消按钮 */
const cancel = () => { const cancel = () => {
dialog.visible = false; dialog.visible = false;
reset(); reset();
} };
/** 表单重置 */ /** 表单重置 */
const reset = () => { const reset = () => {
form.value = { ...initFormData }; form.value = { ...initFormData };
ossConfigFormRef.value?.resetFields(); ossConfigFormRef.value?.resetFields();
} };
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
queryParams.value.pageNum = 1; queryParams.value.pageNum = 1;
getList(); getList();
} };
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
queryFormRef.value?.resetFields(); queryFormRef.value?.resetFields();
handleQuery(); handleQuery();
} };
/** 选择条数 */ /** 选择条数 */
const handleSelectionChange = (selection: OssConfigVO[]) => { const handleSelectionChange = (selection: OssConfigVO[]) => {
ids.value = selection.map(item => item.ossConfigId); ids.value = selection.map((item) => item.ossConfigId);
single.value = selection.length != 1; single.value = selection.length != 1;
multiple.value = !selection.length; multiple.value = !selection.length;
} };
/** 新增按钮操作 */ /** 新增按钮操作 */
const handleAdd = () => { const handleAdd = () => {
reset(); reset();
dialog.visible = true; dialog.visible = true;
dialog.title = "添加对象存储配置"; dialog.title = '添加对象存储配置';
} };
/** 修改按钮操作 */ /** 修改按钮操作 */
const handleUpdate = async (row?: OssConfigVO) => { const handleUpdate = async (row?: OssConfigVO) => {
reset(); reset();
@ -293,49 +284,49 @@ const handleUpdate = async (row?: OssConfigVO) => {
const res = await getOssConfig(ossConfigId); const res = await getOssConfig(ossConfigId);
Object.assign(form.value, res.data); Object.assign(form.value, res.data);
dialog.visible = true; dialog.visible = true;
dialog.title = "修改对象存储配置"; dialog.title = '修改对象存储配置';
} };
/** 提交按钮 */ /** 提交按钮 */
const submitForm = () => { const submitForm = () => {
ossConfigFormRef.value?.validate(async (valid: boolean) => { ossConfigFormRef.value?.validate(async (valid: boolean) => {
if (valid) { if (valid) {
buttonLoading.value = true; buttonLoading.value = true;
if (form.value.ossConfigId) { if (form.value.ossConfigId) {
await updateOssConfig(form.value).finally(() => buttonLoading.value = false); await updateOssConfig(form.value).finally(() => (buttonLoading.value = false));
} else { } else {
await addOssConfig(form.value).finally(() => buttonLoading.value = false); await addOssConfig(form.value).finally(() => (buttonLoading.value = false));
} }
proxy?.$modal.msgSuccess("新增成功"); proxy?.$modal.msgSuccess('新增成功');
dialog.visible = false; dialog.visible = false;
await getList(); await getList();
} }
}); });
} };
/** 状态修改 */ /** 状态修改 */
const handleStatusChange = async (row: OssConfigVO) => { const handleStatusChange = async (row: OssConfigVO) => {
let text = row.status === "0" ? "启用" : "停用"; let text = row.status === '0' ? '启用' : '停用';
try { try {
await proxy?.$modal.confirm('确认要"' + text + '""' + row.configKey + '"配置吗?'); await proxy?.$modal.confirm('确认要"' + text + '""' + row.configKey + '"配置吗?');
await changeOssConfigStatus(row.ossConfigId, row.status, row.configKey); await changeOssConfigStatus(row.ossConfigId, row.status, row.configKey);
await getList() await getList();
proxy?.$modal.msgSuccess(text + "成功"); proxy?.$modal.msgSuccess(text + '成功');
} catch { return } finally { } catch {
row.status = row.status === "0" ? "1" : "0"; return;
} finally {
row.status = row.status === '0' ? '1' : '0';
} }
};
}
/** 删除按钮操作 */ /** 删除按钮操作 */
const handleDelete = async (row?: OssConfigVO) => { const handleDelete = async (row?: OssConfigVO) => {
const ossConfigIds = row?.ossConfigId || ids.value; const ossConfigIds = row?.ossConfigId || ids.value;
await proxy?.$modal.confirm('是否确认删除OSS配置编号为"' + ossConfigIds + '"的数据项?'); await proxy?.$modal.confirm('是否确认删除OSS配置编号为"' + ossConfigIds + '"的数据项?');
loading.value = true; loading.value = true;
await delOssConfig(ossConfigIds).finally(() => loading.value = false); await delOssConfig(ossConfigIds).finally(() => (loading.value = false));
await getList(); await getList();
proxy?.$modal.msgSuccess("删除成功"); proxy?.$modal.msgSuccess('删除成功');
};
}
onMounted(() => { onMounted(() => {
getList(); getList();
}) });
</script> </script>

View File

@ -1,9 +1,9 @@
<template> <template>
<div class="p-2"> <div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="mb-[10px]" v-show="showSearch"> <div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover"> <el-card shadow="hover">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px"> <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px">
<el-form-item label="文件名" prop="fileName"> <el-form-item label="文件名" prop="fileName">
<el-input v-model="queryParams.fileName" placeholder="请输入文件名" clearable style="width: 200px" @keyup.enter="handleQuery" /> <el-input v-model="queryParams.fileName" placeholder="请输入文件名" clearable style="width: 200px" @keyup.enter="handleQuery" />
</el-form-item> </el-form-item>
@ -40,44 +40,42 @@
<template #header> <template #header>
<el-row :gutter="10" class="mb8"> <el-row :gutter="10" class="mb8">
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="primary" plain icon="Upload" @click="handleFile" v-hasPermi="['system:oss:upload']">上传文件</el-button> <el-button v-hasPermi="['system:oss:upload']" type="primary" plain icon="Upload" @click="handleFile">上传文件</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="primary" plain icon="Upload" @click="handleImage" v-hasPermi="['system:oss:upload']">上传图片</el-button> <el-button v-hasPermi="['system:oss:upload']" type="primary" plain icon="Upload" @click="handleImage">上传图片</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:oss:remove']"> <el-button v-hasPermi="['system:oss:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">
删除 删除
</el-button> </el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button <el-button
v-hasPermi="['system:oss:edit']"
:type="previewListResource ? 'danger' : 'warning'" :type="previewListResource ? 'danger' : 'warning'"
plain plain
@click="handlePreviewListResource(!previewListResource)" @click="handlePreviewListResource(!previewListResource)"
v-hasPermi="['system:oss:edit']" >预览开关 : {{ previewListResource ? '禁用' : '启用' }}</el-button
>预览开关 :
{{
previewListResource ? "禁用" : "启用" }}</el-button
> >
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="info" plain icon="Operation" @click="handleOssConfig" v-hasPermi="['system:oss:list']">配置管理</el-button> <el-button v-hasPermi="['system:oss:list']" type="info" plain icon="Operation" @click="handleOssConfig">配置管理</el-button>
</el-col> </el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> <right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
</el-row> </el-row>
</template> </template>
<el-table <el-table
v-if="showTable"
v-loading="loading" v-loading="loading"
:data="ossList" :data="ossList"
@selection-change="handleSelectionChange"
:header-cell-class-name="handleHeaderClass" :header-cell-class-name="handleHeaderClass"
@selection-change="handleSelectionChange"
@header-click="handleHeaderCLick" @header-click="handleHeaderCLick"
v-if="showTable"
> >
<el-table-column type="selection" width="55" align="center" /> <el-table-column type="selection" width="55" align="center" />
<el-table-column label="对象存储主键" align="center" prop="ossId" v-if="false" /> <el-table-column v-if="false" label="对象存储主键" align="center" prop="ossId" />
<el-table-column label="文件名" align="center" prop="fileName" /> <el-table-column label="文件名" align="center" prop="fileName" />
<el-table-column label="原名" align="center" prop="originalName" /> <el-table-column label="原名" align="center" prop="originalName" />
<el-table-column label="文件后缀" align="center" prop="fileSuffix" /> <el-table-column label="文件后缀" align="center" prop="fileSuffix" />
@ -90,7 +88,7 @@
:src="scope.row.url" :src="scope.row.url"
:preview-src-list="[scope.row.url]" :preview-src-list="[scope.row.url]"
/> />
<span v-text="scope.row.url" v-if="!checkFileSuffix(scope.row.fileSuffix) || !previewListResource" /> <span v-if="!checkFileSuffix(scope.row.fileSuffix) || !previewListResource" v-text="scope.row.url" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="180" sortable="custom"> <el-table-column label="创建时间" align="center" prop="createTime" width="180" sortable="custom">
@ -103,23 +101,23 @@
<el-table-column label="操作" align="center" class-name="small-padding fixed-width"> <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope"> <template #default="scope">
<el-tooltip content="下载" placement="top"> <el-tooltip content="下载" placement="top">
<el-button link type="primary" icon="Download" @click="handleDownload(scope.row)" v-hasPermi="['system:oss:download']"></el-button> <el-button v-hasPermi="['system:oss:download']" link type="primary" icon="Download" @click="handleDownload(scope.row)"></el-button>
</el-tooltip> </el-tooltip>
<el-tooltip content="删除" placement="top"> <el-tooltip content="删除" placement="top">
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:oss:remove']"></el-button> <el-button v-hasPermi="['system:oss:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
</el-tooltip> </el-tooltip>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
</el-card> </el-card>
<!-- 添加或修改OSS对象存储对话框 --> <!-- 添加或修改OSS对象存储对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body> <el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
<el-form ref="ossFormRef" :model="form" :rules="rules" label-width="80px"> <el-form ref="ossFormRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="文件名"> <el-form-item label="文件名">
<fileUpload v-model="form.file" v-if="type === 0" /> <fileUpload v-if="type === 0" v-model="form.file" />
<imageUpload v-model="form.file" v-if="type === 1" /> <imageUpload v-if="type === 1" v-model="form.file" />
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
@ -133,9 +131,9 @@
</template> </template>
<script setup name="Oss" lang="ts"> <script setup name="Oss" lang="ts">
import { listOss, delOss } from "@/api/system/oss"; import { listOss, delOss } from '@/api/system/oss';
import ImagePreview from "@/components/ImagePreview/index.vue"; import ImagePreview from '@/components/ImagePreview/index.vue';
import { OssForm, OssQuery, OssVO } from "@/api/system/oss/types"; import { OssForm, OssQuery, OssVO } from '@/api/system/oss/types';
const router = useRouter(); const router = useRouter();
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@ -165,8 +163,8 @@ const ossFormRef = ref<ElFormInstance>();
const queryFormRef = ref<ElFormInstance>(); const queryFormRef = ref<ElFormInstance>();
const initFormData = { const initFormData = {
file: undefined, file: undefined
} };
const data = reactive<PageData<OssForm, OssQuery>>({ const data = reactive<PageData<OssForm, OssQuery>>({
form: { ...initFormData }, form: { ...initFormData },
// 查询参数 // 查询参数
@ -182,9 +180,7 @@ const data = reactive<PageData<OssForm, OssQuery>>({
isAsc: defaultSort.value.order isAsc: defaultSort.value.order
}, },
rules: { rules: {
file: [ file: [{ required: true, message: '文件不能为空', trigger: 'blur' }]
{ required: true, message: "文件不能为空", trigger: "blur" }
]
} }
}); });
@ -193,17 +189,17 @@ const { queryParams, form, rules } = toRefs(data);
/** 查询OSS对象存储列表 */ /** 查询OSS对象存储列表 */
const getList = async () => { const getList = async () => {
loading.value = true; loading.value = true;
const res = await proxy?.getConfigKey("sys.oss.previewListResource"); const res = await proxy?.getConfigKey('sys.oss.previewListResource');
previewListResource.value = res?.data === undefined ? true : res.data === 'true'; previewListResource.value = res?.data === undefined ? true : res.data === 'true';
const response = await listOss(proxy?.addDateRange(queryParams.value, dateRangeCreateTime.value, "CreateTime")); const response = await listOss(proxy?.addDateRange(queryParams.value, dateRangeCreateTime.value, 'CreateTime'));
ossList.value = response.rows; ossList.value = response.rows;
total.value = response.total; total.value = response.total;
loading.value = false; loading.value = false;
showTable.value = true; showTable.value = true;
} };
function checkFileSuffix(fileSuffix: string[]) { function checkFileSuffix(fileSuffix: string[]) {
let arr = ["png", "jpg", "jpeg"]; let arr = ['png', 'jpg', 'jpeg'];
return arr.some(type => { return arr.some((type) => {
return fileSuffix.indexOf(type) > -1; return fileSuffix.indexOf(type) > -1;
}); });
} }
@ -233,18 +229,18 @@ function resetQuery() {
} }
/** 选择条数 */ /** 选择条数 */
function handleSelectionChange(selection: OssVO[]) { function handleSelectionChange(selection: OssVO[]) {
ids.value = selection.map(item => item.ossId); ids.value = selection.map((item) => item.ossId);
single.value = selection.length != 1; single.value = selection.length != 1;
multiple.value = !selection.length; multiple.value = !selection.length;
} }
/** 设置列的排序为我们自定义的排序 */ /** 设置列的排序为我们自定义的排序 */
const handleHeaderClass = ({ column }: any): any => { const handleHeaderClass = ({ column }: any): any => {
column.order = column.multiOrder column.order = column.multiOrder;
} };
/** 点击表头进行排序 */ /** 点击表头进行排序 */
const handleHeaderCLick = (column: any) => { const handleHeaderCLick = (column: any) => {
if (column.sortable !== 'custom') { if (column.sortable !== 'custom') {
return return;
} }
switch (column.multiOrder) { switch (column.multiOrder) {
case 'descending': case 'descending':
@ -257,20 +253,20 @@ const handleHeaderCLick = (column: any) => {
column.multiOrder = 'descending'; column.multiOrder = 'descending';
break; break;
} }
handleOrderChange(column.property, column.multiOrder) handleOrderChange(column.property, column.multiOrder);
} };
const handleOrderChange = (prop: string, order: string) => { const handleOrderChange = (prop: string, order: string) => {
let orderByArr = queryParams.value.orderByColumn ? queryParams.value.orderByColumn.split(",") : []; let orderByArr = queryParams.value.orderByColumn ? queryParams.value.orderByColumn.split(',') : [];
let isAscArr = queryParams.value.isAsc ? queryParams.value.isAsc.split(",") : []; let isAscArr = queryParams.value.isAsc ? queryParams.value.isAsc.split(',') : [];
let propIndex = orderByArr.indexOf(prop) let propIndex = orderByArr.indexOf(prop);
if (propIndex !== -1) { if (propIndex !== -1) {
if (order) { if (order) {
//排序里已存在 只修改排序 //排序里已存在 只修改排序
isAscArr[propIndex] = order; isAscArr[propIndex] = order;
} else { } else {
//如果order为null 则删除排序字段和属性 //如果order为null 则删除排序字段和属性
isAscArr.splice(propIndex, 1);//删除排序 isAscArr.splice(propIndex, 1); //删除排序
orderByArr.splice(propIndex, 1);//删除属性 orderByArr.splice(propIndex, 1); //删除属性
} }
} else { } else {
//排序里不存在则新增排序 //排序里不存在则新增排序
@ -278,58 +274,60 @@ const handleOrderChange = (prop: string, order: string) => {
isAscArr.push(order); isAscArr.push(order);
} }
//合并排序 //合并排序
queryParams.value.orderByColumn = orderByArr.join(","); queryParams.value.orderByColumn = orderByArr.join(',');
queryParams.value.isAsc = isAscArr.join(","); queryParams.value.isAsc = isAscArr.join(',');
getList(); getList();
} };
/** 任务日志列表查询 */ /** 任务日志列表查询 */
const handleOssConfig = () => { const handleOssConfig = () => {
router.push('/system/oss-config/index') router.push('/system/oss-config/index');
} };
/** 文件按钮操作 */ /** 文件按钮操作 */
const handleFile = () => { const handleFile = () => {
reset(); reset();
type.value = 0; type.value = 0;
dialog.visible = true; dialog.visible = true;
dialog.title = "上传文件"; dialog.title = '上传文件';
} };
/** 图片按钮操作 */ /** 图片按钮操作 */
const handleImage = () => { const handleImage = () => {
reset(); reset();
type.value = 1; type.value = 1;
dialog.visible = true; dialog.visible = true;
dialog.title = "上传图片"; dialog.title = '上传图片';
} };
/** 提交按钮 */ /** 提交按钮 */
const submitForm = () => { const submitForm = () => {
dialog.visible = false; dialog.visible = false;
getList(); getList();
} };
/** 下载按钮操作 */ /** 下载按钮操作 */
const handleDownload = (row: OssVO) => { const handleDownload = (row: OssVO) => {
proxy?.$download.oss(row.ossId) proxy?.$download.oss(row.ossId);
} };
/** 用户状态修改 */ /** 用户状态修改 */
const handlePreviewListResource = async (preview: boolean) => { const handlePreviewListResource = async (preview: boolean) => {
let text = preview ? "启用" : "停用"; let text = preview ? '启用' : '停用';
try { try {
await proxy?.$modal.confirm('确认要"' + text + '""预览列表图片"配置吗?'); await proxy?.$modal.confirm('确认要"' + text + '""预览列表图片"配置吗?');
await proxy?.updateConfigByKey("sys.oss.previewListResource", preview); await proxy?.updateConfigByKey('sys.oss.previewListResource', preview);
await getList() await getList();
proxy?.$modal.msgSuccess(text + "成功"); proxy?.$modal.msgSuccess(text + '成功');
} catch { return } } catch {
} return;
}
};
/** 删除按钮操作 */ /** 删除按钮操作 */
const handleDelete = async (row?: OssVO) => { const handleDelete = async (row?: OssVO) => {
const ossIds = row?.ossId || ids.value; const ossIds = row?.ossId || ids.value;
await proxy?.$modal.confirm('是否确认删除OSS对象存储编号为"' + ossIds + '"的数据项?'); await proxy?.$modal.confirm('是否确认删除OSS对象存储编号为"' + ossIds + '"的数据项?');
loading.value = true; loading.value = true;
await delOss(ossIds).finally(() => loading.value = false); await delOss(ossIds).finally(() => (loading.value = false));
await getList(); await getList();
proxy?.$modal.msgSuccess("删除成功"); proxy?.$modal.msgSuccess('删除成功');
} };
onMounted(() => { onMounted(() => {
getList(); getList();
}) });
</script> </script>

View File

@ -1,9 +1,9 @@
<template> <template>
<div class="p-2"> <div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="mb-[10px]" v-show="showSearch"> <div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover"> <el-card shadow="hover">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="70"> <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="70">
<el-form-item label="岗位编码" prop="postCode"> <el-form-item label="岗位编码" prop="postCode">
<el-input v-model="queryParams.postCode" placeholder="请输入岗位编码" clearable style="width: 200px" @keyup.enter="handleQuery" /> <el-input v-model="queryParams.postCode" placeholder="请输入岗位编码" clearable style="width: 200px" @keyup.enter="handleQuery" />
</el-form-item> </el-form-item>
@ -27,26 +27,26 @@
<template #header> <template #header>
<el-row :gutter="10" class="mb8"> <el-row :gutter="10" class="mb8">
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:post:add']">新增</el-button> <el-button v-hasPermi="['system:post:add']" type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['system:post:edit']">修改</el-button> <el-button v-hasPermi="['system:post:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()">修改</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:post:remove']"> <el-button v-hasPermi="['system:post:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">
删除 删除
</el-button> </el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:post:export']">导出</el-button> <el-button v-hasPermi="['system:post:export']" type="warning" plain icon="Download" @click="handleExport">导出</el-button>
</el-col> </el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> <right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
</el-row> </el-row>
</template> </template>
<el-table v-loading="loading" :data="postList" @selection-change="handleSelectionChange"> <el-table v-loading="loading" :data="postList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" /> <el-table-column type="selection" width="55" align="center" />
<el-table-column label="岗位编号" align="center" prop="postId" v-if="false" /> <el-table-column v-if="false" label="岗位编号" align="center" prop="postId" />
<el-table-column label="岗位编码" align="center" prop="postCode" /> <el-table-column label="岗位编码" align="center" prop="postCode" />
<el-table-column label="岗位名称" align="center" prop="postName" /> <el-table-column label="岗位名称" align="center" prop="postName" />
<el-table-column label="岗位排序" align="center" prop="postSort" /> <el-table-column label="岗位排序" align="center" prop="postSort" />
@ -63,20 +63,20 @@
<el-table-column label="操作" width="180" align="center" class-name="small-padding fixed-width"> <el-table-column label="操作" width="180" align="center" class-name="small-padding fixed-width">
<template #default="scope"> <template #default="scope">
<el-tooltip content="修改" placement="top"> <el-tooltip content="修改" placement="top">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:post:edit']"></el-button> <el-button v-hasPermi="['system:post:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
</el-tooltip> </el-tooltip>
<el-tooltip content="删除" placement="top"> <el-tooltip content="删除" placement="top">
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:post:remove']"></el-button> <el-button v-hasPermi="['system:post:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
</el-tooltip> </el-tooltip>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
</el-card> </el-card>
<!-- 添加或修改岗位对话框 --> <!-- 添加或修改岗位对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body> <el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
<el-form ref="postFormRef" :model="form" :rules="rules" label-width="80px"> <el-form ref="postFormRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="岗位名称" prop="postName"> <el-form-item label="岗位名称" prop="postName">
<el-input v-model="form.postName" placeholder="请输入岗位名称" /> <el-input v-model="form.postName" placeholder="请输入岗位名称" />
@ -107,11 +107,11 @@
</template> </template>
<script setup name="Post" lang="ts"> <script setup name="Post" lang="ts">
import { listPost, addPost, delPost, getPost, updatePost } from "@/api/system/post"; import { listPost, addPost, delPost, getPost, updatePost } from '@/api/system/post';
import { PostForm, PostQuery, PostVO } from "@/api/system/post/types"; import { PostForm, PostQuery, PostVO } from '@/api/system/post/types';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { sys_normal_disable } = toRefs<any>(proxy?.useDict("sys_normal_disable")); const { sys_normal_disable } = toRefs<any>(proxy?.useDict('sys_normal_disable'));
const postList = ref<PostVO[]>([]); const postList = ref<PostVO[]>([]);
const loading = ref(true); const loading = ref(true);
@ -134,9 +134,9 @@ const initFormData: PostForm = {
postCode: '', postCode: '',
postName: '', postName: '',
postSort: 0, postSort: 0,
status: "0", status: '0',
remark: '' remark: ''
} };
const data = reactive<PageData<PostForm, PostQuery>>({ const data = reactive<PageData<PostForm, PostQuery>>({
form: { ...initFormData }, form: { ...initFormData },
@ -148,9 +148,9 @@ const data = reactive<PageData<PostForm, PostQuery>>({
status: '' status: ''
}, },
rules: { rules: {
postName: [{ required: true, message: "岗位名称不能为空", trigger: "blur" }], postName: [{ required: true, message: '岗位名称不能为空', trigger: 'blur' }],
postCode: [{ required: true, message: "岗位编码不能为空", trigger: "blur" }], postCode: [{ required: true, message: '岗位编码不能为空', trigger: 'blur' }],
postSort: [{ required: true, message: "岗位顺序不能为空", trigger: "blur" }], postSort: [{ required: true, message: '岗位顺序不能为空', trigger: 'blur' }]
} }
}); });
@ -163,39 +163,39 @@ const getList = async () => {
postList.value = res.rows; postList.value = res.rows;
total.value = res.total; total.value = res.total;
loading.value = false; loading.value = false;
} };
/** 取消按钮 */ /** 取消按钮 */
const cancel = () => { const cancel = () => {
reset(); reset();
dialog.visible = false; dialog.visible = false;
} };
/** 表单重置 */ /** 表单重置 */
const reset = () => { const reset = () => {
form.value = { ...initFormData }; form.value = { ...initFormData };
postFormRef.value?.resetFields(); postFormRef.value?.resetFields();
} };
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
queryParams.value.pageNum = 1; queryParams.value.pageNum = 1;
getList(); getList();
} };
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
queryFormRef.value?.resetFields(); queryFormRef.value?.resetFields();
handleQuery(); handleQuery();
} };
/** 多选框选中数据 */ /** 多选框选中数据 */
const handleSelectionChange = (selection: PostVO[]) => { const handleSelectionChange = (selection: PostVO[]) => {
ids.value = selection.map(item => item.postId); ids.value = selection.map((item) => item.postId);
single.value = selection.length != 1; single.value = selection.length != 1;
multiple.value = !selection.length; multiple.value = !selection.length;
} };
/** 新增按钮操作 */ /** 新增按钮操作 */
const handleAdd = () => { const handleAdd = () => {
reset(); reset();
dialog.visible = true; dialog.visible = true;
dialog.title = "添加岗位"; dialog.title = '添加岗位';
} };
/** 修改按钮操作 */ /** 修改按钮操作 */
const handleUpdate = async (row?: PostVO) => { const handleUpdate = async (row?: PostVO) => {
reset(); reset();
@ -203,33 +203,37 @@ const handleUpdate = async (row?: PostVO) => {
const res = await getPost(postId); const res = await getPost(postId);
Object.assign(form.value, res.data); Object.assign(form.value, res.data);
dialog.visible = true; dialog.visible = true;
dialog.title = "修改岗位"; dialog.title = '修改岗位';
} };
/** 提交按钮 */ /** 提交按钮 */
const submitForm = () => { const submitForm = () => {
postFormRef.value?.validate(async (valid: boolean) => { postFormRef.value?.validate(async (valid: boolean) => {
if (valid) { if (valid) {
form.value.postId ? await updatePost(form.value) : await addPost(form.value); form.value.postId ? await updatePost(form.value) : await addPost(form.value);
proxy?.$modal.msgSuccess("操作成功"); proxy?.$modal.msgSuccess('操作成功');
dialog.visible = false; dialog.visible = false;
await getList(); await getList();
} }
}); });
} };
/** 删除按钮操作 */ /** 删除按钮操作 */
const handleDelete = async (row?: PostVO) => { const handleDelete = async (row?: PostVO) => {
const postIds = row?.postId || ids.value; const postIds = row?.postId || ids.value;
await proxy?.$modal.confirm('是否确认删除岗位编号为"' + postIds + '"的数据项?'); await proxy?.$modal.confirm('是否确认删除岗位编号为"' + postIds + '"的数据项?');
await delPost(postIds); await delPost(postIds);
await getList(); await getList();
proxy?.$modal.msgSuccess("删除成功"); proxy?.$modal.msgSuccess('删除成功');
} };
/** 导出按钮操作 */ /** 导出按钮操作 */
const handleExport = () => { const handleExport = () => {
proxy?.download("system/post/export", { proxy?.download(
...queryParams.value 'system/post/export',
}, `post_${new Date().getTime()}.xlsx`); {
} ...queryParams.value
},
`post_${new Date().getTime()}.xlsx`
);
};
onMounted(() => { onMounted(() => {
getList(); getList();

View File

@ -1,8 +1,8 @@
<template> <template>
<div class="p-2"> <div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="search" v-show="showSearch"> <div v-show="showSearch" class="search">
<el-form :model="queryParams" ref="queryFormRef" :inline="true"> <el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="用户名称" prop="userName"> <el-form-item label="用户名称" prop="userName">
<el-input v-model="queryParams.userName" placeholder="请输入用户名称" clearable style="width: 240px" @keyup.enter="handleQuery" /> <el-input v-model="queryParams.userName" placeholder="请输入用户名称" clearable style="width: 240px" @keyup.enter="handleQuery" />
</el-form-item> </el-form-item>
@ -20,17 +20,17 @@
<template #header> <template #header>
<el-row :gutter="10"> <el-row :gutter="10">
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="openSelectUser" v-hasPermi="['system:role:add']">添加用户</el-button> <el-button v-hasPermi="['system:role:add']" type="primary" plain icon="Plus" @click="openSelectUser">添加用户</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="danger" plain icon="CircleClose" :disabled="multiple" @click="cancelAuthUserAll" v-hasPermi="['system:role:remove']"> <el-button v-hasPermi="['system:role:remove']" type="danger" plain icon="CircleClose" :disabled="multiple" @click="cancelAuthUserAll">
批量取消授权 批量取消授权
</el-button> </el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="warning" plain icon="Close" @click="handleClose">关闭</el-button> <el-button type="warning" plain icon="Close" @click="handleClose">关闭</el-button>
</el-col> </el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" :search="true"></right-toolbar> <right-toolbar v-model:showSearch="showSearch" :search="true" @query-table="getList"></right-toolbar>
</el-row> </el-row>
</template> </template>
<el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange"> <el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange">
@ -52,28 +52,27 @@
<el-table-column label="操作" align="center" class-name="small-padding fixed-width"> <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope"> <template #default="scope">
<el-tooltip content="取消授权" placement="top"> <el-tooltip content="取消授权" placement="top">
<el-button link type="primary" icon="CircleClose" @click="cancelAuthUser(scope.row)" v-hasPermi="['system:role:remove']"> </el-button> <el-button v-hasPermi="['system:role:remove']" link type="primary" icon="CircleClose" @click="cancelAuthUser(scope.row)"> </el-button>
</el-tooltip> </el-tooltip>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
<select-user ref="selectRef" :roleId="queryParams.roleId" @ok="handleQuery" /> <select-user ref="selectRef" :role-id="queryParams.roleId" @ok="handleQuery" />
</el-card> </el-card>
</div> </div>
</template> </template>
<script setup name="AuthUser" lang="ts"> <script setup name="AuthUser" lang="ts">
import { allocatedUserList, authUserCancel, authUserCancelAll } from "@/api/system/role"; import { allocatedUserList, authUserCancel, authUserCancelAll } from '@/api/system/role';
import { UserQuery } from "@/api/system/user/types"; import { UserQuery } from '@/api/system/user/types';
import { UserVO } from "@/api/system/user/types"; import { UserVO } from '@/api/system/user/types';
import SelectUser from "./selectUser.vue"; import SelectUser from './selectUser.vue';
const route = useRoute(); const route = useRoute();
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { sys_normal_disable } = toRefs<any>(proxy?.useDict("sys_normal_disable")); const { sys_normal_disable } = toRefs<any>(proxy?.useDict('sys_normal_disable'));
const userList = ref<UserVO[]>([]); const userList = ref<UserVO[]>([]);
const loading = ref(true); const loading = ref(true);
@ -90,7 +89,7 @@ const queryParams = reactive<UserQuery>({
pageSize: 10, pageSize: 10,
roleId: route.params.roleId as string, roleId: route.params.roleId as string,
userName: undefined, userName: undefined,
phonenumber: undefined, phonenumber: undefined
}); });
/** 查询授权用户列表 */ /** 查询授权用户列表 */
@ -100,47 +99,47 @@ const getList = async () => {
userList.value = res.rows; userList.value = res.rows;
total.value = res.total; total.value = res.total;
loading.value = false; loading.value = false;
} };
// 返回按钮 // 返回按钮
const handleClose = () => { const handleClose = () => {
const obj = { path: "/system/role" }; const obj = { path: '/system/role' };
proxy?.$tab.closeOpenPage(obj); proxy?.$tab.closeOpenPage(obj);
} };
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
queryParams.pageNum = 1; queryParams.pageNum = 1;
getList(); getList();
} };
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
queryFormRef.value?.resetFields(); queryFormRef.value?.resetFields();
handleQuery(); handleQuery();
} };
// 多选框选中数据 // 多选框选中数据
const handleSelectionChange = (selection: UserVO[]) => { const handleSelectionChange = (selection: UserVO[]) => {
userIds.value = selection.map(item => item.userId); userIds.value = selection.map((item) => item.userId);
multiple.value = !selection.length; multiple.value = !selection.length;
} };
/** 打开授权用户表弹窗 */ /** 打开授权用户表弹窗 */
const openSelectUser = () => { const openSelectUser = () => {
selectRef.value?.show(); selectRef.value?.show();
} };
/** 取消授权按钮操作 */ /** 取消授权按钮操作 */
const cancelAuthUser = async (row: UserVO) => { const cancelAuthUser = async (row: UserVO) => {
await proxy?.$modal.confirm('确认要取消该用户"' + row.userName + '"角色吗?'); await proxy?.$modal.confirm('确认要取消该用户"' + row.userName + '"角色吗?');
await authUserCancel({ userId: row.userId, roleId: queryParams.roleId }); await authUserCancel({ userId: row.userId, roleId: queryParams.roleId });
await getList(); await getList();
proxy?.$modal.msgSuccess("取消授权成功"); proxy?.$modal.msgSuccess('取消授权成功');
} };
/** 批量取消授权按钮操作 */ /** 批量取消授权按钮操作 */
const cancelAuthUserAll = async () => { const cancelAuthUserAll = async () => {
const roleId = queryParams.roleId; const roleId = queryParams.roleId;
const uIds = userIds.value.join(","); const uIds = userIds.value.join(',');
await proxy?.$modal.confirm("是否取消选中用户授权数据项?"); await proxy?.$modal.confirm('是否取消选中用户授权数据项?');
await authUserCancelAll({ roleId: roleId, userIds: uIds }); await authUserCancelAll({ roleId: roleId, userIds: uIds });
await getList(); await getList();
proxy?.$modal.msgSuccess("取消授权成功"); proxy?.$modal.msgSuccess('取消授权成功');
} };
onMounted(() => { onMounted(() => {
getList(); getList();

View File

@ -1,7 +1,7 @@
<template> <template>
<div class="p-2"> <div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="mb-[10px]" v-show="showSearch"> <div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover"> <el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px"> <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px">
<el-form-item label="角色名称" prop="roleName"> <el-form-item label="角色名称" prop="roleName">
@ -28,8 +28,8 @@
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" @click="handleQuery" icon="Search">搜索</el-button> <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button @click="resetQuery" icon="Refresh">重置</el-button> <el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
</el-card> </el-card>
@ -40,24 +40,24 @@
<template #header> <template #header>
<el-row :gutter="10"> <el-row :gutter="10">
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="primary" plain @click="handleAdd()" icon="Plus" v-hasPermi="['system:role:add']">新增</el-button> <el-button v-hasPermi="['system:role:add']" type="primary" plain icon="Plus" @click="handleAdd()">新增</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="success" plain @click="handleUpdate()" :disabled="single" icon="Edit" v-hasPermi="['system:role:edit']">修改</el-button> <el-button v-hasPermi="['system:role:edit']" type="success" plain :disabled="single" icon="Edit" @click="handleUpdate()">修改</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="danger" plain :disabled="ids.length === 0" @click="handleDelete()" v-hasPermi="['system:role:delete']">删除</el-button> <el-button v-hasPermi="['system:role:delete']" type="danger" plain :disabled="ids.length === 0" @click="handleDelete()">删除</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:role:export']">导出</el-button> <el-button v-hasPermi="['system:role:export']" type="warning" plain icon="Download" @click="handleExport">导出</el-button>
</el-col> </el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> <right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
</el-row> </el-row>
</template> </template>
<el-table ref="roleTableRef" v-loading="loading" :data="roleList" @selection-change="handleSelectionChange"> <el-table ref="roleTableRef" v-loading="loading" :data="roleList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" /> <el-table-column type="selection" width="55" align="center" />
<el-table-column label="角色编号" prop="roleId" width="120" v-if="false" /> <el-table-column v-if="false" label="角色编号" prop="roleId" width="120" />
<el-table-column label="角色名称" prop="roleName" :show-overflow-tooltip="true" width="150" /> <el-table-column label="角色名称" prop="roleName" :show-overflow-tooltip="true" width="150" />
<el-table-column label="权限字符" prop="roleKey" :show-overflow-tooltip="true" width="200" /> <el-table-column label="权限字符" prop="roleKey" :show-overflow-tooltip="true" width="200" />
<el-table-column label="显示顺序" prop="roleSort" width="100" /> <el-table-column label="显示顺序" prop="roleSort" width="100" />
@ -74,17 +74,17 @@
<el-table-column fixed="right" label="操作" width="180"> <el-table-column fixed="right" label="操作" width="180">
<template #default="scope"> <template #default="scope">
<el-tooltip content="修改" placement="top" v-if="scope.row.roleId !== 1"> <el-tooltip v-if="scope.row.roleId !== 1" content="修改" placement="top">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:role:edit']"></el-button> <el-button v-hasPermi="['system:role:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
</el-tooltip> </el-tooltip>
<el-tooltip content="删除" placement="top" v-if="scope.row.roleId !== 1"> <el-tooltip v-if="scope.row.roleId !== 1" content="删除" placement="top">
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:role:remove']"></el-button> <el-button v-hasPermi="['system:role:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
</el-tooltip> </el-tooltip>
<el-tooltip content="数据权限" placement="top" v-if="scope.row.roleId !== 1"> <el-tooltip v-if="scope.row.roleId !== 1" content="数据权限" placement="top">
<el-button link type="primary" icon="CircleCheck" @click="handleDataScope(scope.row)" v-hasPermi="['system:role:edit']"></el-button> <el-button v-hasPermi="['system:role:edit']" link type="primary" icon="CircleCheck" @click="handleDataScope(scope.row)"></el-button>
</el-tooltip> </el-tooltip>
<el-tooltip content="分配用户" placement="top" v-if="scope.row.roleId !== 1"> <el-tooltip v-if="scope.row.roleId !== 1" content="分配用户" placement="top">
<el-button link type="primary" icon="User" @click="handleAuthUser(scope.row)" v-hasPermi="['system:role:edit']"></el-button> <el-button v-hasPermi="['system:role:edit']" link type="primary" icon="User" @click="handleAuthUser(scope.row)"></el-button>
</el-tooltip> </el-tooltip>
</template> </template>
</el-table-column> </el-table-column>
@ -99,7 +99,7 @@
/> />
</el-card> </el-card>
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body> <el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
<el-form ref="roleFormRef" :model="form" :rules="rules" label-width="100px"> <el-form ref="roleFormRef" :model="form" :rules="rules" label-width="100px">
<el-form-item label="角色名称" prop="roleName"> <el-form-item label="角色名称" prop="roleName">
<el-input v-model="form.roleName" placeholder="请输入角色名称" /> <el-input v-model="form.roleName" placeholder="请输入角色名称" />
@ -120,9 +120,7 @@
</el-form-item> </el-form-item>
<el-form-item label="状态"> <el-form-item label="状态">
<el-radio-group v-model="form.status"> <el-radio-group v-model="form.status">
<el-radio v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.value">{{ <el-radio v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.value">{{ dict.label }}</el-radio>
dict.label
}}</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="菜单权限"> <el-form-item label="菜单权限">
@ -130,10 +128,10 @@
<el-checkbox v-model="menuNodeAll" @change="handleCheckedTreeNodeAll($event, 'menu')">全选/全不选</el-checkbox> <el-checkbox v-model="menuNodeAll" @change="handleCheckedTreeNodeAll($event, 'menu')">全选/全不选</el-checkbox>
<el-checkbox v-model="form.menuCheckStrictly" @change="handleCheckedTreeConnect($event, 'menu')">父子联动</el-checkbox> <el-checkbox v-model="form.menuCheckStrictly" @change="handleCheckedTreeConnect($event, 'menu')">父子联动</el-checkbox>
<el-tree <el-tree
ref="menuRef"
class="tree-border" class="tree-border"
:data="menuOptions" :data="menuOptions"
show-checkbox show-checkbox
ref="menuRef"
node-key="id" node-key="id"
:check-strictly="!form.menuCheckStrictly" :check-strictly="!form.menuCheckStrictly"
empty-text="加载中请稍候" empty-text="加载中请稍候"
@ -153,8 +151,8 @@
</el-dialog> </el-dialog>
<!-- 分配角色数据权限对话框 --> <!-- 分配角色数据权限对话框 -->
<el-dialog :title="dialog.title" v-model="openDataScope" width="500px" append-to-body> <el-dialog v-model="openDataScope" :title="dialog.title" width="500px" append-to-body>
<el-form :model="form" label-width="80px" ref="dataScopeRef"> <el-form ref="dataScopeRef" :model="form" label-width="80px">
<el-form-item label="角色名称"> <el-form-item label="角色名称">
<el-input v-model="form.roleName" :disabled="true" /> <el-input v-model="form.roleName" :disabled="true" />
</el-form-item> </el-form-item>
@ -166,16 +164,16 @@
<el-option v-for="item in dataScopeOptions" :key="item.value" :label="item.label" :value="item.value"></el-option> <el-option v-for="item in dataScopeOptions" :key="item.value" :label="item.label" :value="item.value"></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="数据权限" v-show="form.dataScope === '2'"> <el-form-item v-show="form.dataScope === '2'" label="数据权限">
<el-checkbox v-model="deptExpand" @change="handleCheckedTreeExpand($event, 'dept')">展开/折叠</el-checkbox> <el-checkbox v-model="deptExpand" @change="handleCheckedTreeExpand($event, 'dept')">展开/折叠</el-checkbox>
<el-checkbox v-model="deptNodeAll" @change="handleCheckedTreeNodeAll($event, 'dept')">全选/全不选</el-checkbox> <el-checkbox v-model="deptNodeAll" @change="handleCheckedTreeNodeAll($event, 'dept')">全选/全不选</el-checkbox>
<el-checkbox v-model="form.deptCheckStrictly" @change="handleCheckedTreeConnect($event, 'dept')">父子联动</el-checkbox> <el-checkbox v-model="form.deptCheckStrictly" @change="handleCheckedTreeConnect($event, 'dept')">父子联动</el-checkbox>
<el-tree <el-tree
ref="deptRef"
class="tree-border" class="tree-border"
:data="deptOptions" :data="deptOptions"
show-checkbox show-checkbox
default-expand-all default-expand-all
ref="deptRef"
node-key="id" node-key="id"
:check-strictly="!form.deptCheckStrictly" :check-strictly="!form.deptCheckStrictly"
empty-text="加载中请稍候" empty-text="加载中请稍候"
@ -194,7 +192,7 @@
</template> </template>
<script setup name="Role" lang="ts"> <script setup name="Role" lang="ts">
import { addRole, changeRoleStatus, dataScope, delRole, getRole, listRole, updateRole, deptTreeSelect } from "@/api/system/role"; import { addRole, changeRoleStatus, dataScope, delRole, getRole, listRole, updateRole, deptTreeSelect } from '@/api/system/role';
import { roleMenuTreeselect, treeselect as menuTreeselect } from '@/api/system/menu/index'; import { roleMenuTreeselect, treeselect as menuTreeselect } from '@/api/system/menu/index';
import { RoleVO, RoleForm, RoleQuery, DeptTreeOption } from '@/api/system/role/types'; import { RoleVO, RoleForm, RoleQuery, DeptTreeOption } from '@/api/system/role/types';
import { MenuTreeOption, RoleMenuTree } from '@/api/system/menu/types'; import { MenuTreeOption, RoleMenuTree } from '@/api/system/menu/types';
@ -204,29 +202,29 @@ const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { sys_normal_disable } = toRefs<any>(proxy?.useDict('sys_normal_disable')); const { sys_normal_disable } = toRefs<any>(proxy?.useDict('sys_normal_disable'));
const roleList = ref<RoleVO[]>(); const roleList = ref<RoleVO[]>();
const loading = ref(true) const loading = ref(true);
const showSearch = ref(true) const showSearch = ref(true);
const ids = ref<Array<string | number>>([]) const ids = ref<Array<string | number>>([]);
const single = ref(true) const single = ref(true);
const multiple = ref(true) const multiple = ref(true);
const total = ref(0) const total = ref(0);
const dateRange = ref<[DateModelType, DateModelType]>(['', '']) const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
const menuOptions = ref<MenuTreeOption[]>([]) const menuOptions = ref<MenuTreeOption[]>([]);
const menuExpand = ref(false) const menuExpand = ref(false);
const menuNodeAll = ref(false) const menuNodeAll = ref(false);
const deptExpand = ref(true) const deptExpand = ref(true);
const deptNodeAll = ref(false) const deptNodeAll = ref(false);
const deptOptions = ref<DeptTreeOption[]>([]) const deptOptions = ref<DeptTreeOption[]>([]);
const openDataScope = ref(false) const openDataScope = ref(false);
/** 数据范围选项*/ /** 数据范围选项*/
const dataScopeOptions = ref([ const dataScopeOptions = ref([
{ value: "1", label: "全部数据权限" }, { value: '1', label: '全部数据权限' },
{ value: "2", label: "自定数据权限" }, { value: '2', label: '自定数据权限' },
{ value: "3", label: "本部门数据权限" }, { value: '3', label: '本部门数据权限' },
{ value: "4", label: "本部门及以下数据权限" }, { value: '4', label: '本部门及以下数据权限' },
{ value: "5", label: "仅本人数据权限" } { value: '5', label: '仅本人数据权限' }
]) ]);
const queryFormRef = ref<ElFormInstance>(); const queryFormRef = ref<ElFormInstance>();
const roleFormRef = ref<ElFormInstance>(); const roleFormRef = ref<ElFormInstance>();
@ -245,8 +243,8 @@ const initForm: RoleForm = {
remark: '', remark: '',
dataScope: '1', dataScope: '1',
menuIds: [], menuIds: [],
deptIds: [], deptIds: []
} };
const data = reactive<PageData<RoleForm, RoleQuery>>({ const data = reactive<PageData<RoleForm, RoleQuery>>({
form: { ...initForm }, form: { ...initForm },
@ -255,15 +253,15 @@ const data = reactive<PageData<RoleForm, RoleQuery>>({
pageSize: 10, pageSize: 10,
roleName: '', roleName: '',
roleKey: '', roleKey: '',
status: '', status: ''
}, },
rules: { rules: {
roleName: [{ required: true, message: "角色名称不能为空", trigger: "blur" }], roleName: [{ required: true, message: '角色名称不能为空', trigger: 'blur' }],
roleKey: [{ required: true, message: "权限字符不能为空", trigger: "blur" }], roleKey: [{ required: true, message: '权限字符不能为空', trigger: 'blur' }],
roleSort: [{ required: true, message: "角色顺序不能为空", trigger: "blur" }] roleSort: [{ required: true, message: '角色顺序不能为空', trigger: 'blur' }]
} }
}) });
const { form, queryParams, rules } = toRefs(data) const { form, queryParams, rules } = toRefs(data);
const dialog = reactive<DialogOption>({ const dialog = reactive<DialogOption>({
visible: false, visible: false,
@ -274,13 +272,13 @@ const dialog = reactive<DialogOption>({
* 查询角色列表 * 查询角色列表
*/ */
const getList = () => { const getList = () => {
loading.value = true loading.value = true;
listRole(proxy?.addDateRange(queryParams.value, dateRange.value)).then(res => { listRole(proxy?.addDateRange(queryParams.value, dateRange.value)).then((res) => {
roleList.value = res.rows roleList.value = res.rows;
total.value = res.total total.value = res.total;
loading.value = false loading.value = false;
}) });
} };
/** /**
* 搜索按钮操作 * 搜索按钮操作
@ -288,14 +286,14 @@ const getList = () => {
const handleQuery = () => { const handleQuery = () => {
queryParams.value.pageNum = 1; queryParams.value.pageNum = 1;
getList(); getList();
} };
/** 重置 */ /** 重置 */
const resetQuery = () => { const resetQuery = () => {
dateRange.value = ['', ''] dateRange.value = ['', ''];
queryFormRef.value?.resetFields(); queryFormRef.value?.resetFields();
handleQuery(); handleQuery();
} };
/**删除按钮操作 */ /**删除按钮操作 */
const handleDelete = async (row?: RoleVO) => { const handleDelete = async (row?: RoleVO) => {
const roleids = row?.roleId || ids.value; const roleids = row?.roleId || ids.value;
@ -303,43 +301,47 @@ const handleDelete = async (row?: RoleVO) => {
await delRole(roleids); await delRole(roleids);
getList(); getList();
proxy?.$modal.msgSuccess('删除成功'); proxy?.$modal.msgSuccess('删除成功');
} };
/** 导出按钮操作 */ /** 导出按钮操作 */
const handleExport = () => { const handleExport = () => {
proxy?.download("system/role/export", { proxy?.download(
...queryParams.value, 'system/role/export',
}, `role_${new Date().getTime()}.xlsx`) {
} ...queryParams.value
},
`role_${new Date().getTime()}.xlsx`
);
};
/** 多选框选中数据 */ /** 多选框选中数据 */
const handleSelectionChange = (selection: RoleVO[]) => { const handleSelectionChange = (selection: RoleVO[]) => {
ids.value = selection.map((item: RoleVO) => item.roleId); ids.value = selection.map((item: RoleVO) => item.roleId);
single.value = selection.length != 1; single.value = selection.length != 1;
multiple.value = !selection.length; multiple.value = !selection.length;
} };
/** 角色状态修改 */ /** 角色状态修改 */
const handleStatusChange = async (row: RoleVO) => { const handleStatusChange = async (row: RoleVO) => {
let text = row.status === "0" ? "启用" : "停用"; let text = row.status === '0' ? '启用' : '停用';
try { try {
await proxy?.$modal.confirm('确认要"' + text + '""' + row.roleName + '"角色吗?'); await proxy?.$modal.confirm('确认要"' + text + '""' + row.roleName + '"角色吗?');
await changeRoleStatus(row.roleId, row.status); await changeRoleStatus(row.roleId, row.status);
proxy?.$modal.msgSuccess(text + "成功"); proxy?.$modal.msgSuccess(text + '成功');
} catch { } catch {
row.status = row.status === "0" ? "1" : "0"; row.status = row.status === '0' ? '1' : '0';
} }
} };
/** 分配用户 */ /** 分配用户 */
const handleAuthUser = (row: RoleVO) => { const handleAuthUser = (row: RoleVO) => {
router.push("/system/role-auth/user/" + row.roleId); router.push('/system/role-auth/user/' + row.roleId);
} };
/** 查询菜单树结构 */ /** 查询菜单树结构 */
const getMenuTreeselect = async () => { const getMenuTreeselect = async () => {
const res = await menuTreeselect(); const res = await menuTreeselect();
menuOptions.value = res.data; menuOptions.value = res.data;
} };
/** 所有部门节点数据 */ /** 所有部门节点数据 */
const getDeptAllCheckedKeys = (): any => { const getDeptAllCheckedKeys = (): any => {
// 目前被选中的部门节点 // 目前被选中的部门节点
@ -349,67 +351,65 @@ const getDeptAllCheckedKeys = (): any => {
if (halfCheckedKeys) { if (halfCheckedKeys) {
checkedKeys?.unshift.apply(checkedKeys, halfCheckedKeys); checkedKeys?.unshift.apply(checkedKeys, halfCheckedKeys);
} }
return checkedKeys return checkedKeys;
} };
/** 重置新增的表单以及其他数据 */ /** 重置新增的表单以及其他数据 */
const reset = () => { const reset = () => {
menuRef.value?.setCheckedKeys([]); menuRef.value?.setCheckedKeys([]);
menuExpand.value = false menuExpand.value = false;
menuNodeAll.value = false menuNodeAll.value = false;
deptExpand.value = true deptExpand.value = true;
deptNodeAll.value = false deptNodeAll.value = false;
form.value = { ...initForm }; form.value = { ...initForm };
roleFormRef.value?.resetFields(); roleFormRef.value?.resetFields();
};
}
/** 添加角色 */ /** 添加角色 */
const handleAdd = () => { const handleAdd = () => {
reset(); reset();
getMenuTreeselect(); getMenuTreeselect();
dialog.visible = true; dialog.visible = true;
dialog.title = "添加角色"; dialog.title = '添加角色';
} };
/** 修改角色 */ /** 修改角色 */
const handleUpdate = async (row?: RoleVO) => { const handleUpdate = async (row?: RoleVO) => {
reset(); reset();
const roleId = row?.roleId || ids.value[0] const roleId = row?.roleId || ids.value[0];
const { data } = await getRole(roleId); const { data } = await getRole(roleId);
Object.assign(form.value, data); Object.assign(form.value, data);
form.value.roleSort = Number(form.value.roleSort); form.value.roleSort = Number(form.value.roleSort);
const res = await getRoleMenuTreeselect(roleId); const res = await getRoleMenuTreeselect(roleId);
dialog.title = "修改角色"; dialog.title = '修改角色';
dialog.visible = true; dialog.visible = true;
res.checkedKeys.forEach((v) => { res.checkedKeys.forEach((v) => {
nextTick(() => { nextTick(() => {
menuRef.value?.setChecked(v, true, false); menuRef.value?.setChecked(v, true, false);
}) });
}) });
};
}
/** 根据角色ID查询菜单树结构 */ /** 根据角色ID查询菜单树结构 */
const getRoleMenuTreeselect = (roleId: string | number) => { const getRoleMenuTreeselect = (roleId: string | number) => {
return roleMenuTreeselect(roleId).then((res): RoleMenuTree => { return roleMenuTreeselect(roleId).then((res): RoleMenuTree => {
menuOptions.value = res.data.menus; menuOptions.value = res.data.menus;
return res.data; return res.data;
}) });
} };
/** 根据角色ID查询部门树结构 */ /** 根据角色ID查询部门树结构 */
const getRoleDeptTreeSelect = async (roleId: string | number) => { const getRoleDeptTreeSelect = async (roleId: string | number) => {
const res = await deptTreeSelect(roleId); const res = await deptTreeSelect(roleId);
deptOptions.value = res.data.depts; deptOptions.value = res.data.depts;
return res.data; return res.data;
} };
/** 树权限(展开/折叠)*/ /** 树权限(展开/折叠)*/
const handleCheckedTreeExpand = (value: boolean, type: string) => { const handleCheckedTreeExpand = (value: boolean, type: string) => {
if (type == "menu") { if (type == 'menu') {
let treeList = menuOptions.value; let treeList = menuOptions.value;
for (let i = 0; i < treeList.length; i++) { for (let i = 0; i < treeList.length; i++) {
if (menuRef.value) { if (menuRef.value) {
menuRef.value.store.nodesMap[treeList[i].id].expanded = value; menuRef.value.store.nodesMap[treeList[i].id].expanded = value;
} }
} }
} else if (type == "dept") { } else if (type == 'dept') {
let treeList = deptOptions.value; let treeList = deptOptions.value;
for (let i = 0; i < treeList.length; i++) { for (let i = 0; i < treeList.length; i++) {
if (deptRef.value) { if (deptRef.value) {
@ -417,23 +417,23 @@ const handleCheckedTreeExpand = (value: boolean, type: string) => {
} }
} }
} }
} };
/** 树权限(全选/全不选) */ /** 树权限(全选/全不选) */
const handleCheckedTreeNodeAll = (value: any, type: string) => { const handleCheckedTreeNodeAll = (value: any, type: string) => {
if (type == "menu") { if (type == 'menu') {
menuRef.value?.setCheckedNodes(value ? menuOptions.value as any : []); menuRef.value?.setCheckedNodes(value ? (menuOptions.value as any) : []);
} else if (type == "dept") { } else if (type == 'dept') {
deptRef.value?.setCheckedNodes(value ? deptOptions.value as any : []); deptRef.value?.setCheckedNodes(value ? (deptOptions.value as any) : []);
} }
} };
/** 树权限(父子联动) */ /** 树权限(父子联动) */
const handleCheckedTreeConnect = (value: any, type: string) => { const handleCheckedTreeConnect = (value: any, type: string) => {
if (type == "menu") { if (type == 'menu') {
form.value.menuCheckStrictly = value; form.value.menuCheckStrictly = value;
} else if (type == "dept") { } else if (type == 'dept') {
form.value.deptCheckStrictly = value; form.value.deptCheckStrictly = value;
} }
} };
/** 所有菜单节点数据 */ /** 所有菜单节点数据 */
const getMenuAllCheckedKeys = (): any => { const getMenuAllCheckedKeys = (): any => {
// 目前被选中的菜单节点 // 目前被选中的菜单节点
@ -444,57 +444,57 @@ const getMenuAllCheckedKeys = (): any => {
checkedKeys?.unshift.apply(checkedKeys, halfCheckedKeys); checkedKeys?.unshift.apply(checkedKeys, halfCheckedKeys);
} }
return checkedKeys; return checkedKeys;
} };
/** 提交按钮 */ /** 提交按钮 */
const submitForm = () => { const submitForm = () => {
roleFormRef.value?.validate(async (valid: boolean) => { roleFormRef.value?.validate(async (valid: boolean) => {
if (valid) { if (valid) {
form.value.menuIds = getMenuAllCheckedKeys() form.value.menuIds = getMenuAllCheckedKeys();
form.value.roleId ? await updateRole(form.value) : await addRole(form.value); form.value.roleId ? await updateRole(form.value) : await addRole(form.value);
proxy?.$modal.msgSuccess("操作成功") proxy?.$modal.msgSuccess('操作成功');
dialog.visible = false dialog.visible = false;
getList() getList();
} }
}) });
} };
/** 取消按钮 */ /** 取消按钮 */
const cancel = () => { const cancel = () => {
reset() reset();
dialog.visible = false; dialog.visible = false;
} };
/** 选择角色权限范围触发 */ /** 选择角色权限范围触发 */
const dataScopeSelectChange = (value: string) => { const dataScopeSelectChange = (value: string) => {
if (value !== "2") { if (value !== '2') {
deptRef.value?.setCheckedKeys([]) deptRef.value?.setCheckedKeys([]);
} }
} };
/** 分配数据权限操作 */ /** 分配数据权限操作 */
const handleDataScope = async (row: RoleVO) => { const handleDataScope = async (row: RoleVO) => {
const response = await getRole(row.roleId); const response = await getRole(row.roleId);
Object.assign(form.value, response.data); Object.assign(form.value, response.data);
const res = await getRoleDeptTreeSelect(row.roleId); const res = await getRoleDeptTreeSelect(row.roleId);
openDataScope.value = true; openDataScope.value = true;
dialog.title = "分配数据权限"; dialog.title = '分配数据权限';
await nextTick(() => { await nextTick(() => {
deptRef.value?.setCheckedKeys(res.checkedKeys); deptRef.value?.setCheckedKeys(res.checkedKeys);
}) });
} };
/** 提交按钮(数据权限) */ /** 提交按钮(数据权限) */
const submitDataScope = async () => { const submitDataScope = async () => {
if (form.value.roleId) { if (form.value.roleId) {
form.value.deptIds = getDeptAllCheckedKeys(); form.value.deptIds = getDeptAllCheckedKeys();
await dataScope(form.value); await dataScope(form.value);
proxy?.$modal.msgSuccess("修改成功"); proxy?.$modal.msgSuccess('修改成功');
openDataScope.value = false; openDataScope.value = false;
getList(); getList();
} }
} };
/** 取消按钮(数据权限)*/ /** 取消按钮(数据权限)*/
const cancelDataScope = () => { const cancelDataScope = () => {
dataScopeRef.value?.resetFields(); dataScopeRef.value?.resetFields();
form.value = { ...initForm }; form.value = { ...initForm };
openDataScope.value = false; openDataScope.value = false;
} };
onMounted(() => { onMounted(() => {
getList(); getList();

View File

@ -1,7 +1,7 @@
<template> <template>
<el-row> <el-row>
<el-dialog title="选择用户" v-model="visible" width="800px" top="5vh" append-to-body> <el-dialog v-model="visible" title="选择用户" width="800px" top="5vh" append-to-body>
<el-form :model="queryParams" ref="queryFormRef" :inline="true"> <el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="用户名称" prop="userName"> <el-form-item label="用户名称" prop="userName">
<el-input v-model="queryParams.userName" placeholder="请输入用户名称" clearable @keyup.enter="handleQuery" /> <el-input v-model="queryParams.userName" placeholder="请输入用户名称" clearable @keyup.enter="handleQuery" />
</el-form-item> </el-form-item>
@ -14,7 +14,7 @@
</el-form-item> </el-form-item>
</el-form> </el-form>
<el-row> <el-row>
<el-table @row-click="clickRow" ref="tableRef" :data="userList" @selection-change="handleSelectionChange" height="260px"> <el-table ref="tableRef" :data="userList" height="260px" @row-click="clickRow" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55"></el-table-column> <el-table-column type="selection" width="55"></el-table-column>
<el-table-column label="用户名称" prop="userName" :show-overflow-tooltip="true" /> <el-table-column label="用户名称" prop="userName" :show-overflow-tooltip="true" />
<el-table-column label="用户昵称" prop="nickName" :show-overflow-tooltip="true" /> <el-table-column label="用户昵称" prop="nickName" :show-overflow-tooltip="true" />
@ -31,7 +31,7 @@
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<pagination v-if="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> <pagination v-if="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
</el-row> </el-row>
<template #footer> <template #footer>
<div class="dialog-footer"> <div class="dialog-footer">
@ -44,16 +44,15 @@
</template> </template>
<script setup name="SelectUser" lang="ts"> <script setup name="SelectUser" lang="ts">
import { authUserSelectAll, unallocatedUserList } from "@/api/system/role"; import { authUserSelectAll, unallocatedUserList } from '@/api/system/role';
import { UserVO } from '@/api/system/user/types'; import { UserVO } from '@/api/system/user/types';
import { UserQuery } from '@/api/system/user/types'; import { UserQuery } from '@/api/system/user/types';
const props = defineProps({ const props = defineProps({
roleId: { roleId: {
type: [Number, String] type: [Number, String]
} }
}) });
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { sys_normal_disable } = toRefs<any>(proxy?.useDict('sys_normal_disable')); const { sys_normal_disable } = toRefs<any>(proxy?.useDict('sys_normal_disable'));
@ -69,7 +68,7 @@ const queryParams = reactive<UserQuery>({
roleId: undefined, roleId: undefined,
userName: undefined, userName: undefined,
phonenumber: undefined phonenumber: undefined
}) });
const tableRef = ref<ElTableInstance>(); const tableRef = ref<ElTableInstance>();
const queryFormRef = ref<ElFormInstance>(); const queryFormRef = ref<ElFormInstance>();
@ -78,7 +77,7 @@ const show = () => {
queryParams.roleId = props.roleId; queryParams.roleId = props.roleId;
getList(); getList();
visible.value = true; visible.value = true;
} };
/** /**
* 选择行 * 选择行
@ -86,35 +85,35 @@ const show = () => {
const clickRow = (row: any) => { const clickRow = (row: any) => {
// ele的bug // ele的bug
tableRef.value?.toggleRowSelection(row, false); tableRef.value?.toggleRowSelection(row, false);
} };
/** 多选框选中数据 */ /** 多选框选中数据 */
const handleSelectionChange = (selection: UserVO[]) => { const handleSelectionChange = (selection: UserVO[]) => {
userIds.value = selection.map((item: UserVO) => item.userId); userIds.value = selection.map((item: UserVO) => item.userId);
} };
/** 查询数据 */ /** 查询数据 */
const getList = async () => { const getList = async () => {
const res = await unallocatedUserList(queryParams); const res = await unallocatedUserList(queryParams);
userList.value = res.rows; userList.value = res.rows;
total.value = res.total; total.value = res.total;
} };
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
queryParams.pageNum = 1; queryParams.pageNum = 1;
getList(); getList();
} };
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
queryFormRef.value?.resetFields(); queryFormRef.value?.resetFields();
getList(); getList();
} };
const emit = defineEmits(["ok"]); const emit = defineEmits(['ok']);
/**选择授权用户操作 */ /**选择授权用户操作 */
const handleSelectUser = async () => { const handleSelectUser = async () => {
const roleId = queryParams.roleId; const roleId = queryParams.roleId;
const ids = userIds.value.join(','); const ids = userIds.value.join(',');
if (ids == "") { if (ids == '') {
proxy?.$modal.msgError('请选择要分配的用户'); proxy?.$modal.msgError('请选择要分配的用户');
return; return;
} }
@ -122,10 +121,10 @@ const handleSelectUser = async () => {
proxy?.$modal.msgSuccess('分配成功'); proxy?.$modal.msgSuccess('分配成功');
emit('ok'); emit('ok');
visible.value = false; visible.value = false;
} };
// 暴露 // 暴露
defineExpose({ defineExpose({
show, show
}); });
</script> </script>

View File

@ -1,9 +1,9 @@
<template> <template>
<div class="p-2"> <div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="mb-[10px]" v-show="showSearch"> <div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover"> <el-card shadow="hover">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px"> <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px">
<el-form-item label="租户编号" prop="tenantId"> <el-form-item label="租户编号" prop="tenantId">
<el-input v-model="queryParams.tenantId" placeholder="请输入租户编号" clearable style="width: 240px" @keyup.enter="handleQuery" /> <el-input v-model="queryParams.tenantId" placeholder="请输入租户编号" clearable style="width: 240px" @keyup.enter="handleQuery" />
</el-form-item> </el-form-item>
@ -29,28 +29,28 @@
<template #header> <template #header>
<el-row :gutter="10" class="mb8"> <el-row :gutter="10" class="mb8">
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:tenant:add']">新增</el-button> <el-button v-hasPermi="['system:tenant:add']" type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['system:tenant:edit']" <el-button v-hasPermi="['system:tenant:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()"
>修改</el-button >修改</el-button
> >
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:tenant:remove']"> <el-button v-hasPermi="['system:tenant:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">
删除 删除
</el-button> </el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:tenant:export']">导出</el-button> <el-button v-hasPermi="['system:tenant:export']" type="warning" plain icon="Download" @click="handleExport">导出</el-button>
</el-col> </el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> <right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
</el-row> </el-row>
</template> </template>
<el-table v-loading="loading" :data="tenantList" @selection-change="handleSelectionChange"> <el-table v-loading="loading" :data="tenantList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" /> <el-table-column type="selection" width="55" align="center" />
<el-table-column label="id" align="center" prop="id" v-if="false" /> <el-table-column v-if="false" label="id" align="center" prop="id" />
<el-table-column label="租户编号" align="center" prop="tenantId" /> <el-table-column label="租户编号" align="center" prop="tenantId" />
<el-table-column label="联系人" align="center" prop="contactUserName" /> <el-table-column label="联系人" align="center" prop="contactUserName" />
<el-table-column label="联系电话" align="center" prop="contactPhone" /> <el-table-column label="联系电话" align="center" prop="contactPhone" />
@ -69,23 +69,23 @@
<el-table-column width="150" label="操作" align="center" fixed="right" class-name="small-padding fixed-width"> <el-table-column width="150" label="操作" align="center" fixed="right" class-name="small-padding fixed-width">
<template #default="scope"> <template #default="scope">
<el-tooltip content="修改" placement="top"> <el-tooltip content="修改" placement="top">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:tenant:edit']"></el-button> <el-button v-hasPermi="['system:tenant:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
</el-tooltip> </el-tooltip>
<el-tooltip content="同步套餐" placement="top"> <el-tooltip content="同步套餐" placement="top">
<el-button link type="primary" icon="Refresh" @click="handleSyncTenantPackage(scope.row)" v-hasPermi="['system:tenant:edit']"> <el-button v-hasPermi="['system:tenant:edit']" link type="primary" icon="Refresh" @click="handleSyncTenantPackage(scope.row)">
</el-button> </el-button>
</el-tooltip> </el-tooltip>
<el-tooltip content="删除" placement="top"> <el-tooltip content="删除" placement="top">
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:tenant:remove']"></el-button> <el-button v-hasPermi="['system:tenant:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
</el-tooltip> </el-tooltip>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
</el-card> </el-card>
<!-- 添加或修改租户对话框 --> <!-- 添加或修改租户对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body> <el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
<el-form ref="tenantFormRef" :model="form" :rules="rules" label-width="80px"> <el-form ref="tenantFormRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="企业名称" prop="companyName"> <el-form-item label="企业名称" prop="companyName">
<el-input v-model="form.companyName" placeholder="请输入企业名称" /> <el-input v-model="form.companyName" placeholder="请输入企业名称" />
@ -100,7 +100,7 @@
<el-input v-model="form.username" placeholder="请输入系统用户名" maxlength="30" /> <el-input v-model="form.username" placeholder="请输入系统用户名" maxlength="30" />
</el-form-item> </el-form-item>
<el-form-item v-if="!form.id" label="用户密码" prop="password"> <el-form-item v-if="!form.id" label="用户密码" prop="password">
<el-input type="password" v-model="form.password" placeholder="请输入系统用户密码" maxlength="20" /> <el-input v-model="form.password" type="password" placeholder="请输入系统用户密码" maxlength="20" />
</el-form-item> </el-form-item>
<el-form-item label="租户套餐" prop="packageId"> <el-form-item label="租户套餐" prop="packageId">
<el-select v-model="form.packageId" :disabled="!!form.tenantId" placeholder="请选择租户套餐" clearable style="width: 100%"> <el-select v-model="form.packageId" :disabled="!!form.tenantId" placeholder="请选择租户套餐" clearable style="width: 100%">
@ -108,7 +108,7 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="过期时间" prop="expireTime"> <el-form-item label="过期时间" prop="expireTime">
<el-date-picker clearable v-model="form.expireTime" type="datetime" value-format="YYYY-MM-DD HH:mm:ss" placeholder="请选择过期时间"> <el-date-picker v-model="form.expireTime" clearable type="datetime" value-format="YYYY-MM-DD HH:mm:ss" placeholder="请选择过期时间">
</el-date-picker> </el-date-picker>
</el-form-item> </el-form-item>
<el-form-item label="用户数量" prop="accountCount"> <el-form-item label="用户数量" prop="accountCount">
@ -124,7 +124,7 @@
<el-input v-model="form.licenseNumber" placeholder="请输入统一社会信用代码" /> <el-input v-model="form.licenseNumber" placeholder="请输入统一社会信用代码" />
</el-form-item> </el-form-item>
<el-form-item label="企业简介" prop="intro"> <el-form-item label="企业简介" prop="intro">
<el-input type="textarea" v-model="form.intro" placeholder="请输入企业简介" /> <el-input v-model="form.intro" type="textarea" placeholder="请输入企业简介" />
</el-form-item> </el-form-item>
<el-form-item label="备注" prop="remark"> <el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" placeholder="请输入备注" /> <el-input v-model="form.remark" placeholder="请输入备注" />
@ -182,8 +182,8 @@ const initFormData: TenantForm = {
packageId: '', packageId: '',
expireTime: '', expireTime: '',
accountCount: 0, accountCount: 0,
status: '0', status: '0'
} };
const data = reactive<PageData<TenantForm, TenantQuery>>({ const data = reactive<PageData<TenantForm, TenantQuery>>({
form: { ...initFormData }, form: { ...initFormData },
queryParams: { queryParams: {
@ -195,17 +195,17 @@ const data = reactive<PageData<TenantForm, TenantQuery>>({
companyName: '' companyName: ''
}, },
rules: { rules: {
id: [{ required: true, message: "id不能为空", trigger: "blur" }], id: [{ required: true, message: 'id不能为空', trigger: 'blur' }],
tenantId: [{ required: true, message: "租户编号不能为空", trigger: "blur" }], tenantId: [{ required: true, message: '租户编号不能为空', trigger: 'blur' }],
contactUserName: [{ required: true, message: "联系人不能为空", trigger: "blur" }], contactUserName: [{ required: true, message: '联系人不能为空', trigger: 'blur' }],
contactPhone: [{ required: true, message: "联系电话不能为空", trigger: "blur" }], contactPhone: [{ required: true, message: '联系电话不能为空', trigger: 'blur' }],
companyName: [{ required: true, message: "企业名称不能为空", trigger: "blur" }], companyName: [{ required: true, message: '企业名称不能为空', trigger: 'blur' }],
username: [ username: [
{ required: true, message: "用户名不能为空", trigger: "blur" }, { required: true, message: '用户名不能为空', trigger: 'blur' },
{ min: 2, max: 20, message: '用户名称长度必须介于 2 和 20 之间', trigger: 'blur' } { min: 2, max: 20, message: '用户名称长度必须介于 2 和 20 之间', trigger: 'blur' }
], ],
password: [ password: [
{ required: true, message: "密码不能为空", trigger: "blur" }, { required: true, message: '密码不能为空', trigger: 'blur' },
{ min: 5, max: 20, message: '用户密码长度必须介于 5 和 20 之间', trigger: 'blur' } { min: 5, max: 20, message: '用户密码长度必须介于 5 和 20 之间', trigger: 'blur' }
] ]
} }
@ -215,9 +215,9 @@ const { queryParams, form, rules } = toRefs(data);
/** 查询所有租户套餐 */ /** 查询所有租户套餐 */
const getTenantPackage = async () => { const getTenantPackage = async () => {
const res = await selectTenantPackage() const res = await selectTenantPackage();
packageList.value = res.data; packageList.value = res.data;
} };
/** 查询租户列表 */ /** 查询租户列表 */
const getList = async () => { const getList = async () => {
@ -226,60 +226,58 @@ const getList = async () => {
tenantList.value = res.rows; tenantList.value = res.rows;
total.value = res.total; total.value = res.total;
loading.value = false; loading.value = false;
} };
// 租户套餐状态修改 // 租户套餐状态修改
const handleStatusChange = async (row: TenantVO) => { const handleStatusChange = async (row: TenantVO) => {
let text = row.status === "0" ? "启用" : "停用"; let text = row.status === '0' ? '启用' : '停用';
try { try {
await proxy?.$modal.confirm('确认要"' + text + '""' + row.companyName + '"租户吗?'); await proxy?.$modal.confirm('确认要"' + text + '""' + row.companyName + '"租户吗?');
await changeTenantStatus(row.id, row.tenantId, row.status); await changeTenantStatus(row.id, row.tenantId, row.status);
proxy?.$modal.msgSuccess(text + "成功"); proxy?.$modal.msgSuccess(text + '成功');
} catch { } catch {
row.status = row.status === "0" ? "1" : "0"; row.status = row.status === '0' ? '1' : '0';
} }
};
}
// 取消按钮 // 取消按钮
const cancel = () => { const cancel = () => {
reset(); reset();
dialog.visible = false; dialog.visible = false;
} };
// 表单重置 // 表单重置
const reset = () => { const reset = () => {
form.value = { ...initFormData }; form.value = { ...initFormData };
tenantFormRef.value?.resetFields(); tenantFormRef.value?.resetFields();
} };
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
queryParams.value.pageNum = 1; queryParams.value.pageNum = 1;
getList(); getList();
} };
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
queryFormRef.value?.resetFields(); queryFormRef.value?.resetFields();
handleQuery(); handleQuery();
} };
// 多选框选中数据 // 多选框选中数据
const handleSelectionChange = (selection: TenantVO[]) => { const handleSelectionChange = (selection: TenantVO[]) => {
ids.value = selection.map(item => item.id); ids.value = selection.map((item) => item.id);
single.value = selection.length != 1; single.value = selection.length != 1;
multiple.value = !selection.length; multiple.value = !selection.length;
} };
/** 新增按钮操作 */ /** 新增按钮操作 */
const handleAdd = () => { const handleAdd = () => {
reset(); reset();
getTenantPackage(); getTenantPackage();
dialog.visible = true; dialog.visible = true;
dialog.title = "添加租户"; dialog.title = '添加租户';
} };
/** 修改按钮操作 */ /** 修改按钮操作 */
const handleUpdate = async (row?: TenantVO) => { const handleUpdate = async (row?: TenantVO) => {
@ -287,10 +285,10 @@ const handleUpdate = async (row?: TenantVO) => {
await getTenantPackage(); await getTenantPackage();
const _id = row?.id || ids.value[0]; const _id = row?.id || ids.value[0];
const res = await getTenant(_id); const res = await getTenant(_id);
Object.assign(form.value, res.data) Object.assign(form.value, res.data);
dialog.visible = true; dialog.visible = true;
dialog.title = "修改租户"; dialog.title = '修改租户';
} };
/** 提交按钮 */ /** 提交按钮 */
const submitForm = () => { const submitForm = () => {
@ -298,28 +296,26 @@ const submitForm = () => {
if (valid) { if (valid) {
buttonLoading.value = true; buttonLoading.value = true;
if (form.value.id) { if (form.value.id) {
await updateTenant(form.value).finally(() => buttonLoading.value = false); await updateTenant(form.value).finally(() => (buttonLoading.value = false));
} else { } else {
await addTenant(form.value).finally(() => buttonLoading.value = false); await addTenant(form.value).finally(() => (buttonLoading.value = false));
} }
proxy?.$modal.msgSuccess("操作成功"); proxy?.$modal.msgSuccess('操作成功');
dialog.visible = false; dialog.visible = false;
await getList(); await getList();
} }
}); });
} };
/** 删除按钮操作 */ /** 删除按钮操作 */
const handleDelete = async (row?: TenantVO) => { const handleDelete = async (row?: TenantVO) => {
const _ids = row?.id || ids.value; const _ids = row?.id || ids.value;
await proxy?.$modal.confirm('是否确认删除租户编号为"' + _ids + '"的数据项?') await proxy?.$modal.confirm('是否确认删除租户编号为"' + _ids + '"的数据项?');
loading.value = true; loading.value = true;
await delTenant(_ids).finally(() => loading.value = false); await delTenant(_ids).finally(() => (loading.value = false));
await getList(); await getList();
proxy?.$modal.msgSuccess("删除成功"); proxy?.$modal.msgSuccess('删除成功');
};
}
/** 同步租户套餐按钮操作 */ /** 同步租户套餐按钮操作 */
const handleSyncTenantPackage = async (row: TenantVO) => { const handleSyncTenantPackage = async (row: TenantVO) => {
@ -328,20 +324,26 @@ const handleSyncTenantPackage = async (row: TenantVO) => {
loading.value = true; loading.value = true;
await syncTenantPackage(row.tenantId, row.packageId); await syncTenantPackage(row.tenantId, row.packageId);
await getList(); await getList();
proxy?.$modal.msgSuccess("同步成功"); proxy?.$modal.msgSuccess('同步成功');
} catch { return } finally { } catch {
return;
} finally {
loading.value = false; loading.value = false;
} }
} };
/** 导出按钮操作 */ /** 导出按钮操作 */
const handleExport = () => { const handleExport = () => {
proxy?.download('system/tenant/export', { proxy?.download(
...queryParams.value 'system/tenant/export',
}, `tenant_${new Date().getTime()}.xlsx`) {
} ...queryParams.value
},
`tenant_${new Date().getTime()}.xlsx`
);
};
onMounted(() => { onMounted(() => {
getList(); getList();
}) });
</script> </script>

View File

@ -1,9 +1,9 @@
<template> <template>
<div class="p-2"> <div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="mb-[10px]" v-show="showSearch"> <div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover"> <el-card shadow="hover">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px"> <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px">
<el-form-item label="套餐名称" prop="packageName"> <el-form-item label="套餐名称" prop="packageName">
<el-input v-model="queryParams.packageName" placeholder="请输入套餐名称" clearable style="width: 240px" @keyup.enter="handleQuery" /> <el-input v-model="queryParams.packageName" placeholder="请输入套餐名称" clearable style="width: 240px" @keyup.enter="handleQuery" />
</el-form-item> </el-form-item>
@ -20,28 +20,28 @@
<template #header> <template #header>
<el-row :gutter="10" class="mb8"> <el-row :gutter="10" class="mb8">
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:tenantPackage:add']"> 新增 </el-button> <el-button v-hasPermi="['system:tenantPackage:add']" type="primary" plain icon="Plus" @click="handleAdd"> 新增 </el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['system:tenantPackage:edit']"> <el-button v-hasPermi="['system:tenantPackage:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()">
修改 修改
</el-button> </el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:tenantPackage:remove']"> <el-button v-hasPermi="['system:tenantPackage:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">
删除 删除
</el-button> </el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:tenantPackage:export']">导出 </el-button> <el-button v-hasPermi="['system:tenantPackage:export']" type="warning" plain icon="Download" @click="handleExport">导出 </el-button>
</el-col> </el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> <right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
</el-row> </el-row>
</template> </template>
<el-table v-loading="loading" :data="tenantPackageList" @selection-change="handleSelectionChange"> <el-table v-loading="loading" :data="tenantPackageList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" /> <el-table-column type="selection" width="55" align="center" />
<el-table-column label="租户套餐id" align="center" prop="packageId" v-if="false" /> <el-table-column v-if="false" label="租户套餐id" align="center" prop="packageId" />
<el-table-column label="套餐名称" align="center" prop="packageName" /> <el-table-column label="套餐名称" align="center" prop="packageName" />
<el-table-column label="备注" align="center" prop="remark" /> <el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="状态" align="center" prop="status"> <el-table-column label="状态" align="center" prop="status">
@ -52,20 +52,20 @@
<el-table-column label="操作" align="center" class-name="small-padding fixed-width"> <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope"> <template #default="scope">
<el-tooltip content="修改" placement="top"> <el-tooltip content="修改" placement="top">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:tenantPackage:edit']"></el-button> <el-button v-hasPermi="['system:tenantPackage:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
</el-tooltip> </el-tooltip>
<el-tooltip content="删除" placement="top"> <el-tooltip content="删除" placement="top">
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:tenantPackage:remove']"></el-button> <el-button v-hasPermi="['system:tenantPackage:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
</el-tooltip> </el-tooltip>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
</el-card> </el-card>
<!-- 添加或修改租户套餐对话框 --> <!-- 添加或修改租户套餐对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body> <el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
<el-form ref="tenantPackageFormRef" :model="form" :rules="rules" label-width="80px"> <el-form ref="tenantPackageFormRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="套餐名称" prop="packageName"> <el-form-item label="套餐名称" prop="packageName">
<el-input v-model="form.packageName" placeholder="请输入套餐名称" /> <el-input v-model="form.packageName" placeholder="请输入套餐名称" />
@ -75,10 +75,10 @@
<el-checkbox v-model="menuNodeAll" @change="handleCheckedTreeNodeAll($event, 'menu')">全选/全不选 </el-checkbox> <el-checkbox v-model="menuNodeAll" @change="handleCheckedTreeNodeAll($event, 'menu')">全选/全不选 </el-checkbox>
<el-checkbox v-model="form.menuCheckStrictly" @change="handleCheckedTreeConnect($event, 'menu')">父子联动 </el-checkbox> <el-checkbox v-model="form.menuCheckStrictly" @change="handleCheckedTreeConnect($event, 'menu')">父子联动 </el-checkbox>
<el-tree <el-tree
ref="menuTreeRef"
class="tree-border" class="tree-border"
:data="menuOptions" :data="menuOptions"
show-checkbox show-checkbox
ref="menuTreeRef"
node-key="id" node-key="id"
:check-strictly="!form.menuCheckStrictly" :check-strictly="!form.menuCheckStrictly"
empty-text="加载中请稍候" empty-text="加载中请稍候"
@ -107,11 +107,11 @@ import {
addTenantPackage, addTenantPackage,
updateTenantPackage, updateTenantPackage,
changePackageStatus changePackageStatus
} from "@/api/system/tenantPackage"; } from '@/api/system/tenantPackage';
import { treeselect as menuTreeselect, tenantPackageMenuTreeselect } from "@/api/system/menu"; import { treeselect as menuTreeselect, tenantPackageMenuTreeselect } from '@/api/system/menu';
import { TenantPkgForm, TenantPkgQuery, TenantPkgVO } from "@/api/system/tenantPackage/types"; import { TenantPkgForm, TenantPkgQuery, TenantPkgVO } from '@/api/system/tenantPackage/types';
import { MenuTreeOption } from "@/api/system/menu/types"; import { MenuTreeOption } from '@/api/system/menu/types';
import to from "await-to-js"; import to from 'await-to-js';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@ -133,15 +133,14 @@ const tenantPackageFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({ const dialog = reactive<DialogOption>({
visible: false, visible: false,
title: "" title: ''
}); });
const initFormData: TenantPkgForm = { const initFormData: TenantPkgForm = {
packageId: undefined, packageId: undefined,
packageName: "", packageName: '',
menuIds: "", menuIds: '',
remark: "", remark: '',
menuCheckStrictly: true menuCheckStrictly: true
}; };
const data = reactive<PageData<TenantPkgForm, TenantPkgQuery>>({ const data = reactive<PageData<TenantPkgForm, TenantPkgQuery>>({
@ -149,11 +148,11 @@ const data = reactive<PageData<TenantPkgForm, TenantPkgQuery>>({
queryParams: { queryParams: {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
packageName: "" packageName: ''
}, },
rules: { rules: {
packageId: [{ required: true, message: "租户套餐id不能为空", trigger: "blur" }], packageId: [{ required: true, message: '租户套餐id不能为空', trigger: 'blur' }],
packageName: [{ required: true, message: "套餐名称不能为空", trigger: "blur" }] packageName: [{ required: true, message: '套餐名称不能为空', trigger: 'blur' }]
} }
}); });
@ -195,13 +194,13 @@ const getList = async () => {
// 租户套餐状态修改 // 租户套餐状态修改
const handleStatusChange = async (row: TenantPkgVO) => { const handleStatusChange = async (row: TenantPkgVO) => {
let text = row.status === "0" ? "启用" : "停用"; let text = row.status === '0' ? '启用' : '停用';
const [err] = await to(proxy?.$modal.confirm("确认要\"" + text + "\"\"" + row.packageName + "\"套餐吗?") as Promise<any>); const [err] = await to(proxy?.$modal.confirm('确认要"' + text + '""' + row.packageName + '"套餐吗?') as Promise<any>);
if (err) { if (err) {
row.status = row.status === "0" ? "1" : "0"; row.status = row.status === '0' ? '1' : '0';
} else { } else {
await changePackageStatus(row.packageId, row.status); await changePackageStatus(row.packageId, row.status);
proxy?.$modal.msgSuccess(text + "成功"); proxy?.$modal.msgSuccess(text + '成功');
} }
}; };
@ -234,14 +233,14 @@ const resetQuery = () => {
// 多选框选中数据 // 多选框选中数据
const handleSelectionChange = (selection: TenantPkgVO[]) => { const handleSelectionChange = (selection: TenantPkgVO[]) => {
ids.value = selection.map(item => item.packageId); ids.value = selection.map((item) => item.packageId);
single.value = selection.length != 1; single.value = selection.length != 1;
multiple.value = !selection.length; multiple.value = !selection.length;
}; };
// 树权限(展开/折叠) // 树权限(展开/折叠)
const handleCheckedTreeExpand = (value: CheckboxValueType, type: string) => { const handleCheckedTreeExpand = (value: CheckboxValueType, type: string) => {
if (type == "menu") { if (type == 'menu') {
let treeList = menuOptions.value; let treeList = menuOptions.value;
for (let i = 0; i < treeList.length; i++) { for (let i = 0; i < treeList.length; i++) {
if (menuTreeRef.value) { if (menuTreeRef.value) {
@ -253,14 +252,14 @@ const handleCheckedTreeExpand = (value: CheckboxValueType, type: string) => {
// 树权限(全选/全不选) // 树权限(全选/全不选)
const handleCheckedTreeNodeAll = (value: CheckboxValueType, type: string) => { const handleCheckedTreeNodeAll = (value: CheckboxValueType, type: string) => {
if (type == "menu") { if (type == 'menu') {
menuTreeRef.value?.setCheckedNodes(value ? menuOptions.value as any : []); menuTreeRef.value?.setCheckedNodes(value ? (menuOptions.value as any) : []);
} }
}; };
// 树权限(父子联动) // 树权限(父子联动)
const handleCheckedTreeConnect = (value: CheckboxValueType, type: string) => { const handleCheckedTreeConnect = (value: CheckboxValueType, type: string) => {
if (type == "menu") { if (type == 'menu') {
form.value.menuCheckStrictly = value as boolean; form.value.menuCheckStrictly = value as boolean;
} }
}; };
@ -270,7 +269,7 @@ const handleAdd = () => {
reset(); reset();
getMenuTreeselect(); getMenuTreeselect();
dialog.visible = true; dialog.visible = true;
dialog.title = "添加租户套餐"; dialog.title = '添加租户套餐';
}; };
/** 修改按钮操作 */ /** 修改按钮操作 */
@ -281,7 +280,7 @@ const handleUpdate = async (row?: TenantPkgVO) => {
form.value = response.data; form.value = response.data;
const res = await getPackageMenuTreeselect(_packageId); const res = await getPackageMenuTreeselect(_packageId);
dialog.visible = true; dialog.visible = true;
dialog.title = "修改租户套餐"; dialog.title = '修改租户套餐';
res.data.checkedKeys.forEach((v) => { res.data.checkedKeys.forEach((v) => {
nextTick(() => { nextTick(() => {
menuTreeRef.value?.setChecked(v, true, false); menuTreeRef.value?.setChecked(v, true, false);
@ -296,11 +295,11 @@ const submitForm = () => {
buttonLoading.value = true; buttonLoading.value = true;
form.value.menuIds = getMenuAllCheckedKeys(); form.value.menuIds = getMenuAllCheckedKeys();
if (form.value.packageId != null) { if (form.value.packageId != null) {
await updateTenantPackage(form.value).finally(() => buttonLoading.value = false); await updateTenantPackage(form.value).finally(() => (buttonLoading.value = false));
} else { } else {
await addTenantPackage(form.value).finally(() => buttonLoading.value = false); await addTenantPackage(form.value).finally(() => (buttonLoading.value = false));
} }
proxy?.$modal.msgSuccess("操作成功"); proxy?.$modal.msgSuccess('操作成功');
dialog.visible = false; dialog.visible = false;
await getList(); await getList();
} }
@ -310,20 +309,24 @@ const submitForm = () => {
/** 删除按钮操作 */ /** 删除按钮操作 */
const handleDelete = async (row?: TenantPkgVO) => { const handleDelete = async (row?: TenantPkgVO) => {
const _packageIds = row?.packageId || ids.value; const _packageIds = row?.packageId || ids.value;
await proxy?.$modal.confirm("是否确认删除租户套餐编号为\"" + _packageIds + "\"的数据项?").finally(() => { await proxy?.$modal.confirm('是否确认删除租户套餐编号为"' + _packageIds + '"的数据项?').finally(() => {
loading.value = false; loading.value = false;
}); });
await delTenantPackage(_packageIds); await delTenantPackage(_packageIds);
loading.value = true; loading.value = true;
await getList(); await getList();
proxy?.$modal.msgSuccess("删除成功"); proxy?.$modal.msgSuccess('删除成功');
}; };
/** 导出按钮操作 */ /** 导出按钮操作 */
const handleExport = () => { const handleExport = () => {
proxy?.download("system/tenantPackage/export", { proxy?.download(
...queryParams.value 'system/tenantPackage/export',
}, `tenantPackage_${new Date().getTime()}.xlsx`); {
...queryParams.value
},
`tenantPackage_${new Date().getTime()}.xlsx`
);
}; };
onMounted(() => { onMounted(() => {

View File

@ -21,12 +21,12 @@
<h4 class="panel-title">角色信息</h4> <h4 class="panel-title">角色信息</h4>
<div> <div>
<el-table <el-table
ref="tableRef"
v-loading="loading" v-loading="loading"
:row-key="getRowKey" :row-key="getRowKey"
@row-click="clickRow"
ref="tableRef"
@selection-change="handleSelectionChange"
:data="roles.slice((pageNum - 1) * pageSize, pageNum * pageSize)" :data="roles.slice((pageNum - 1) * pageSize, pageNum * pageSize)"
@row-click="clickRow"
@selection-change="handleSelectionChange"
> >
<el-table-column label="序号" width="55" type="index" align="center"> <el-table-column label="序号" width="55" type="index" align="center">
<template #default="scope"> <template #default="scope">
@ -43,8 +43,8 @@
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<pagination v-show="total > 0" :total="total" v-model:page="pageNum" v-model:limit="pageSize" /> <pagination v-show="total > 0" v-model:page="pageNum" v-model:limit="pageSize" :total="total" />
<div style="text-align: center;margin-left:-120px;margin-top:30px;"> <div style="text-align: center; margin-left: -120px; margin-top: 30px">
<el-button type="primary" @click="submitForm()">提交</el-button> <el-button type="primary" @click="submitForm()">提交</el-button>
<el-button @click="close()">返回</el-button> <el-button @click="close()">返回</el-button>
</div> </div>
@ -55,9 +55,9 @@
</template> </template>
<script setup name="AuthRole" lang="ts"> <script setup name="AuthRole" lang="ts">
import { RoleVO } from "@/api/system/role/types"; import { RoleVO } from '@/api/system/role/types';
import { getAuthRole, updateAuthRole } from "@/api/system/user"; import { getAuthRole, updateAuthRole } from '@/api/system/user';
import { UserForm } from "@/api/system/user/types"; import { UserForm } from '@/api/system/user/types';
const route = useRoute(); const route = useRoute();
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@ -70,7 +70,7 @@ const roleIds = ref<Array<string | number>>([]);
const roles = ref<RoleVO[]>([]); const roles = ref<RoleVO[]>([]);
const form = ref<Partial<UserForm>>({ const form = ref<Partial<UserForm>>({
nickName: undefined, nickName: undefined,
userName: "", userName: '',
userId: undefined userId: undefined
}); });
@ -83,7 +83,7 @@ const clickRow = (row: RoleVO) => {
}; };
/** 多选框选中数据 */ /** 多选框选中数据 */
const handleSelectionChange = (selection: RoleVO[]) => { const handleSelectionChange = (selection: RoleVO[]) => {
roleIds.value = selection.map(item => item.roleId); roleIds.value = selection.map((item) => item.roleId);
}; };
/** 保存选中的数据编号 */ /** 保存选中的数据编号 */
const getRowKey = (row: RoleVO): string => { const getRowKey = (row: RoleVO): string => {
@ -91,15 +91,15 @@ const getRowKey = (row: RoleVO): string => {
}; };
/** 关闭按钮 */ /** 关闭按钮 */
const close = () => { const close = () => {
const obj = { path: "/system/user" }; const obj = { path: '/system/user' };
proxy?.$tab.closeOpenPage(obj); proxy?.$tab.closeOpenPage(obj);
}; };
/** 提交按钮 */ /** 提交按钮 */
const submitForm = async () => { const submitForm = async () => {
const userId = form.value.userId; const userId = form.value.userId;
const rIds = roleIds.value.join(","); const rIds = roleIds.value.join(',');
await updateAuthRole({ userId: userId as string, roleIds: rIds }); await updateAuthRole({ userId: userId as string, roleIds: rIds });
proxy?.$modal.msgSuccess("授权成功"); proxy?.$modal.msgSuccess('授权成功');
close(); close();
}; };
@ -112,7 +112,7 @@ const getList = async () => {
Object.assign(roles.value, res.data.roles); Object.assign(roles.value, res.data.roles);
total.value = roles.value.length; total.value = roles.value.length;
await nextTick(() => { await nextTick(() => {
roles.value.forEach(row => { roles.value.forEach((row) => {
if (row?.flag) { if (row?.flag) {
tableRef.value?.toggleRowSelection(row, true); tableRef.value?.toggleRowSelection(row, true);
} }

View File

@ -6,8 +6,8 @@
<el-card shadow="hover"> <el-card shadow="hover">
<el-input v-model="deptName" placeholder="请输入部门名称" prefix-icon="Search" clearable /> <el-input v-model="deptName" placeholder="请输入部门名称" prefix-icon="Search" clearable />
<el-tree <el-tree
class="mt-2"
ref="deptTreeRef" ref="deptTreeRef"
class="mt-2"
node-key="id" node-key="id"
:data="deptOptions" :data="deptOptions"
:props="{ label: 'label', children: 'children' }" :props="{ label: 'label', children: 'children' }"
@ -21,7 +21,7 @@
</el-col> </el-col>
<el-col :lg="20" :xs="24"> <el-col :lg="20" :xs="24">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="mb-[10px]" v-show="showSearch"> <div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover"> <el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px"> <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px">
<el-form-item label="用户名称" prop="userName"> <el-form-item label="用户名称" prop="userName">
@ -42,7 +42,7 @@
<el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" /> <el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="创建时间" style="width: 308px;"> <el-form-item label="创建时间" style="width: 308px">
<el-date-picker <el-date-picker
v-model="dateRange" v-model="dateRange"
value-format="YYYY-MM-DD" value-format="YYYY-MM-DD"
@ -53,8 +53,8 @@
></el-date-picker> ></el-date-picker>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" @click="handleQuery" icon="Search">搜索</el-button> <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button @click="resetQuery" icon="Refresh">重置</el-button> <el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
</el-card> </el-card>
@ -65,15 +65,15 @@
<template #header> <template #header>
<el-row :gutter="10"> <el-row :gutter="10">
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="primary" plain @click="handleAdd()" v-has-permi="['system:user:add']" icon="Plus">新增</el-button> <el-button v-has-permi="['system:user:add']" type="primary" plain icon="Plus" @click="handleAdd()">新增</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="success" plain @click="handleUpdate()" :disabled="single" v-has-permi="['system:user:add']" icon="Edit"> <el-button v-has-permi="['system:user:add']" type="success" plain :disabled="single" icon="Edit" @click="handleUpdate()">
修改 修改
</el-button> </el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="danger" plain @click="handleDelete()" :disabled="multiple" v-has-permi="['system:user:delete']" icon="Delete"> <el-button v-has-permi="['system:user:delete']" type="danger" plain :disabled="multiple" icon="Delete" @click="handleDelete()">
删除 删除
</el-button> </el-button>
</el-col> </el-col>
@ -85,38 +85,38 @@
></el-button> ></el-button>
<template #dropdown> <template #dropdown>
<el-dropdown-menu> <el-dropdown-menu>
<el-dropdown-item @click="importTemplate" icon="Download">下载模板</el-dropdown-item> <el-dropdown-item icon="Download" @click="importTemplate">下载模板</el-dropdown-item>
<el-dropdown-item @click="handleImport" icon="Top"> 导入数据</el-dropdown-item> <el-dropdown-item icon="Top" @click="handleImport"> 导入数据</el-dropdown-item>
<el-dropdown-item @click="handleExport" icon="Download"> 导出数据</el-dropdown-item> <el-dropdown-item icon="Download" @click="handleExport"> 导出数据</el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</template> </template>
</el-dropdown> </el-dropdown>
</el-col> </el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" :columns="columns" :search="true"></right-toolbar> <right-toolbar v-model:showSearch="showSearch" :columns="columns" :search="true" @query-table="getList"></right-toolbar>
</el-row> </el-row>
</template> </template>
<el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange"> <el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="50" align="center" /> <el-table-column type="selection" width="50" align="center" />
<el-table-column label="用户编号" align="center" key="userId" prop="userId" v-if="columns[0].visible" /> <el-table-column v-if="columns[0].visible" key="userId" label="用户编号" align="center" prop="userId" />
<el-table-column label="用户名称" align="center" key="userName" prop="userName" v-if="columns[1].visible" :show-overflow-tooltip="true" /> <el-table-column v-if="columns[1].visible" key="userName" label="用户名称" align="center" prop="userName" :show-overflow-tooltip="true" />
<el-table-column label="用户昵称" align="center" key="nickName" prop="nickName" v-if="columns[2].visible" :show-overflow-tooltip="true" /> <el-table-column v-if="columns[2].visible" key="nickName" label="用户昵称" align="center" prop="nickName" :show-overflow-tooltip="true" />
<el-table-column <el-table-column
v-if="columns[3].visible"
key="deptName"
label="部门" label="部门"
align="center" align="center"
key="deptName"
prop="dept.deptName" prop="dept.deptName"
v-if="columns[3].visible"
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
/> />
<el-table-column label="手机号码" align="center" key="phonenumber" prop="phonenumber" v-if="columns[4].visible" width="120" /> <el-table-column v-if="columns[4].visible" key="phonenumber" label="手机号码" align="center" prop="phonenumber" width="120" />
<el-table-column label="状态" align="center" key="status" v-if="columns[5].visible"> <el-table-column v-if="columns[5].visible" key="status" label="状态" align="center">
<template #default="scope"> <template #default="scope">
<el-switch v-model="scope.row.status" active-value="0" inactive-value="1" @change="handleStatusChange(scope.row)"></el-switch> <el-switch v-model="scope.row.status" active-value="0" inactive-value="1" @change="handleStatusChange(scope.row)"></el-switch>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" v-if="columns[6].visible" width="160"> <el-table-column v-if="columns[6].visible" label="创建时间" align="center" prop="createTime" width="160">
<template #default="scope"> <template #default="scope">
<span>{{ scope.row.createTime }}</span> <span>{{ scope.row.createTime }}</span>
</template> </template>
@ -124,19 +124,19 @@
<el-table-column label="操作" fixed="right" width="180" class-name="small-padding fixed-width"> <el-table-column label="操作" fixed="right" width="180" class-name="small-padding fixed-width">
<template #default="scope"> <template #default="scope">
<el-tooltip content="修改" placement="top" v-if="scope.row.userId !== 1"> <el-tooltip v-if="scope.row.userId !== 1" content="修改" placement="top">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:user:edit']"></el-button> <el-button v-hasPermi="['system:user:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
</el-tooltip> </el-tooltip>
<el-tooltip content="删除" placement="top" v-if="scope.row.userId !== 1"> <el-tooltip v-if="scope.row.userId !== 1" content="删除" placement="top">
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:user:remove']"></el-button> <el-button v-hasPermi="['system:user:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
</el-tooltip> </el-tooltip>
<el-tooltip content="重置密码" placement="top" v-if="scope.row.userId !== 1"> <el-tooltip v-if="scope.row.userId !== 1" content="重置密码" placement="top">
<el-button link type="primary" icon="Key" @click="handleResetPwd(scope.row)" v-hasPermi="['system:user:resetPwd']"></el-button> <el-button v-hasPermi="['system:user:resetPwd']" link type="primary" icon="Key" @click="handleResetPwd(scope.row)"></el-button>
</el-tooltip> </el-tooltip>
<el-tooltip content="分配角色" placement="top" v-if="scope.row.userId !== 1"> <el-tooltip v-if="scope.row.userId !== 1" content="分配角色" placement="top">
<el-button link type="primary" icon="CircleCheck" @click="handleAuthRole(scope.row)" v-hasPermi="['system:user:edit']"></el-button> <el-button v-hasPermi="['system:user:edit']" link type="primary" icon="CircleCheck" @click="handleAuthRole(scope.row)"></el-button>
</el-tooltip> </el-tooltip>
</template> </template>
</el-table-column> </el-table-column>
@ -144,9 +144,9 @@
<pagination <pagination
v-show="total > 0" v-show="total > 0"
:total="total"
v-model:page="queryParams.pageNum" v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize" v-model:limit="queryParams.pageSize"
:total="total"
@pagination="getList" @pagination="getList"
/> />
</el-card> </el-card>
@ -154,8 +154,8 @@
</el-row> </el-row>
<!-- 添加或修改用户配置对话框 --> <!-- 添加或修改用户配置对话框 -->
<el-dialog ref="formDialogRef" :title="dialog.title" v-model="dialog.visible" width="600px" append-to-body @close="closeDialog"> <el-dialog ref="formDialogRef" v-model="dialog.visible" :title="dialog.title" width="600px" append-to-body @close="closeDialog">
<el-form :model="form" :rules="rules" ref="userFormRef" label-width="80px"> <el-form ref="userFormRef" :model="form" :rules="rules" label-width="80px">
<el-row> <el-row>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="用户昵称" prop="nickName"> <el-form-item label="用户昵称" prop="nickName">
@ -210,8 +210,7 @@
<el-col :span="12"> <el-col :span="12">
<el-form-item label="状态"> <el-form-item label="状态">
<el-radio-group v-model="form.status"> <el-radio-group v-model="form.status">
<el-radio v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.value">{{ <el-radio v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.value">{{ dict.label }}</el-radio>
dict.label }}</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
</el-col> </el-col>
@ -261,7 +260,7 @@
</el-dialog> </el-dialog>
<!-- 用户导入对话框 --> <!-- 用户导入对话框 -->
<el-dialog :title="upload.title" v-model="upload.open" width="400px" append-to-body> <el-dialog v-model="upload.open" :title="upload.title" width="400px" append-to-body>
<el-upload <el-upload
ref="uploadRef" ref="uploadRef"
:limit="1" :limit="1"
@ -282,7 +281,7 @@
<div class="text-center el-upload__tip"> <div class="text-center el-upload__tip">
<div class="el-upload__tip"><el-checkbox v-model="upload.updateSupport" />是否更新已经存在的用户数据</div> <div class="el-upload__tip"><el-checkbox v-model="upload.updateSupport" />是否更新已经存在的用户数据</div>
<span>仅允许导入xlsxlsx格式文件</span> <span>仅允许导入xlsxlsx格式文件</span>
<el-link type="primary" :underline="false" style="font-size:12px;vertical-align: baseline;" @click="importTemplate">下载模板</el-link> <el-link type="primary" :underline="false" style="font-size: 12px; vertical-align: baseline" @click="importTemplate">下载模板</el-link>
</div> </div>
</template> </template>
</el-upload> </el-upload>
@ -297,22 +296,22 @@
</template> </template>
<script setup name="User" lang="ts"> <script setup name="User" lang="ts">
import api from "@/api/system/user" import api from '@/api/system/user';
import { UserForm, UserQuery, UserVO } from '@/api/system/user/types'; import { UserForm, UserQuery, UserVO } from '@/api/system/user/types';
import { treeselect } from "@/api/system/dept"; import { treeselect } from '@/api/system/dept';
import { DeptVO } from "@/api/system/dept/types"; import { DeptVO } from '@/api/system/dept/types';
import { RoleVO } from "@/api/system/role/types"; import { RoleVO } from '@/api/system/role/types';
import { PostVO } from "@/api/system/post/types"; import { PostVO } from '@/api/system/post/types';
import { to } from "await-to-js"; import { to } from 'await-to-js';
import { globalHeaders } from "@/utils/request"; import { globalHeaders } from '@/utils/request';
const router = useRouter(); const router = useRouter();
const { proxy } = getCurrentInstance() as ComponentInternalInstance const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { sys_normal_disable, sys_user_sex } = toRefs<any>(proxy?.useDict('sys_normal_disable', 'sys_user_sex')); const { sys_normal_disable, sys_user_sex } = toRefs<any>(proxy?.useDict('sys_normal_disable', 'sys_user_sex'));
const userList = ref<UserVO[]>(); const userList = ref<UserVO[]>();
const loading = ref(true); const loading = ref(true);
const showSearch = ref(true) const showSearch = ref(true);
const ids = ref<Array<number | string>>([]); const ids = ref<Array<number | string>>([]);
const single = ref(true); const single = ref(true);
const multiple = ref(true); const multiple = ref(true);
@ -320,7 +319,7 @@ const total = ref(0);
const dateRange = ref<[DateModelType, DateModelType]>(['', '']); const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
const deptName = ref(''); const deptName = ref('');
const deptOptions = ref<DeptVO[]>([]); const deptOptions = ref<DeptVO[]>([]);
const initPassword = ref<String>(''); const initPassword = ref<string>('');
const postOptions = ref<PostVO[]>([]); const postOptions = ref<PostVO[]>([]);
const roleOptions = ref<RoleVO[]>([]); const roleOptions = ref<RoleVO[]>([]);
/*** 用户导入参数 */ /*** 用户导入参数 */
@ -328,7 +327,7 @@ const upload = reactive<ImportOption>({
// 是否显示弹出层(用户导入) // 是否显示弹出层(用户导入)
open: false, open: false,
// 弹出层标题(用户导入) // 弹出层标题(用户导入)
title: "", title: '',
// 是否禁用上传 // 是否禁用上传
isUploading: false, isUploading: false,
// 是否更新已经存在的用户数据 // 是否更新已经存在的用户数据
@ -336,19 +335,18 @@ const upload = reactive<ImportOption>({
// 设置上传的请求头部 // 设置上传的请求头部
headers: globalHeaders(), headers: globalHeaders(),
// 上传的地址 // 上传的地址
url: import.meta.env.VITE_APP_BASE_API + "/system/user/importData" url: import.meta.env.VITE_APP_BASE_API + '/system/user/importData'
}) });
// 列显隐信息 // 列显隐信息
const columns = ref<FieldOption[]>([ const columns = ref<FieldOption[]>([
{ key: 0, label: `用户编号`, visible: false,children: [] }, { key: 0, label: `用户编号`, visible: false, children: [] },
{ key: 1, label: `用户名称`, visible: true,children: [] }, { key: 1, label: `用户名称`, visible: true, children: [] },
{ key: 2, label: `用户昵称`, visible: true,children: [] }, { key: 2, label: `用户昵称`, visible: true, children: [] },
{ key: 3, label: `部门`, visible: true,children: [] }, { key: 3, label: `部门`, visible: true, children: [] },
{ key: 4, label: `手机号码`, visible: true,children: [] }, { key: 4, label: `手机号码`, visible: true, children: [] },
{ key: 5, label: `状态`, visible: true,children: [] }, { key: 5, label: `状态`, visible: true, children: [] },
{ key: 6, label: `创建时间`, visible: true,children: [] } { key: 6, label: `创建时间`, visible: true, children: [] }
]) ]);
const deptTreeRef = ref<ElTreeInstance>(); const deptTreeRef = ref<ElTreeInstance>();
const queryFormRef = ref<ElFormInstance>(); const queryFormRef = ref<ElFormInstance>();
@ -370,11 +368,11 @@ const initFormData: UserForm = {
phonenumber: undefined, phonenumber: undefined,
email: undefined, email: undefined,
sex: undefined, sex: undefined,
status: "0", status: '0',
remark: '', remark: '',
postIds: [], postIds: [],
roleIds: [] roleIds: []
} };
const data = reactive<PageData<UserForm, UserQuery>>({ const data = reactive<PageData<UserForm, UserQuery>>({
form: { ...initFormData }, form: { ...initFormData },
queryParams: { queryParams: {
@ -386,24 +384,54 @@ const data = reactive<PageData<UserForm, UserQuery>>({
deptId: '' deptId: ''
}, },
rules: { rules: {
userName: [{ required: true, message: "用户名称不能为空", trigger: "blur" }, { min: 2, max: 20, message: "用户名称长度必须介于 2 和 20 之间", trigger: "blur" }], userName: [
nickName: [{ required: true, message: "用户称不能为空", trigger: "blur" }], { required: true, message: '用户称不能为空', trigger: 'blur' },
password: [{ required: true, message: "用户密码不能为空", trigger: "blur" }, { min: 5, max: 20, message: "用户密码长度必须介于 5 和 20 之间", trigger: "blur" }], {
email: [{ type: "email", message: "请输入正确的邮箱地址", trigger: ["blur", "change"] }], min: 2,
phonenumber: [{ pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: "请输入正确的手机号码", trigger: "blur" }] max: 20,
message: '用户名称长度必须介于 2 和 20 之间',
trigger: 'blur'
}
],
nickName: [{ required: true, message: '用户昵称不能为空', trigger: 'blur' }],
password: [
{ required: true, message: '用户密码不能为空', trigger: 'blur' },
{
min: 5,
max: 20,
message: '用户密码长度必须介于 5 和 20 之间',
trigger: 'blur'
}
],
email: [
{
type: 'email',
message: '请输入正确的邮箱地址',
trigger: ['blur', 'change']
}
],
phonenumber: [
{
pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/,
message: '请输入正确的手机号码',
trigger: 'blur'
}
]
} }
}) });
const { queryParams, form, rules } = toRefs<PageData<UserForm, UserQuery>>(data) const { queryParams, form, rules } = toRefs<PageData<UserForm, UserQuery>>(data);
/** 通过条件过滤节点 */ /** 通过条件过滤节点 */
const filterNode = (value: string, data: any) => { const filterNode = (value: string, data: any) => {
if (!value) return true if (!value) return true;
return data.label.indexOf(value) !== -1 return data.label.indexOf(value) !== -1;
} };
/** 根据名称筛选部门树 */ /** 根据名称筛选部门树 */
watchEffect( watchEffect(
() => { deptTreeRef.value?.filter(deptName.value); }, () => {
deptTreeRef.value?.filter(deptName.value);
},
{ {
flush: 'post' // watchEffect会在DOM挂载或者更新之前就会触发此属性控制在DOM元素更新后运行 flush: 'post' // watchEffect会在DOM挂载或者更新之前就会触发此属性控制在DOM元素更新后运行
} }
@ -422,29 +450,28 @@ const getList = async () => {
loading.value = false; loading.value = false;
userList.value = res.rows; userList.value = res.rows;
total.value = res.total; total.value = res.total;
} };
/** 节点单击事件 */ /** 节点单击事件 */
const handleNodeClick = (data: DeptVO) => { const handleNodeClick = (data: DeptVO) => {
queryParams.value.deptId = data.id; queryParams.value.deptId = data.id;
handleQuery() handleQuery();
} };
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
queryParams.value.pageNum = 1 queryParams.value.pageNum = 1;
getList() getList();
} };
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
dateRange.value = ['', ''] dateRange.value = ['', ''];
queryFormRef.value?.resetFields(); queryFormRef.value?.resetFields();
queryParams.value.pageNum = 1; queryParams.value.pageNum = 1;
queryParams.value.deptId = undefined; queryParams.value.deptId = undefined;
deptTreeRef.value?.setCurrentKey(undefined); deptTreeRef.value?.setCurrentKey(undefined);
handleQuery(); handleQuery();
} };
/** 删除按钮操作 */ /** 删除按钮操作 */
const handleDelete = async (row?: UserVO) => { const handleDelete = async (row?: UserVO) => {
@ -453,78 +480,85 @@ const handleDelete = async (row?: UserVO) => {
if (!err) { if (!err) {
await api.delUser(userIds); await api.delUser(userIds);
await getList(); await getList();
proxy?.$modal.msgSuccess("删除成功"); proxy?.$modal.msgSuccess('删除成功');
} }
} };
/** 用户状态修改 */ /** 用户状态修改 */
const handleStatusChange = async (row: UserVO) => { const handleStatusChange = async (row: UserVO) => {
let text = row.status === "0" ? "启用" : "停用" let text = row.status === '0' ? '启用' : '停用';
try { try {
await proxy?.$modal.confirm('确认要"' + text + '""' + row.userName + '"用户吗?'); await proxy?.$modal.confirm('确认要"' + text + '""' + row.userName + '"用户吗?');
await api.changeUserStatus(row.userId, row.status); await api.changeUserStatus(row.userId, row.status);
proxy?.$modal.msgSuccess(text + "成功"); proxy?.$modal.msgSuccess(text + '成功');
} catch (err) { } catch (err) {
row.status = row.status === "0" ? "1" : "0"; row.status = row.status === '0' ? '1' : '0';
} }
} };
/** 跳转角色分配 */ /** 跳转角色分配 */
const handleAuthRole = (row: UserVO) => { const handleAuthRole = (row: UserVO) => {
const userId = row.userId; const userId = row.userId;
router.push("/system/user-auth/role/" + userId); router.push('/system/user-auth/role/' + userId);
} };
/** 重置密码按钮操作 */ /** 重置密码按钮操作 */
const handleResetPwd = async (row: UserVO) => { const handleResetPwd = async (row: UserVO) => {
const [err, res] = await to(ElMessageBox.prompt('请输入"' + row.userName + '"的新密码', "提示", { const [err, res] = await to(
confirmButtonText: "确定", ElMessageBox.prompt('请输入"' + row.userName + '"的新密码', '提示', {
cancelButtonText: "取消", confirmButtonText: '确定',
closeOnClickModal: false, cancelButtonText: '取消',
inputPattern: /^.{5,20}$/, closeOnClickModal: false,
inputErrorMessage: "用户密码长度必须介于 5 和 20 之间", inputPattern: /^.{5,20}$/,
})) inputErrorMessage: '用户密码长度必须介于 5 和 20 之间'
})
);
if (!err) { if (!err) {
await api.resetUserPwd(row.userId, res.value); await api.resetUserPwd(row.userId, res.value);
proxy?.$modal.msgSuccess("修改成功,新密码是:" + res.value); proxy?.$modal.msgSuccess('修改成功,新密码是:' + res.value);
} }
} };
/** 选择条数 */ /** 选择条数 */
const handleSelectionChange = (selection: UserVO[]) => { const handleSelectionChange = (selection: UserVO[]) => {
ids.value = selection.map((item) => item.userId); ids.value = selection.map((item) => item.userId);
single.value = selection.length != 1; single.value = selection.length != 1;
multiple.value = !selection.length; multiple.value = !selection.length;
} };
/** 导入按钮操作 */ /** 导入按钮操作 */
const handleImport = () => { const handleImport = () => {
upload.title = "用户导入"; upload.title = '用户导入';
upload.open = true; upload.open = true;
} };
/** 导出按钮操作 */ /** 导出按钮操作 */
const handleExport = () => { const handleExport = () => {
proxy?.download("system/user/export", { proxy?.download(
...queryParams.value, 'system/user/export',
}, `user_${new Date().getTime()}.xlsx`); {
...queryParams.value
},
`user_${new Date().getTime()}.xlsx`
);
}; };
/** 下载模板操作 */ /** 下载模板操作 */
const importTemplate = () => { const importTemplate = () => {
proxy?.download("system/user/importTemplate", { proxy?.download('system/user/importTemplate', {}, `user_template_${new Date().getTime()}.xlsx`);
}, `user_template_${new Date().getTime()}.xlsx`); };
}
/**文件上传中处理 */ /**文件上传中处理 */
const handleFileUploadProgress = () => { const handleFileUploadProgress = () => {
upload.isUploading = true; upload.isUploading = true;
} };
/** 文件上传成功处理 */ /** 文件上传成功处理 */
const handleFileSuccess = (response: any, file: UploadFile) => { const handleFileSuccess = (response: any, file: UploadFile) => {
upload.open = false; upload.open = false;
upload.isUploading = false; upload.isUploading = false;
uploadRef.value?.handleRemove(file); uploadRef.value?.handleRemove(file);
ElMessageBox.alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.msg + "</div>", "导入结果", { dangerouslyUseHTMLString: true }); ElMessageBox.alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.msg + '</div>', '导入结果', {
dangerouslyUseHTMLString: true
});
getList(); getList();
} };
/** 提交上传文件 */ /** 提交上传文件 */
function submitFileForm() { function submitFileForm() {
@ -538,59 +572,57 @@ const initTreeData = async () => {
const { data } = await treeselect(); const { data } = await treeselect();
deptOptions.value = data; deptOptions.value = data;
} }
} };
/** 重置操作表单 */ /** 重置操作表单 */
const reset = () => { const reset = () => {
form.value = { ...initFormData }; form.value = { ...initFormData };
userFormRef.value?.resetFields(); userFormRef.value?.resetFields();
} };
/** 取消按钮 */ /** 取消按钮 */
const cancel = () => { const cancel = () => {
dialog.visible = false; dialog.visible = false;
reset(); reset();
} };
/** 新增按钮操作 */ /** 新增按钮操作 */
const handleAdd = async () => { const handleAdd = async () => {
reset(); reset();
const { data } = await api.getUser(); const { data } = await api.getUser();
dialog.visible = true; dialog.visible = true;
dialog.title = "新增用户"; dialog.title = '新增用户';
await initTreeData(); await initTreeData();
postOptions.value = data.posts; postOptions.value = data.posts;
roleOptions.value = data.roles; roleOptions.value = data.roles;
form.value.password = initPassword.value.toString(); form.value.password = initPassword.value.toString();
} };
/** 修改按钮操作 */ /** 修改按钮操作 */
const handleUpdate = async (row?: UserForm) => { const handleUpdate = async (row?: UserForm) => {
reset(); reset();
const userId = row?.userId || ids.value[0] const userId = row?.userId || ids.value[0];
const { data } = await api.getUser(userId) const { data } = await api.getUser(userId);
dialog.visible = true; dialog.visible = true;
dialog.title = "修改用户"; dialog.title = '修改用户';
await initTreeData(); await initTreeData();
Object.assign(form.value, data.user); Object.assign(form.value, data.user);
postOptions.value = data.posts; postOptions.value = data.posts;
roleOptions.value = data.roles; roleOptions.value = data.roles;
form.value.postIds = data.postIds; form.value.postIds = data.postIds;
form.value.roleIds = data.roleIds; form.value.roleIds = data.roleIds;
form.value.password = ""; form.value.password = '';
} };
/** 提交按钮 */ /** 提交按钮 */
const submitForm = () => { const submitForm = () => {
userFormRef.value?.validate(async (valid: boolean) => { userFormRef.value?.validate(async (valid: boolean) => {
if (valid) { if (valid) {
form.value.userId ? await api.updateUser(form.value) : await api.addUser(form.value); form.value.userId ? await api.updateUser(form.value) : await api.addUser(form.value);
proxy?.$modal.msgSuccess("操作成功"); proxy?.$modal.msgSuccess('操作成功');
dialog.visible = false; dialog.visible = false;
await getList(); await getList();
} }
}) });
} };
/** /**
* 关闭用户弹窗 * 关闭用户弹窗
@ -598,7 +630,7 @@ const submitForm = () => {
const closeDialog = () => { const closeDialog = () => {
dialog.visible = false; dialog.visible = false;
resetForm(); resetForm();
} };
/** /**
* 重置表单 * 重置表单
@ -609,11 +641,11 @@ const resetForm = () => {
form.value.id = undefined; form.value.id = undefined;
form.value.status = '1'; form.value.status = '1';
} };
onMounted(() => { onMounted(() => {
getTreeSelect() // 初始化部门数据 getTreeSelect(); // 初始化部门数据
getList() // 初始化列表数据 getList(); // 初始化列表数据
proxy?.getConfigKey("sys.user.initPassword").then(response => { proxy?.getConfigKey('sys.user.initPassword').then((response) => {
initPassword.value = response.data; initPassword.value = response.data;
}); });
}); });

View File

@ -3,14 +3,14 @@
<el-row :gutter="20"> <el-row :gutter="20">
<el-col :span="6" :xs="24"> <el-col :span="6" :xs="24">
<el-card class="box-card"> <el-card class="box-card">
<template v-slot:header> <template #header>
<div class="clearfix"> <div class="clearfix">
<span>个人信息</span> <span>个人信息</span>
</div> </div>
</template> </template>
<div> <div>
<div class="text-center"> <div class="text-center">
<userAvatar/> <userAvatar />
</div> </div>
<ul class="list-group list-group-striped"> <ul class="list-group list-group-striped">
<li class="list-group-item"> <li class="list-group-item">
@ -27,7 +27,7 @@
</li> </li>
<li class="list-group-item"> <li class="list-group-item">
<svg-icon icon-class="tree" />所属部门 <svg-icon icon-class="tree" />所属部门
<div class="pull-right" v-if="state.user.dept">{{ state.user.dept.deptName }} / {{ state.postGroup }}</div> <div v-if="state.user.dept" class="pull-right">{{ state.user.dept.deptName }} / {{ state.postGroup }}</div>
</li> </li>
<li class="list-group-item"> <li class="list-group-item">
<svg-icon icon-class="peoples" />所属角色 <svg-icon icon-class="peoples" />所属角色
@ -43,7 +43,7 @@
</el-col> </el-col>
<el-col :span="18" :xs="24"> <el-col :span="18" :xs="24">
<el-card> <el-card>
<template v-slot:header> <template #header>
<div class="clearfix"> <div class="clearfix">
<span>基本资料</span> <span>基本资料</span>
</div> </div>
@ -66,38 +66,38 @@
</template> </template>
<script setup name="Profile" lang="ts"> <script setup name="Profile" lang="ts">
import UserAvatar from "./userAvatar.vue"; import UserAvatar from './userAvatar.vue';
import UserInfo from "./userInfo.vue"; import UserInfo from './userInfo.vue';
import ResetPwd from "./resetPwd.vue"; import ResetPwd from './resetPwd.vue';
import ThirdParty from "./thirdParty.vue"; import ThirdParty from './thirdParty.vue';
import { getAuthList } from "@/api/system/social/auth"; import { getAuthList } from '@/api/system/social/auth';
import { getUserProfile } from "@/api/system/user"; import { getUserProfile } from '@/api/system/user';
const activeTab = ref("userinfo"); const activeTab = ref('userinfo');
const state = ref<Record<string, any>>({ const state = ref<Record<string, any>>({
user: {}, user: {},
roleGroup: '', roleGroup: '',
postGroup: '', postGroup: '',
auths: [] auths: []
}); });
const userForm = ref({}); const userForm = ref({});
const getUser = async () => { const getUser = async () => {
const res = await getUserProfile(); const res = await getUserProfile();
state.value.user = res.data.user; state.value.user = res.data.user;
userForm.value = { ...res.data.user } userForm.value = { ...res.data.user };
state.value.roleGroup = res.data.roleGroup; state.value.roleGroup = res.data.roleGroup;
state.value.postGroup = res.data.postGroup; state.value.postGroup = res.data.postGroup;
}; };
const getAuths = async () => { const getAuths = async () => {
const res = await getAuthList(); const res = await getAuthList();
state.value.auths = res.data; state.value.auths = res.data;
}; };
onMounted(() => { onMounted(() => {
getUser(); getUser();
getAuths(); getAuths();
}) });
</script> </script>

View File

@ -17,37 +17,43 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { updateUserPwd } from "@/api/system/user"; import { updateUserPwd } from '@/api/system/user';
import type { ResetPwdForm } from "@/api/system/user/types"; import type { ResetPwdForm } from '@/api/system/user/types';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const pwdRef = ref<ElFormInstance>(); const pwdRef = ref<ElFormInstance>();
const user = ref<ResetPwdForm>({ const user = ref<ResetPwdForm>({
oldPassword: "", oldPassword: '',
newPassword: "", newPassword: '',
confirmPassword: "" confirmPassword: ''
}); });
const equalToPassword = (rule: any, value: string, callback: any) => { const equalToPassword = (rule: any, value: string, callback: any) => {
if (user.value.newPassword !== value) { if (user.value.newPassword !== value) {
callback(new Error("两次输入的密码不一致")); callback(new Error('两次输入的密码不一致'));
} else { } else {
callback(); callback();
} }
}; };
const rules = ref({ const rules = ref({
oldPassword: [{ required: true, message: "旧密码不能为空", trigger: "blur" }], oldPassword: [{ required: true, message: '旧密码不能为空', trigger: 'blur' }],
newPassword: [{ required: true, message: "新密码不能为空", trigger: "blur" }, { newPassword: [
min: 6, { required: true, message: '新密码不能为空', trigger: 'blur' },
max: 20, {
message: "长度在 6 到 20 个字符", min: 6,
trigger: "blur" max: 20,
}], message: '长度在 6 到 20 个字符',
confirmPassword: [{ required: true, message: "确认密码不能为空", trigger: "blur" }, { trigger: 'blur'
required: true, }
validator: equalToPassword, ],
trigger: "blur" confirmPassword: [
}] { required: true, message: '确认密码不能为空', trigger: 'blur' },
{
required: true,
validator: equalToPassword,
trigger: 'blur'
}
]
}); });
/** 提交按钮 */ /** 提交按钮 */
@ -55,7 +61,7 @@ const submit = () => {
pwdRef.value?.validate(async (valid: boolean) => { pwdRef.value?.validate(async (valid: boolean) => {
if (valid) { if (valid) {
await updateUserPwd(user.value.oldPassword, user.value.newPassword); await updateUserPwd(user.value.oldPassword, user.value.newPassword);
proxy?.$modal.msgSuccess("修改成功"); proxy?.$modal.msgSuccess('修改成功');
} }
}); });
}; };

View File

@ -4,14 +4,14 @@
<el-table-column label="序号" width="50" type="index"></el-table-column> <el-table-column label="序号" width="50" type="index"></el-table-column>
<el-table-column label="绑定账号平台" width="140" align="center" prop="source" show-overflow-tooltip /> <el-table-column label="绑定账号平台" width="140" align="center" prop="source" show-overflow-tooltip />
<el-table-column label="头像" width="120" align="center" prop="avatar"> <el-table-column label="头像" width="120" align="center" prop="avatar">
<template v-slot="scope"> <template #default="scope">
<img :src="scope.row.avatar" style="width: 45px; height: 45px" /> <img :src="scope.row.avatar" style="width: 45px; height: 45px" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="系统账号" width="180" align="center" prop="userName" :show-overflow-tooltip="true" /> <el-table-column label="系统账号" width="180" align="center" prop="userName" :show-overflow-tooltip="true" />
<el-table-column label="绑定时间" width="180" align="center" prop="createTime" /> <el-table-column label="绑定时间" width="180" align="center" prop="createTime" />
<el-table-column label="操作" width="80" align="center" class-name="small-padding fixed-width"> <el-table-column label="操作" width="80" align="center" class-name="small-padding fixed-width">
<template v-slot="scope"> <template #default="scope">
<el-button size="small" type="text" @click="unlockAuth(scope.row)">解绑</el-button> <el-button size="small" type="text" @click="unlockAuth(scope.row)">解绑</el-button>
</template> </template>
</el-table-column> </el-table-column>
@ -20,25 +20,25 @@
<div id="git-user-binding"> <div id="git-user-binding">
<h4 class="provider-desc">你可以绑定以下第三方帐号</h4> <h4 class="provider-desc">你可以绑定以下第三方帐号</h4>
<div id="authlist" class="user-bind"> <div id="authlist" class="user-bind">
<a class="third-app" href="#" @click="authUrl('wechat');" title="使用 微信 账号授权登录"> <a class="third-app" href="#" title="使用 微信 账号授权登录" @click="authUrl('wechat')">
<div class="git-other-login-icon"> <div class="git-other-login-icon">
<svg-icon icon-class="wechat" /> <svg-icon icon-class="wechat" />
</div> </div>
<span class="app-name">WeiXin</span> <span class="app-name">WeiXin</span>
</a> </a>
<a class="third-app" href="#" @click="authUrl('maxkey');" title="使用 MaxKey 账号授权登录"> <a class="third-app" href="#" title="使用 MaxKey 账号授权登录" @click="authUrl('maxkey')">
<div class="git-other-login-icon"> <div class="git-other-login-icon">
<svg-icon icon-class="maxkey" /> <svg-icon icon-class="maxkey" />
</div> </div>
<span class="app-name">MaxKey</span> <span class="app-name">MaxKey</span>
</a> </a>
<a class="third-app" href="#" @click="authUrl('gitee');" title="使用 Gitee 账号授权登录"> <a class="third-app" href="#" title="使用 Gitee 账号授权登录" @click="authUrl('gitee')">
<div class="git-other-login-icon"> <div class="git-other-login-icon">
<svg-icon icon-class="gitee" /> <svg-icon icon-class="gitee" />
</div> </div>
<span class="app-name">Gitee</span> <span class="app-name">Gitee</span>
</a> </a>
<a class="third-app" href="#" @click="authUrl('github');" title="使用 GitHub 账号授权登录"> <a class="third-app" href="#" title="使用 GitHub 账号授权登录" @click="authUrl('github')">
<div class="git-other-login-icon"> <div class="git-other-login-icon">
<svg-icon icon-class="github" /> <svg-icon icon-class="github" />
</div> </div>
@ -50,31 +50,32 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { authUnlock, authBinding } from "@/api/system/social/auth"; import { authUnlock, authBinding } from '@/api/system/social/auth';
import { PropType } from "vue"; import { PropType } from 'vue';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const props = defineProps({ const props = defineProps({
auths: { auths: {
type: Object as PropType<any>, type: Object as PropType<any>
} }
}); });
const auths = computed(() => props.auths); const auths = computed(() => props.auths);
const unlockAuth = (row: any) => { const unlockAuth = (row: any) => {
ElMessageBox.confirm('您确定要解除"' + row.source + '"的账号绑定吗?') ElMessageBox.confirm('您确定要解除"' + row.source + '"的账号绑定吗?')
.then(() => { .then(() => {
return authUnlock(row.id); return authUnlock(row.id);
}).then((res: any) => { })
.then((res: any) => {
if (res.code === 200) { if (res.code === 200) {
proxy?.$modal.msgSuccess("解绑成功"); proxy?.$modal.msgSuccess('解绑成功');
proxy?.$tab.refreshPage(); proxy?.$tab.refreshPage();
} else { } else {
proxy?.$modal.msgError(res.msg); proxy?.$modal.msgError(res.msg);
} }
}).catch(() => { }); })
.catch(() => {});
}; };
const authUrl = (source: string) => { const authUrl = (source: string) => {
@ -111,7 +112,7 @@ const authUrl = (source: string) => {
margin-top: 10px; margin-top: 10px;
} }
.git-other-login-icon>img { .git-other-login-icon > img {
height: 32px; height: 32px;
} }
@ -122,15 +123,13 @@ a {
} }
.provider-desc { .provider-desc {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Liberation Sans", 'Liberation Sans', 'PingFang SC', 'Microsoft YaHei', 'Hiragino Sans GB', 'Wenquanyi Micro Hei', 'WenQuanYi Zen Hei', 'ST Heiti', SimHei, SimSun,
"PingFang SC", "Microsoft YaHei", "Hiragino Sans GB", "Wenquanyi Micro Hei", 'WenQuanYi Zen Hei Sharp', sans-serif;
"WenQuanYi Zen Hei", "ST Heiti", SimHei, SimSun, "WenQuanYi Zen Hei Sharp",
sans-serif;
font-size: 1.071rem; font-size: 1.071rem;
} }
td>img { td > img {
height: 20px; height: 20px;
width: 20px; width: 20px;
display: inline-block; display: inline-block;

View File

@ -5,16 +5,16 @@
<el-row> <el-row>
<el-col :xs="24" :md="12" :style="{ height: '350px' }"> <el-col :xs="24" :md="12" :style="{ height: '350px' }">
<vue-cropper <vue-cropper
v-if="visible"
ref="cropper" ref="cropper"
:img="options.img" :img="options.img"
:info="true" :info="true"
:autoCrop="options.autoCrop" :auto-crop="options.autoCrop"
:autoCropWidth="options.autoCropWidth" :auto-crop-width="options.autoCropWidth"
:autoCropHeight="options.autoCropHeight" :auto-crop-height="options.autoCropHeight"
:fixedBox="options.fixedBox" :fixed-box="options.fixedBox"
:outputType="options.outputType" :output-type="options.outputType"
@realTime="realTime" @real-time="realTime"
v-if="visible"
/> />
</el-col> </el-col>
<el-col :xs="24" :md="12" :style="{ height: '350px' }"> <el-col :xs="24" :md="12" :style="{ height: '350px' }">
@ -56,10 +56,10 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import "vue-cropper/dist/index.css"; import 'vue-cropper/dist/index.css';
import { VueCropper } from "vue-cropper"; import { VueCropper } from 'vue-cropper';
import { uploadAvatar } from "@/api/system/user"; import { uploadAvatar } from '@/api/system/user';
import useUserStore from "@/store/modules/user"; import useUserStore from '@/store/modules/user';
interface Options { interface Options {
img: string | any; // 裁剪图片的地址 img: string | any; // 裁剪图片的地址
@ -73,13 +73,12 @@ interface Options {
visible: boolean; visible: boolean;
} }
const userStore = useUserStore(); const userStore = useUserStore();
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const open = ref(false); const open = ref(false);
const visible = ref(false); const visible = ref(false);
const title = ref("修改头像"); const title = ref('修改头像');
const cropper = ref<any>({}); const cropper = ref<any>({});
//图片裁剪数据 //图片裁剪数据
@ -89,8 +88,8 @@ const options = reactive<Options>({
autoCropWidth: 200, autoCropWidth: 200,
autoCropHeight: 200, autoCropHeight: 200,
fixedBox: true, fixedBox: true,
outputType: "png", outputType: 'png',
fileName: "", fileName: '',
previews: {}, previews: {},
visible: false visible: false
}); });
@ -104,8 +103,7 @@ const modalOpened = () => {
visible.value = true; visible.value = true;
}; };
/** 覆盖默认上传行为 */ /** 覆盖默认上传行为 */
const requestUpload = (): any => { const requestUpload = (): any => {};
};
/** 向左旋转 */ /** 向左旋转 */
const rotateLeft = () => { const rotateLeft = () => {
cropper.value.rotateLeft(); cropper.value.rotateLeft();
@ -121,8 +119,8 @@ const changeScale = (num: number) => {
}; };
/** 上传预处理 */ /** 上传预处理 */
const beforeUpload = (file: any) => { const beforeUpload = (file: any) => {
if (file.type.indexOf("image/") == -1) { if (file.type.indexOf('image/') == -1) {
proxy?.$modal.msgError("文件格式错误,请上传图片类型,如JPGPNG后缀的文件。"); proxy?.$modal.msgError('文件格式错误,请上传图片类型,如JPGPNG后缀的文件。');
} else { } else {
const reader = new FileReader(); const reader = new FileReader();
reader.readAsDataURL(file); reader.readAsDataURL(file);
@ -136,7 +134,7 @@ const beforeUpload = (file: any) => {
const uploadImg = async () => { const uploadImg = async () => {
cropper.value.getCropBlob(async (data: any) => { cropper.value.getCropBlob(async (data: any) => {
let formData = new FormData(); let formData = new FormData();
formData.append("avatarfile", data, options.fileName); formData.append('avatarfile', data, options.fileName);
const res = await uploadAvatar(formData); const res = await uploadAvatar(formData);
open.value = false; open.value = false;
options.img = res.data.imgUrl; options.img = res.data.imgUrl;
@ -164,7 +162,7 @@ const closeDialog = () => {
} }
.user-info-head:hover:after { .user-info-head:hover:after {
content: "+"; content: '+';
position: absolute; position: absolute;
left: 0; left: 0;
right: 0; right: 0;

View File

@ -23,7 +23,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { updateUserProfile } from "@/api/system/user"; import { updateUserProfile } from '@/api/system/user';
const props = defineProps({ const props = defineProps({
user: { user: {
@ -35,26 +35,31 @@ const userForm = computed(() => props.user);
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const userRef = ref<ElFormInstance>(); const userRef = ref<ElFormInstance>();
const rules = ref<ElFormRules>({ const rules = ref<ElFormRules>({
nickName: [{ required: true, message: "用户昵称不能为空", trigger: "blur" }], nickName: [{ required: true, message: '用户昵称不能为空', trigger: 'blur' }],
email: [{ required: true, message: "邮箱地址不能为空", trigger: "blur" }, { email: [
type: "email", { required: true, message: '邮箱地址不能为空', trigger: 'blur' },
message: "请输入正确的邮箱地址", {
trigger: ["blur", "change"] type: 'email',
}], message: '请输入正确的邮箱地址',
phonenumber: [{ trigger: ['blur', 'change']
required: true, }
message: "手机号码不能为空", ],
trigger: "blur" phonenumber: [
}, { pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: "请输入正确的手机号码", trigger: "blur" }] {
required: true,
message: '手机号码不能为空',
trigger: 'blur'
},
{ pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: '请输入正确的手机号码', trigger: 'blur' }
]
}); });
/** 提交按钮 */ /** 提交按钮 */
const submit = () => { const submit = () => {
userRef.value?.validate(async (valid: boolean) => { userRef.value?.validate(async (valid: boolean) => {
if (valid) { if (valid) {
await updateUserProfile(props.user); await updateUserProfile(props.user);
proxy?.$modal.msgSuccess("修改成功"); proxy?.$modal.msgSuccess('修改成功');
} }
}); });
}; };

View File

@ -3,27 +3,27 @@
<el-row> <el-row>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="表名称" prop="tableName"> <el-form-item label="表名称" prop="tableName">
<el-input placeholder="请输入仓库名称" v-model="infoForm.tableName" /> <el-input v-model="infoForm.tableName" placeholder="请输入仓库名称" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="表描述" prop="tableComment"> <el-form-item label="表描述" prop="tableComment">
<el-input placeholder="请输入" v-model="infoForm.tableComment" /> <el-input v-model="infoForm.tableComment" placeholder="请输入" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="实体类名称" prop="className"> <el-form-item label="实体类名称" prop="className">
<el-input placeholder="请输入" v-model="infoForm.className" /> <el-input v-model="infoForm.className" placeholder="请输入" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="作者" prop="functionAuthor"> <el-form-item label="作者" prop="functionAuthor">
<el-input placeholder="请输入" v-model="infoForm.functionAuthor" /> <el-input v-model="infoForm.functionAuthor" placeholder="请输入" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="24"> <el-col :span="24">
<el-form-item label="备注" prop="remark"> <el-form-item label="备注" prop="remark">
<el-input type="textarea" :rows="3" v-model="infoForm.remark"></el-input> <el-input v-model="infoForm.remark" type="textarea" :rows="3"></el-input>
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
@ -31,19 +31,19 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { propTypes } from "@/utils/propTypes"; import { propTypes } from '@/utils/propTypes';
const prop = defineProps({ const prop = defineProps({
info: propTypes.any.def({}) info: propTypes.any.def({})
}); });
const infoForm = computed(() => prop.info) const infoForm = computed(() => prop.info);
// 表单校验 // 表单校验
const rules = ref({ const rules = ref({
tableName: [{ required: true, message: "请输入表名称", trigger: "blur" }], tableName: [{ required: true, message: '请输入表名称', trigger: 'blur' }],
tableComment: [{ required: true, message: "请输入表描述", trigger: "blur" }], tableComment: [{ required: true, message: '请输入表描述', trigger: 'blur' }],
className: [{ required: true, message: "请输入实体类名称", trigger: "blur" }], className: [{ required: true, message: '请输入实体类名称', trigger: 'blur' }],
functionAuthor: [{ required: true, message: "请输入作者", trigger: "blur" }] functionAuthor: [{ required: true, message: '请输入作者', trigger: 'blur' }]
}); });
</script> </script>

View File

@ -35,22 +35,22 @@
<el-table-column label="插入" min-width="5%"> <el-table-column label="插入" min-width="5%">
<template #default="scope"> <template #default="scope">
<el-checkbox true-label="1" false-label="0" v-model="scope.row.isInsert"></el-checkbox> <el-checkbox v-model="scope.row.isInsert" true-label="1" false-label="0"></el-checkbox>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="编辑" min-width="5%"> <el-table-column label="编辑" min-width="5%">
<template #default="scope"> <template #default="scope">
<el-checkbox true-label="1" false-label="0" v-model="scope.row.isEdit"></el-checkbox> <el-checkbox v-model="scope.row.isEdit" true-label="1" false-label="0"></el-checkbox>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="列表" min-width="5%"> <el-table-column label="列表" min-width="5%">
<template #default="scope"> <template #default="scope">
<el-checkbox true-label="1" false-label="0" v-model="scope.row.isList"></el-checkbox> <el-checkbox v-model="scope.row.isList" true-label="1" false-label="0"></el-checkbox>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="查询" min-width="5%"> <el-table-column label="查询" min-width="5%">
<template #default="scope"> <template #default="scope">
<el-checkbox true-label="1" false-label="0" v-model="scope.row.isQuery"></el-checkbox> <el-checkbox v-model="scope.row.isQuery" true-label="1" false-label="0"></el-checkbox>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="查询方式" min-width="10%"> <el-table-column label="查询方式" min-width="10%">
@ -69,7 +69,7 @@
</el-table-column> </el-table-column>
<el-table-column label="必填" min-width="5%"> <el-table-column label="必填" min-width="5%">
<template #default="scope"> <template #default="scope">
<el-checkbox true-label="1" false-label="0" v-model="scope.row.isRequired"></el-checkbox> <el-checkbox v-model="scope.row.isRequired" true-label="1" false-label="0"></el-checkbox>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="显示类型" min-width="12%"> <el-table-column label="显示类型" min-width="12%">
@ -104,7 +104,7 @@
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
<el-form label-width="100px"> <el-form label-width="100px">
<div style="text-align: center;margin-left:-100px;margin-top:10px;"> <div style="text-align: center; margin-left: -100px; margin-top: 10px">
<el-button type="primary" @click="submitForm()">提交</el-button> <el-button type="primary" @click="submitForm()">提交</el-button>
<el-button @click="close()">返回</el-button> <el-button @click="close()">返回</el-button>
</div> </div>
@ -118,7 +118,7 @@ import { DbColumnVO, DbTableVO } from '@/api/tool/gen/types';
import { optionselect as getDictOptionselect } from '@/api/system/dict/type'; import { optionselect as getDictOptionselect } from '@/api/system/dict/type';
import { DictTypeVO } from '@/api/system/dict/type/types'; import { DictTypeVO } from '@/api/system/dict/type/types';
import BasicInfoForm from './basicInfoForm.vue'; import BasicInfoForm from './basicInfoForm.vue';
import GenInfoForm from "./genInfoForm.vue"; import GenInfoForm from './genInfoForm.vue';
const route = useRoute(); const route = useRoute();
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@ -138,8 +138,8 @@ const submitForm = () => {
const basicForm = basicInfo.value?.$refs.basicInfoForm; const basicForm = basicInfo.value?.$refs.basicInfoForm;
const genForm = genInfo.value?.$refs.genInfoForm; const genForm = genInfo.value?.$refs.genInfoForm;
Promise.all([basicForm, genForm].map(getFormPromise)).then(async res => { Promise.all([basicForm, genForm].map(getFormPromise)).then(async (res) => {
const validateResult = res.every(item => !!item); const validateResult = res.every((item) => !!item);
if (validateResult) { if (validateResult) {
const genTable: any = Object.assign({}, info.value); const genTable: any = Object.assign({}, info.value);
genTable.columns = columns.value; genTable.columns = columns.value;
@ -155,24 +155,24 @@ const submitForm = () => {
close(); close();
} }
} else { } else {
proxy?.$modal.msgError("表单校验未通过,请重新检查提交内容"); proxy?.$modal.msgError('表单校验未通过,请重新检查提交内容');
} }
}); });
} };
const getFormPromise = (form: any) => { const getFormPromise = (form: any) => {
return new Promise(resolve => { return new Promise((resolve) => {
form.validate((res: any) => { form.validate((res: any) => {
resolve(res); resolve(res);
}); });
}); });
} };
const close = () => { const close = () => {
const obj = { path: "/tool/gen", query: { t: Date.now(), pageNum: route.query.pageNum } }; const obj = { path: '/tool/gen', query: { t: Date.now(), pageNum: route.query.pageNum } };
proxy?.$tab.closeOpenPage(obj); proxy?.$tab.closeOpenPage(obj);
} };
(async () => { (async () => {
const tableId = route.params && route.params.tableId as string; const tableId = route.params && (route.params.tableId as string);
if (tableId) { if (tableId) {
// 获取表详细信息 // 获取表详细信息
const res = await getGenTable(tableId); const res = await getGenTable(tableId);

View File

@ -95,7 +95,7 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="24" v-if="infoForm.genType == '1'"> <el-col v-if="infoForm.genType == '1'" :span="24">
<el-form-item prop="genPath"> <el-form-item prop="genPath">
<template #label> <template #label>
自定义路径 自定义路径
@ -223,7 +223,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { listMenu } from '@/api/system/menu'; import { listMenu } from '@/api/system/menu';
import { propTypes } from "@/utils/propTypes"; import { propTypes } from '@/utils/propTypes';
interface MenuOptionsType { interface MenuOptionsType {
menuId: number | string; menuId: number | string;
@ -246,21 +246,21 @@ const table = computed(() => props.tables);
// 表单校验 // 表单校验
const rules = ref({ const rules = ref({
tplCategory: [{ required: true, message: "请选择生成模板", trigger: "blur" }], tplCategory: [{ required: true, message: '请选择生成模板', trigger: 'blur' }],
packageName: [{ required: true, message: "请输入生成包路径", trigger: "blur" }], packageName: [{ required: true, message: '请输入生成包路径', trigger: 'blur' }],
moduleName: [{ required: true, message: "请输入生成模块名", trigger: "blur" }], moduleName: [{ required: true, message: '请输入生成模块名', trigger: 'blur' }],
businessName: [{ required: true, message: "请输入生成业务名", trigger: "blur" }], businessName: [{ required: true, message: '请输入生成业务名', trigger: 'blur' }],
functionName: [{ required: true, message: "请输入生成功能名", trigger: "blur" }] functionName: [{ required: true, message: '请输入生成功能名', trigger: 'blur' }]
}); });
const subSelectChange = () => { const subSelectChange = () => {
infoForm.value.subTableFkName = ""; infoForm.value.subTableFkName = '';
} };
const tplSelectChange = (value: string) => { const tplSelectChange = (value: string) => {
if (value !== "sub") { if (value !== 'sub') {
infoForm.value.subTableName = ""; infoForm.value.subTableName = '';
infoForm.value.subTableFkName = ""; infoForm.value.subTableFkName = '';
} }
} };
const setSubTableColumns = (value: string) => { const setSubTableColumns = (value: string) => {
table.value.forEach((item: any) => { table.value.forEach((item: any) => {
const name = item.tableName; const name = item.tableName;
@ -268,24 +268,27 @@ const setSubTableColumns = (value: string) => {
subColumns.value = item.columns; subColumns.value = item.columns;
return; return;
} }
}) });
} };
/** 查询菜单下拉树结构 */ /** 查询菜单下拉树结构 */
const getMenuTreeselect = async () => { const getMenuTreeselect = async () => {
const res = await listMenu(); const res = await listMenu();
res.data.forEach(m => m.menuId = m.menuId.toString()); res.data.forEach((m) => (m.menuId = m.menuId.toString()));
const data = proxy?.handleTree<MenuOptionsType>(res.data, "menuId"); const data = proxy?.handleTree<MenuOptionsType>(res.data, 'menuId');
if (data) { if (data) {
menuOptions.value = data menuOptions.value = data;
} }
} };
watch(() => props.info.subTableName, val => { watch(
setSubTableColumns(val); () => props.info.subTableName,
}); (val) => {
setSubTableColumns(val);
}
);
onMounted(() => { onMounted(() => {
getMenuTreeselect(); getMenuTreeselect();
}) });
</script> </script>

View File

@ -1,7 +1,7 @@
<template> <template>
<!-- 导入表 --> <!-- 导入表 -->
<el-dialog title="导入表" v-model="visible" width="1100px" top="5vh" append-to-body> <el-dialog v-model="visible" title="导入表" width="1100px" top="5vh" append-to-body>
<el-form :model="queryParams" ref="queryFormRef" :inline="true"> <el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="数据源" prop="dataName"> <el-form-item label="数据源" prop="dataName">
<el-select v-model="queryParams.dataName" filterable placeholder="请选择/输入数据源名称" style="width: 200px"> <el-select v-model="queryParams.dataName" filterable placeholder="请选择/输入数据源名称" style="width: 200px">
<el-option v-for="item in dataNameList" :key="item" :label="item" :value="item"> </el-option> <el-option v-for="item in dataNameList" :key="item" :label="item" :value="item"> </el-option>
@ -19,14 +19,14 @@
</el-form-item> </el-form-item>
</el-form> </el-form>
<el-row> <el-row>
<el-table @row-click="clickRow" ref="tableRef" :data="dbTableList" @selection-change="handleSelectionChange" height="260px"> <el-table ref="tableRef" :data="dbTableList" height="260px" @row-click="clickRow" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55"></el-table-column> <el-table-column type="selection" width="55"></el-table-column>
<el-table-column prop="tableName" label="表名称" :show-overflow-tooltip="true"></el-table-column> <el-table-column prop="tableName" label="表名称" :show-overflow-tooltip="true"></el-table-column>
<el-table-column prop="tableComment" label="表描述" :show-overflow-tooltip="true"></el-table-column> <el-table-column prop="tableComment" label="表描述" :show-overflow-tooltip="true"></el-table-column>
<el-table-column prop="createTime" label="创建时间"></el-table-column> <el-table-column prop="createTime" label="创建时间"></el-table-column>
<el-table-column prop="updateTime" label="更新时间"></el-table-column> <el-table-column prop="updateTime" label="更新时间"></el-table-column>
</el-table> </el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
</el-row> </el-row>
<template #footer> <template #footer>
<div class="dialog-footer"> <div class="dialog-footer">
@ -59,7 +59,7 @@ const queryParams = reactive<DbTableQuery>({
}); });
const dataNameList = ref<Array<string>>([]); const dataNameList = ref<Array<string>>([]);
const emit = defineEmits(["ok"]); const emit = defineEmits(['ok']);
/** 查询参数列表 */ /** 查询参数列表 */
const show = (dataName: string) => { const show = (dataName: string) => {
@ -71,53 +71,53 @@ const show = (dataName: string) => {
} }
getList(); getList();
visible.value = true; visible.value = true;
} };
/** 单击选择行 */ /** 单击选择行 */
const clickRow = (row: DbTableVO) => { const clickRow = (row: DbTableVO) => {
// ele bug // ele bug
tableRef.value?.toggleRowSelection(row, false); tableRef.value?.toggleRowSelection(row, false);
} };
/** 多选框选中数据 */ /** 多选框选中数据 */
const handleSelectionChange = (selection: DbTableVO[]) => { const handleSelectionChange = (selection: DbTableVO[]) => {
tables.value = selection.map(item => item.tableName); tables.value = selection.map((item) => item.tableName);
} };
/** 查询表数据 */ /** 查询表数据 */
const getList = async () => { const getList = async () => {
const res = await listDbTable(queryParams); const res = await listDbTable(queryParams);
dbTableList.value = res.rows; dbTableList.value = res.rows;
total.value = res.total; total.value = res.total;
} };
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
queryParams.pageNum = 1; queryParams.pageNum = 1;
getList(); getList();
} };
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
queryFormRef.value?.resetFields(); queryFormRef.value?.resetFields();
handleQuery(); handleQuery();
} };
/** 导入按钮操作 */ /** 导入按钮操作 */
const handleImportTable = async () => { const handleImportTable = async () => {
const tableNames = tables.value.join(","); const tableNames = tables.value.join(',');
if (tableNames == "") { if (tableNames == '') {
proxy?.$modal.msgError("请选择要导入的表"); proxy?.$modal.msgError('请选择要导入的表');
return; return;
} }
const res = await importTable({ tables: tableNames, dataName: queryParams.dataName }); const res = await importTable({ tables: tableNames, dataName: queryParams.dataName });
proxy?.$modal.msgSuccess(res.msg); proxy?.$modal.msgSuccess(res.msg);
if (res.code === 200) { if (res.code === 200) {
visible.value = false; visible.value = false;
emit("ok"); emit('ok');
} }
} };
/** 查询多数据源名称 */ /** 查询多数据源名称 */
const getDataNameList = async () => { const getDataNameList = async () => {
const res = await getDataNames() const res = await getDataNames();
dataNameList.value = res.data; dataNameList.value = res.data;
} };
defineExpose({ defineExpose({
show, show
}); });
</script> </script>

View File

@ -1,9 +1,9 @@
<template> <template>
<div class="p-2"> <div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="mb-[10px]" v-show="showSearch"> <div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover"> <el-card shadow="hover">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px"> <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px">
<el-form-item label="数据源" prop="dataName"> <el-form-item label="数据源" prop="dataName">
<el-select v-model="queryParams.dataName" filterable clearable placeholder="请选择/输入数据源名称" style="width: 200px"> <el-select v-model="queryParams.dataName" filterable clearable placeholder="请选择/输入数据源名称" style="width: 200px">
<el-option key="" label="全部" value="" /> <el-option key="" label="全部" value="" />
@ -39,20 +39,20 @@
<template #header> <template #header>
<el-row :gutter="10" class="mb8"> <el-row :gutter="10" class="mb8">
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="primary" plain icon="Download" @click="handleGenTable()" v-hasPermi="['tool:gen:code']">生成</el-button> <el-button v-hasPermi="['tool:gen:code']" type="primary" plain icon="Download" @click="handleGenTable()">生成</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="info" plain icon="Upload" @click="openImportTable" v-hasPermi="['tool:gen:import']">导入</el-button> <el-button v-hasPermi="['tool:gen:import']" type="info" plain icon="Upload" @click="openImportTable">导入</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleEditTable()" v-hasPermi="['tool:gen:edit']">修改</el-button> <el-button v-hasPermi="['tool:gen:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleEditTable()">修改</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['tool:gen:remove']"> <el-button v-hasPermi="['tool:gen:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">
删除 删除
</el-button> </el-button>
</el-col> </el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> <right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
</el-row> </el-row>
</template> </template>
@ -72,28 +72,28 @@
<el-table-column label="操作" align="center" width="330" class-name="small-padding fixed-width"> <el-table-column label="操作" align="center" width="330" class-name="small-padding fixed-width">
<template #default="scope"> <template #default="scope">
<el-tooltip content="预览" placement="top"> <el-tooltip content="预览" placement="top">
<el-button link type="primary" icon="View" @click="handlePreview(scope.row)" v-hasPermi="['tool:gen:preview']"></el-button> <el-button v-hasPermi="['tool:gen:preview']" link type="primary" icon="View" @click="handlePreview(scope.row)"></el-button>
</el-tooltip> </el-tooltip>
<el-tooltip content="编辑" placement="top"> <el-tooltip content="编辑" placement="top">
<el-button link type="primary" icon="Edit" @click="handleEditTable(scope.row)" v-hasPermi="['tool:gen:edit']"></el-button> <el-button v-hasPermi="['tool:gen:edit']" link type="primary" icon="Edit" @click="handleEditTable(scope.row)"></el-button>
</el-tooltip> </el-tooltip>
<el-tooltip content="删除" placement="top"> <el-tooltip content="删除" placement="top">
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['tool:gen:remove']"></el-button> <el-button v-hasPermi="['tool:gen:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
</el-tooltip> </el-tooltip>
<el-tooltip content="同步" placement="top"> <el-tooltip content="同步" placement="top">
<el-button link type="primary" icon="Refresh" @click="handleSynchDb(scope.row)" v-hasPermi="['tool:gen:edit']"></el-button> <el-button v-hasPermi="['tool:gen:edit']" link type="primary" icon="Refresh" @click="handleSynchDb(scope.row)"></el-button>
</el-tooltip> </el-tooltip>
<el-tooltip content="生成代码" placement="top"> <el-tooltip content="生成代码" placement="top">
<el-button link type="primary" icon="Download" @click="handleGenTable(scope.row)" v-hasPermi="['tool:gen:code']"></el-button> <el-button v-hasPermi="['tool:gen:code']" link type="primary" icon="Download" @click="handleGenTable(scope.row)"></el-button>
</el-tooltip> </el-tooltip>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
</el-card> </el-card>
<!-- 预览界面 --> <!-- 预览界面 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="80%" top="5vh" append-to-body class="scrollbar"> <el-dialog v-model="dialog.visible" :title="dialog.title" width="80%" top="5vh" append-to-body class="scrollbar">
<el-tabs v-model="preview.activeName"> <el-tabs v-model="preview.activeName">
<el-tab-pane <el-tab-pane
v-for="(value, key) in preview.data" v-for="(value, key) in preview.data"
@ -101,7 +101,7 @@
:name="key.substring(key.lastIndexOf('/') + 1, key.indexOf('.vm'))" :name="key.substring(key.lastIndexOf('/') + 1, key.indexOf('.vm'))"
:key="value" :key="value"
> >
<el-link :underline="false" icon="DocumentCopy" v-copyText="value" v-copyText:callback="copyTextSuccess" style="float:right"> <el-link v-copyText="value" v-copyText:callback="copyTextSuccess" :underline="false" icon="DocumentCopy" style="float: right">
&nbsp;复制 &nbsp;复制
</el-link> </el-link>
<pre>{{ value }}</pre> <pre>{{ value }}</pre>
@ -129,7 +129,7 @@ const single = ref(true);
const multiple = ref(true); const multiple = ref(true);
const total = ref(0); const total = ref(0);
const dateRange = ref<[DateModelType, DateModelType]>(['', '']); const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
const uniqueId = ref(""); const uniqueId = ref('');
const dataNameList = ref<Array<string>>([]); const dataNameList = ref<Array<string>>([]);
const queryFormRef = ref<ElFormInstance>(); const queryFormRef = ref<ElFormInstance>();
@ -140,13 +140,13 @@ const queryParams = ref<TableQuery>({
pageSize: 10, pageSize: 10,
tableName: '', tableName: '',
tableComment: '', tableComment: '',
dataName: "" dataName: ''
}) });
const preview = ref<any>({ const preview = ref<any>({
data: {}, data: {},
activeName: 'domain.java' activeName: 'domain.java'
}) });
const dialog = reactive<DialogOption>({ const dialog = reactive<DialogOption>({
visible: false, visible: false,
title: '代码预览' title: '代码预览'
@ -161,13 +161,13 @@ onActivated(() => {
queryFormRef.value?.resetFields(); queryFormRef.value?.resetFields();
getList(); getList();
} }
}) });
/** 查询多数据源名称 */ /** 查询多数据源名称 */
const getDataNameList = async () => { const getDataNameList = async () => {
const res = await getDataNames() const res = await getDataNames();
dataNameList.value = res.data; dataNameList.value = res.data;
} };
/** 查询表集合 */ /** 查询表集合 */
const getList = async () => { const getList = async () => {
@ -176,65 +176,65 @@ const getList = async () => {
tableList.value = res.rows; tableList.value = res.rows;
total.value = res.total; total.value = res.total;
loading.value = false; loading.value = false;
} };
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
queryParams.value.pageNum = 1; queryParams.value.pageNum = 1;
getList(); getList();
} };
/** 生成代码操作 */ /** 生成代码操作 */
const handleGenTable = async (row?: TableVO) => { const handleGenTable = async (row?: TableVO) => {
const tbIds = row?.tableId || ids.value; const tbIds = row?.tableId || ids.value;
if (tbIds == "") { if (tbIds == '') {
proxy?.$modal.msgError('请选择要生成的数据'); proxy?.$modal.msgError('请选择要生成的数据');
return; return;
} }
if (row?.genType === "1") { if (row?.genType === '1') {
await genCode(row.tableId); await genCode(row.tableId);
proxy?.$modal.msgSuccess('成功生成到自定义路径:' + row.genPath); proxy?.$modal.msgSuccess('成功生成到自定义路径:' + row.genPath);
} else { } else {
proxy?.$download.zip('/tool/gen/batchGenCode?tableIdStr=' + tbIds, 'ruoyi.zip'); proxy?.$download.zip('/tool/gen/batchGenCode?tableIdStr=' + tbIds, 'ruoyi.zip');
} }
} };
/** 同步数据库操作 */ /** 同步数据库操作 */
const handleSynchDb = async (row: TableVO) => { const handleSynchDb = async (row: TableVO) => {
const tableId = row.tableId; const tableId = row.tableId;
await proxy?.$modal.confirm('确认要强制同步"' + row.tableName + '"表结构吗?'); await proxy?.$modal.confirm('确认要强制同步"' + row.tableName + '"表结构吗?');
await synchDb(tableId); await synchDb(tableId);
proxy?.$modal.msgSuccess('同步成功'); proxy?.$modal.msgSuccess('同步成功');
} };
/** 打开导入表弹窗 */ /** 打开导入表弹窗 */
const openImportTable = () => { const openImportTable = () => {
importRef.value?.show(queryParams.value.dataName); importRef.value?.show(queryParams.value.dataName);
} };
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
dateRange.value = ['', '']; dateRange.value = ['', ''];
queryFormRef.value?.resetFields(); queryFormRef.value?.resetFields();
handleQuery(); handleQuery();
} };
/** 预览按钮 */ /** 预览按钮 */
const handlePreview = async (row: TableVO) => { const handlePreview = async (row: TableVO) => {
const res = await previewTable(row.tableId); const res = await previewTable(row.tableId);
preview.value.data = res.data; preview.value.data = res.data;
dialog.visible = true; dialog.visible = true;
preview.value.activeName = 'domain.java'; preview.value.activeName = 'domain.java';
} };
/** 复制代码成功 */ /** 复制代码成功 */
const copyTextSuccess = () => { const copyTextSuccess = () => {
proxy?.$modal.msgSuccess('复制成功'); proxy?.$modal.msgSuccess('复制成功');
} };
// 多选框选中数据 // 多选框选中数据
const handleSelectionChange = (selection: TableVO[]) => { const handleSelectionChange = (selection: TableVO[]) => {
ids.value = selection.map(item => item.tableId); ids.value = selection.map((item) => item.tableId);
single.value = selection.length != 1; single.value = selection.length != 1;
multiple.value = !selection.length; multiple.value = !selection.length;
} };
/** 修改按钮操作 */ /** 修改按钮操作 */
const handleEditTable = (row?: TableVO) => { const handleEditTable = (row?: TableVO) => {
const tableId = row?.tableId || ids.value[0]; const tableId = row?.tableId || ids.value[0];
router.push({ path: '/tool/gen-edit/index/' + tableId, query: { pageNum: queryParams.value.pageNum } }); router.push({ path: '/tool/gen-edit/index/' + tableId, query: { pageNum: queryParams.value.pageNum } });
} };
/** 删除按钮操作 */ /** 删除按钮操作 */
const handleDelete = async (row?: TableVO) => { const handleDelete = async (row?: TableVO) => {
const tableIds = row?.tableId || ids.value; const tableIds = row?.tableId || ids.value;
@ -242,10 +242,10 @@ const handleDelete = async (row?: TableVO) => {
await delTable(tableIds); await delTable(tableIds);
await getList(); await getList();
proxy?.$modal.msgSuccess('删除成功'); proxy?.$modal.msgSuccess('删除成功');
} };
onMounted(() => { onMounted(() => {
getList(); getList();
getDataNameList(); getDataNameList();
}) });
</script> </script>

View File

@ -1,8 +1,8 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "es2022", "target": "esnext",
"useDefineForClassFields": true,
"module": "esnext", "module": "esnext",
"useDefineForClassFields": true,
"moduleResolution": "node", "moduleResolution": "node",
"strict": true, "strict": true,
"jsx": "preserve", "jsx": "preserve",
@ -21,6 +21,6 @@
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true "forceConsistentCasingInFileNames": true
}, },
"include": ["src/**/*.ts", "src/**/*.vue", "src/types/**/*.d.ts"], "include": ["src/**/*.ts", "src/**/*.vue", "src/types/**/*.d.ts", "vite.config.ts"],
"exclude": ["node_modules", "dist", "**/*.js"] "exclude": ["node_modules", "dist", "**/*.js", "**/*.md", "src/**/*.md"]
} }