!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

@ -1,28 +1,35 @@
module.exports = {
env: {
browser: true,
es2021: true,
node: true
node: true,
es6: true
},
parser: 'vue-eslint-parser',
extends: [
'eslint:recommended',
'plugin:vue/vue3-essential',
'plugin:@typescript-eslint/recommended',
'plugin:vue/vue3-recommended',
'./.eslintrc-auto-import.json',
'plugin:prettier/recommended'
'plugin:@typescript-eslint/recommended',
"prettier",
'plugin:prettier/recommended',
],
parserOptions: {
ecmaVersion: '2020',
sourceType: 'module',
project: "./tsconfig.*?.json",
parser: '@typescript-eslint/parser'
},
plugins: ['vue', '@typescript-eslint'],
plugins: ['vue', '@typescript-eslint', 'import', 'promise', 'node', 'prettier'],
rules: {
'vue/multi-word-component-names': 'off',
'@typescript-eslint/no-empty-function': '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',
'prefer-rest-params': 'off',
// prettier
'prettier/prettier': 'error',
'@typescript-eslint/ban-types': [
'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",
"scripts": {
"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",
"lint": "eslint src/**/*.{ts,js,vue} --fix",
"lint:eslint": "eslint --fix --ext .ts,.js,.vue ./src ",
"prepare": "husky install",
"prettier": "prettier --write ."
},
@ -19,28 +20,28 @@
"dependencies": {
"@element-plus/icons-vue": "2.1.0",
"@vueup/vue-quill": "1.2.0",
"@vueuse/core": "9.5.0",
"@vueuse/core": "10.7.0",
"animate.css": "4.1.1",
"await-to-js": "^3.0.0",
"axios": "^1.3.4",
"crypto-js": "^4.1.1",
"echarts": "5.4.0",
"element-plus": "2.2.27",
"element-plus": "2.4.3",
"file-saver": "2.0.5",
"fuse.js": "6.6.2",
"js-cookie": "3.0.1",
"jsencrypt": "3.3.1",
"crypto-js": "^4.1.1",
"nprogress": "0.2.0",
"path-browserify": "1.0.1",
"path-to-regexp": "6.2.0",
"pinia": "2.0.22",
"pinia": "2.1.7",
"screenfull": "6.0.0",
"vform3-builds": "3.0.8",
"vue": "3.2.45",
"vue": "3.3.11",
"vue-cropper": "1.0.3",
"vue-i18n": "9.2.2",
"vue-router": "4.1.4",
"vue-types": "^5.0.3"
"vue-router": "4.2.5",
"vue-types": "5.1.1"
},
"devDependencies": {
"@iconify/json": "^2.2.40",
@ -51,34 +52,38 @@
"@types/node": "18.14.2",
"@types/nprogress": "0.2.0",
"@types/path-browserify": "^1.0.0",
"@typescript-eslint/eslint-plugin": "5.56.0",
"@typescript-eslint/parser": "5.56.0",
"@unocss/preset-attributify": "^0.50.6",
"@unocss/preset-icons": "^0.50.6",
"@unocss/preset-uno": "^0.50.6",
"@vitejs/plugin-vue": "4.0.0",
"@typescript-eslint/eslint-plugin": "6.14.0",
"@typescript-eslint/parser": "6.14.0",
"@unocss/preset-attributify": "^0.58.0",
"@unocss/preset-icons": "^0.58.0",
"@unocss/preset-uno": "^0.58.0",
"@vue/compiler-sfc": "3.2.45",
"@vitejs/plugin-vue": "4.5.2",
"autoprefixer": "10.4.14",
"eslint": "8.36.0",
"eslint-config-prettier": "8.8.0",
"eslint-plugin-prettier": "4.2.1",
"eslint-plugin-vue": "9.9.0",
"eslint": "8.55.0",
"eslint-config-prettier": "9.1.0",
"eslint-define-config": "2.0.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",
"husky": "7.0.4",
"postcss": "^8.4.21",
"prettier": "2.8.6",
"prettier": "3.1.1",
"sass": "1.56.1",
"typescript": "4.9.5",
"unocss": "^0.50.6",
"unplugin-auto-import": "0.13.0",
"unplugin-icons": "0.15.1",
"unplugin-vue-components": "0.23.0",
"vite": "4.3.1",
"typescript": "5.2.2",
"unocss": "^0.58.0",
"unplugin-auto-import": "0.17.2",
"unplugin-icons": "0.18.1",
"unplugin-vue-components": "0.26.0",
"unplugin-vue-setup-extend-plus": "0.4.9",
"vite-plugin-compression": "0.5.1",
"vite-plugin-svg-icons": "2.0.1",
"unplugin-vue-setup-extend-plus": "0.4.9",
"vitest": "^0.29.7",
"vue-eslint-parser": "9.1.0",
"vue-tsc": "0.35.0"
"vue-eslint-parser": "9.3.2",
"vue-tsc": "0.35.0",
"vite": "5.0.4"
}
}

View File

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

View File

@ -2,8 +2,7 @@
<el-breadcrumb class="app-breadcrumb" separator="/">
<transition-group name="breadcrumb">
<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">{{
item.meta?.title }}</span>
<span v-if="item.redirect === 'noRedirect' || index == levelList.length - 1" class="no-redirect">{{ item.meta?.title }}</span>
<a v-else @click.prevent="handleLink(item)">{{ item.meta?.title }}</a>
</el-breadcrumb-item>
</transition-group>
@ -11,42 +10,42 @@
</template>
<script setup lang="ts">
import { RouteLocationMatched } from 'vue-router'
import { RouteLocationMatched } from 'vue-router';
const route = useRoute();
const router = useRouter();
const levelList = ref<RouteLocationMatched[]>([])
const levelList = ref<RouteLocationMatched[]>([]);
const getBreadcrumb = () => {
// only show routes with meta.title
let matched = route.matched.filter(item => item.meta && item.meta.title);
const first = matched[0]
let matched = route.matched.filter((item) => item.meta && item.meta.title);
const first = matched[0];
// 判断是否为首页
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 name = route && route.name as string
const name = route && (route.name as string);
if (!name) {
return false
return false;
}
return name.trim() === 'Index'
}
return name.trim() === 'Index';
};
const handleLink = (item: RouteLocationMatched) => {
const { redirect, path } = item
redirect ? router.push(redirect as string) : router.push(path)
}
const { redirect, path } = item;
redirect ? router.push(redirect as string) : router.push(path);
};
watchEffect(() => {
// if you go to the redirect page, do not update the breadcrumbs
if (route.path.startsWith('/redirect/')) return
getBreadcrumb()
})
if (route.path.startsWith('/redirect/')) return;
getBreadcrumb();
});
onMounted(() => {
getBreadcrumb();
})
});
</script>
<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>
<!-- 代码构建 -->
<div>
<v-form-designer
class="build"
ref="buildRef"
class="build"
: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>
</template>
</v-form-designer>
</div>
</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">
.build {
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({
formJson: {
type: [String, Object],
default: ""
},
formData: {
type: [String, Object],
default: ""
},
isView: {
type: Boolean,
default: false
}
})
const props = withDefaults(defineProps<Props>(), {
formJson: '',
formData: '',
isView: false
});
const vFormRef = ref(null)
const vFormRef = ref(null);
// 获取表单数据-异步
const getFormData = () => {
return vFormRef.value.getFormData()
}
return vFormRef.value.getFormData();
};
/**
* 设置表单内容
@ -28,35 +30,28 @@ const getFormData = () => {
* formConfig{ formTemplate表单模板formData表单数据hiddenField需要隐藏的字段字符串集合disabledField需要禁用的自读字符串集合}
*/
const initForm = (formConf) => {
const { formTemplate, formData, hiddenField, disabledField } = toRaw(formConf)
const { formTemplate, formData, hiddenField, disabledField } = toRaw(formConf);
if (formTemplate) {
vFormRef.value.setFormJson(formTemplate)
vFormRef.value.setFormJson(formTemplate);
if (formData) {
vFormRef.value.setFormData(formData)
vFormRef.value.setFormData(formData);
}
if (disabledField && disabledField.length > 0) {
setTimeout(() => {
vFormRef.value.disableWidgets(disabledField)
}, 200)
vFormRef.value.disableWidgets(disabledField);
}, 200);
}
if (hiddenField && hiddenField.length > 0) {
setTimeout(() => {
vFormRef.value.hideWidgets(hiddenField)
}, 200)
vFormRef.value.hideWidgets(hiddenField);
}, 200);
}
if (props.isView) {
console.log(props.isView)
setTimeout(() => {
vFormRef.value.disableForm()
}, 100)
vFormRef.value.disableForm();
}, 100);
}
}
}
defineExpose({ getFormData, initForm })
};
defineExpose({ getFormData, initForm });
</script>
<template>
<div class="">
<v-form-render ref="vFormRef" :form-json="formJson" :form-data="formData" />
</div>
</template>

View File

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

View File

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

View File

@ -1,6 +1,7 @@
<template>
<div class="upload-file">
<el-upload
ref="fileUploadRef"
multiple
:action="uploadFileUrl"
:before-upload="handleBeforeUpload"
@ -12,30 +13,29 @@
:show-file-list="false"
:headers="headers"
class="upload-file-uploader"
ref="fileUploadRef"
>
<!-- 上传按钮 -->
<el-button type="primary">选取文件</el-button>
</el-upload>
<!-- 上传提示 -->
<div class="el-upload__tip" v-if="showTip">
<div v-if="showTip" class="el-upload__tip">
请上传
<template v-if="fileSize">
大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b>
</template>
<template v-if="fileType">
格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b>
格式为 <b style="color: #f56c6c">{{ fileType.join('/') }}</b>
</template>
的文件
</div>
<!-- 文件列表 -->
<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">
<span class="el-icon-document"> {{ getFileName(file.name) }} </span>
</el-link>
<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>
</li>
</transition-group>
@ -43,9 +43,9 @@
</template>
<script setup lang="ts">
import { listByIds, delOss } from "@/api/system/oss";
import { listByIds, delOss } from '@/api/system/oss';
import { propTypes } from '@/utils/propTypes';
import { globalHeaders } from "@/utils/request";
import { globalHeaders } from '@/utils/request';
const props = defineProps({
modelValue: [String, Object, Array],
@ -54,9 +54,9 @@ const props = defineProps({
// 大小限制(MB)
fileSize: propTypes.number.def(5),
// 文件类型, 例如['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;
@ -65,17 +65,17 @@ const number = ref(0);
const uploadList = ref<any[]>([]);
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 fileList = ref<any[]>([]);
const showTip = computed(
() => props.isShowTip && (props.fileType || props.fileSize)
);
const showTip = computed(() => props.isShowTip && (props.fileType || props.fileSize));
const fileUploadRef = ref<ElUploadInstance>();
watch(() => props.modelValue, async val => {
watch(
() => props.modelValue,
async (val) => {
if (val) {
let temp = 1;
// 首先将值转为数组
@ -83,14 +83,18 @@ watch(() => props.modelValue, async val => {
if (Array.isArray(val)) {
list = val;
} else {
const res = await listByIds(val as string)
const res = await listByIds(val as string);
list = res.data.map((oss) => {
const data = { name: oss.originalName, url: oss.url, ossId: oss.ossId };
const data = {
name: oss.originalName,
url: oss.url,
ossId: oss.ossId
};
return data;
});
}
// 然后将数组转为对象数组
fileList.value = list.map(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;
@ -99,7 +103,9 @@ watch(() => props.modelValue, async val => {
fileList.value = [];
return [];
}
}, { deep: true, immediate: true });
},
{ deep: true, immediate: true }
);
// 上传前校检格式和大小
const handleBeforeUpload = (file: any) => {
@ -109,7 +115,7 @@ const handleBeforeUpload = (file: any) => {
const fileExt = fileName[fileName.length - 1];
const isTypeOk = props.fileType.indexOf(fileExt) >= 0;
if (!isTypeOk) {
proxy?.$modal.msgError(`文件格式不正确, 请上传${props.fileType.join("/")}格式文件!`);
proxy?.$modal.msgError(`文件格式不正确, 请上传${props.fileType.join('/')}格式文件!`);
return false;
}
}
@ -121,25 +127,29 @@ const handleBeforeUpload = (file: any) => {
return false;
}
}
proxy?.$modal.loading("正在上传文件,请稍候...");
proxy?.$modal.loading('正在上传文件,请稍候...');
number.value++;
return true;
}
};
// 文件个数超出
const handleExceed = () => {
proxy?.$modal.msgError(`上传文件数量不能超过 ${props.limit} 个!`);
}
};
// 上传失败
const handleUploadError = () => {
proxy?.$modal.msgError("上传文件失败");
}
proxy?.$modal.msgError('上传文件失败');
};
// 上传成功回调
const handleUploadSuccess = (res: any, file: UploadFile) => {
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();
} else {
number.value--;
@ -148,48 +158,48 @@ const handleUploadSuccess = (res: any, file: UploadFile) => {
fileUploadRef.value?.handleRemove(file);
uploadedSuccessfully();
}
}
};
// 删除文件
const handleDelete = (index: number) => {
let ossId = fileList.value[index].ossId;
delOss(ossId);
fileList.value.splice(index, 1);
emit("update:modelValue", listToString(fileList.value));
}
emit('update:modelValue', listToString(fileList.value));
};
// 上传结束处理
const uploadedSuccessfully = () => {
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 = [];
number.value = 0;
emit("update:modelValue", listToString(fileList.value));
emit('update:modelValue', listToString(fileList.value));
proxy?.$modal.closeLoading();
}
}
};
// 获取文件名称
const getFileName = (name: string) => {
// 如果是url那么取最后的名字 如果不是直接返回
if (name.lastIndexOf("/") > -1) {
return name.slice(name.lastIndexOf("/") + 1);
if (name.lastIndexOf('/') > -1) {
return name.slice(name.lastIndexOf('/') + 1);
} else {
return name;
}
}
};
// 对象转成指定字符串分隔
const listToString = (list: any[], separator?: string) => {
let strs = "";
separator = separator || ",";
list.forEach(item => {
let strs = '';
separator = separator || ',';
list.forEach((item) => {
if (item.ossId) {
strs += item.ossId + separator;
}
})
return strs != "" ? strs.substring(0, strs.length - 1) : "";
}
});
return strs != '' ? strs.substring(0, strs.length - 1) : '';
};
</script>
<style scoped lang="scss">

View File

@ -1,5 +1,5 @@
<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">
<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"
@ -13,12 +13,12 @@ import { propTypes } from '@/utils/propTypes';
defineProps({
isActive: propTypes.bool.def(false)
})
});
const emit = defineEmits(['toggleClick'])
const emit = defineEmits(['toggleClick']);
const toggleClick = () => {
emit('toggleClick');
}
};
</script>
<style scoped>

View File

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

View File

@ -1,6 +1,6 @@
<template>
<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>
<svg-icon :icon-class="modelValue" />
</template>
@ -8,18 +8,18 @@
<el-popover shadow="none" :visible="visible" placement="bottom-end" trigger="click" :width="450">
<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-bottom v-show="!visible"></i-ep-caret-bottom>
</div>
</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]">
<ul class="icon-list">
<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" />
</li>
</el-tooltip>
@ -50,13 +50,11 @@ const filterValue = ref('');
*/
const filterIcons = () => {
if (filterValue.value) {
iconNames.value = icons.filter(iconName =>
iconName.includes(filterValue.value)
);
iconNames.value = icons.filter((iconName) => iconName.includes(filterValue.value));
} else {
iconNames.value = icons;
}
}
};
/**
* 选择图标
* @param iconName 选择的图标名称
@ -64,12 +62,12 @@ const filterIcons = () => {
const selectedIcon = (iconName: string) => {
emit('update:modelValue', iconName);
visible.value = false;
}
};
</script>
<style scoped lang="scss">
.el-scrollbar {
max-height: calc(50vh - 100px)!important;
max-height: calc(50vh - 100px) !important;
overflow-y: auto;
}
.el-divider--horizontal {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,11 +1,11 @@
<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>
<div class="setting-drawer-block-checbox">
<div class="setting-drawer-block-checbox-item" @click="handleTheme(SideThemeEnum.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">
<svg viewBox="64 64 896 896" data-icon="check" width="1em" height="1em" :fill="theme" aria-hidden="true" focusable="false" class>
<path
@ -17,7 +17,7 @@
</div>
<div class="setting-drawer-block-checbox-item" @click="handleTheme(SideThemeEnum.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">
<svg viewBox="64 64 896 896" data-icon="check" width="1em" height="1em" :fill="theme" aria-hidden="true" focusable="false" class>
<path
@ -37,7 +37,7 @@
<div class="drawer-item">
<span>深色模式</span>
<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>
</div>
@ -88,126 +88,126 @@
</template>
<script setup lang="ts">
import { useDynamicTitle } from '@/utils/dynamicTitle'
import useAppStore from '@/store/modules/app'
import useSettingsStore from '@/store/modules/settings'
import usePermissionStore from '@/store/modules/permission'
import { handleThemeStyle } from '@/utils/theme'
import { ComponentInternalInstance } from "vue";
import { SettingTypeEnum } from "@/enums/SettingTypeEnum";
import { SideThemeEnum } from "@/enums/SideThemeEnum";
import { useDynamicTitle } from '@/utils/dynamicTitle';
import useAppStore from '@/store/modules/app';
import useSettingsStore from '@/store/modules/settings';
import usePermissionStore from '@/store/modules/permission';
import { handleThemeStyle } from '@/utils/theme';
import { SettingTypeEnum } from '@/enums/SettingTypeEnum';
import { SideThemeEnum } from '@/enums/SideThemeEnum';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const appStore = useAppStore()
const settingsStore = useSettingsStore()
const permissionStore = usePermissionStore()
const appStore = useAppStore();
const settingsStore = useSettingsStore();
const permissionStore = usePermissionStore();
const showSettings = ref(false);
const theme = ref(settingsStore.theme);
const sideTheme = ref(settingsStore.sideTheme);
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({
storageKey: 'useDarkKey',
valueDark: 'dark',
valueLight: 'light',
valueLight: 'light'
});
watch(isDark, ()=> {
watch(isDark, () => {
if (isDark.value) {
settingsStore.changeSetting({ key: SettingTypeEnum.SIDE_THEME, value: SideThemeEnum.DARK })
settingsStore.changeSetting({ key: SettingTypeEnum.SIDE_THEME, value: SideThemeEnum.DARK });
} else {
settingsStore.changeSetting({ key: SettingTypeEnum.SIDE_THEME, value: sideTheme.value })
settingsStore.changeSetting({ key: SettingTypeEnum.SIDE_THEME, value: sideTheme.value });
}
})
});
const toggleDark = () => useToggle(isDark);
/** 是否需要topNav */
const topNav = computed({
get: () => storeSettings.value.topNav,
set: (val) => {
settingsStore.changeSetting({ key: SettingTypeEnum.TOP_NAV, value: val })
settingsStore.changeSetting({ key: SettingTypeEnum.TOP_NAV, value: val });
if (!val) {
appStore.toggleSideBarHide(false);
permissionStore.setSidebarRouters(permissionStore.defaultRoutes);
}
}
})
});
/** 是否需要tagview */
const tagsView = computed({
get: () => storeSettings.value.tagsView,
set: (val) => {
settingsStore.changeSetting({ key: SettingTypeEnum.TAGS_VIEW, value: val })
settingsStore.changeSetting({ key: SettingTypeEnum.TAGS_VIEW, value: val });
}
})
});
/**是否需要固定头部 */
const fixedHeader = computed({
get: () => storeSettings.value.fixedHeader,
set: (val) => {
settingsStore.changeSetting({ key: SettingTypeEnum.FIXED_HEADER, value: val })
settingsStore.changeSetting({ key: SettingTypeEnum.FIXED_HEADER, value: val });
}
})
});
/**是否需要侧边栏的logo */
const sidebarLogo = computed({
get: () => storeSettings.value.sidebarLogo,
set: (val) => {
settingsStore.changeSetting({ key: SettingTypeEnum.SIDEBAR_LOGO, value: val })
settingsStore.changeSetting({ key: SettingTypeEnum.SIDEBAR_LOGO, value: val });
}
})
});
/**是否需要侧边栏的动态网页的title */
const dynamicTitle = computed({
get: () => storeSettings.value.dynamicTitle,
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) => {
settingsStore.changeSetting({ key: SettingTypeEnum.THEME, value: val })
const themeChange = (val: string) => {
settingsStore.changeSetting({ key: SettingTypeEnum.THEME, value: val });
theme.value = val;
if (val) {
handleThemeStyle(val);
}
}
};
const handleTheme = (val: string) => {
sideTheme.value = val;
if (isDark.value && val === SideThemeEnum.LIGHT) {
// 暗黑模式颜色不变
settingsStore.changeSetting({ key: SettingTypeEnum.SIDE_THEME, value: SideThemeEnum.DARK })
return
settingsStore.changeSetting({ key: SettingTypeEnum.SIDE_THEME, value: SideThemeEnum.DARK });
return;
}
settingsStore.changeSetting({ key: SettingTypeEnum.SIDE_THEME, value: val })
}
settingsStore.changeSetting({ key: SettingTypeEnum.SIDE_THEME, value: val });
};
const saveSetting = () => {
proxy?.$modal.loading("正在保存到本地,请稍候...");
proxy?.$modal.loading('正在保存到本地,请稍候...');
let layoutSetting = {
"topNav": storeSettings.value.topNav,
"tagsView": storeSettings.value.tagsView,
"fixedHeader": storeSettings.value.fixedHeader,
"sidebarLogo": storeSettings.value.sidebarLogo,
"dynamicTitle": storeSettings.value.dynamicTitle,
"sideTheme": storeSettings.value.sideTheme,
"theme": storeSettings.value.theme
topNav: storeSettings.value.topNav,
tagsView: storeSettings.value.tagsView,
fixedHeader: storeSettings.value.fixedHeader,
sidebarLogo: storeSettings.value.sidebarLogo,
dynamicTitle: storeSettings.value.dynamicTitle,
sideTheme: storeSettings.value.sideTheme,
theme: storeSettings.value.theme
};
localStorage.setItem("layout-setting", JSON.stringify(layoutSetting));
setTimeout(() => {proxy?.$modal.closeLoading()}, 1000)
}
localStorage.setItem('layout-setting', JSON.stringify(layoutSetting));
setTimeout(() => {
proxy?.$modal.closeLoading();
}, 1000);
};
const resetSetting = () => {
proxy?.$modal.loading("正在清除设置缓存并刷新,请稍候...");
localStorage.removeItem("layout-setting")
setTimeout("window.location.reload()", 1000)
}
proxy?.$modal.loading('正在清除设置缓存并刷新,请稍候...');
localStorage.removeItem('layout-setting');
setTimeout('window.location.reload()', 1000);
};
const openSetting = () => {
showSettings.value = true;
}
};
defineExpose({
openSetting,
})
openSetting
});
</script>
<style lang="scss" scoped>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -5,56 +5,56 @@
</template>
<script setup lang="ts">
import useTagsViewStore from '@/store/modules/tagsView'
import { TagView } from 'vue-router'
import useTagsViewStore from '@/store/modules/tagsView';
import { TagView } from 'vue-router';
const tagAndTagSpacing = ref(4);
const scrollContainerRef = ref<ElScrollbarInstance>()
const scrollContainerRef = ref<ElScrollbarInstance>();
const scrollWrapper = computed(() => scrollContainerRef.value?.$refs.wrapRef as any);
onMounted(() => {
scrollWrapper.value?.addEventListener('scroll', emitScroll, true)
})
scrollWrapper.value?.addEventListener('scroll', emitScroll, true);
});
onBeforeUnmount(() => {
scrollWrapper.value?.removeEventListener('scroll', emitScroll)
})
scrollWrapper.value?.removeEventListener('scroll', emitScroll);
});
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;
$scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4
}
const emits = defineEmits(['scroll'])
$scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4;
};
const emits = defineEmits(['scroll']);
const emitScroll = () => {
emits('scroll')
}
emits('scroll');
};
const tagsViewStore = useTagsViewStore()
const tagsViewStore = useTagsViewStore();
const visitedViews = computed(() => tagsViewStore.visitedViews);
const moveToTarget = (currentTag: TagView) => {
const $container = scrollContainerRef.value?.$el
const $containerWidth = $container.offsetWidth
const $container = scrollContainerRef.value?.$el;
const $containerWidth = $container.offsetWidth;
const $scrollWrapper = scrollWrapper.value;
let firstTag = null
let lastTag = null
let firstTag = null;
let lastTag = null;
// find first tag and last tag
if (visitedViews.value.length > 0) {
firstTag = visitedViews.value[0]
lastTag = visitedViews.value[visitedViews.value.length - 1]
firstTag = visitedViews.value[0];
lastTag = visitedViews.value[visitedViews.value.length - 1];
}
if (firstTag === currentTag) {
$scrollWrapper.scrollLeft = 0
$scrollWrapper.scrollLeft = 0;
} else if (lastTag === currentTag) {
$scrollWrapper.scrollLeft = $scrollWrapper.scrollWidth - $containerWidth
$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
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)) {
@ -68,21 +68,21 @@ const moveToTarget = (currentTag: TagView) => {
}
// the tag's offsetLeft after of nextTag
const afterNextTagOffsetLeft = nextTag.offsetLeft + nextTag.offsetWidth + tagAndTagSpacing.value
const afterNextTagOffsetLeft = nextTag.offsetLeft + nextTag.offsetWidth + tagAndTagSpacing.value;
// the tag's offsetLeft before of prevTag
const beforePrevTagOffsetLeft = prevTag.offsetLeft - tagAndTagSpacing.value
const beforePrevTagOffsetLeft = prevTag.offsetLeft - tagAndTagSpacing.value;
if (afterNextTagOffsetLeft > $scrollWrapper.scrollLeft + $containerWidth) {
$scrollWrapper.scrollLeft = afterNextTagOffsetLeft - $containerWidth
$scrollWrapper.scrollLeft = afterNextTagOffsetLeft - $containerWidth;
} else if (beforePrevTagOffsetLeft < $scrollWrapper.scrollLeft) {
$scrollWrapper.scrollLeft = beforePrevTagOffsetLeft
$scrollWrapper.scrollLeft = beforePrevTagOffsetLeft;
}
}
}
};
defineExpose({
moveToTarget,
})
moveToTarget
});
</script>
<style lang="scss" scoped>

View File

@ -14,36 +14,35 @@
>
{{ tag.title }}
<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>
</router-link>
</scroll-pane>
<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 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 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 @click="closeAllTags(selectedTag)"><circle-close 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 @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="!isLastView()" @click="closeRightTags"><right style="width: 1em; height: 1em" /> 关闭右侧</li>
<li @click="closeAllTags(selectedTag)"><circle-close style="width: 1em; height: 1em" /> 全部关闭</li>
</ul>
</div>
</template>
<script setup lang="ts">
import ScrollPane from './ScrollPane.vue'
import { getNormalPath } from '@/utils/ruoyi'
import useTagsViewStore from "@/store/modules/tagsView";
import useSettingsStore from '@/store/modules/settings'
import usePermissionStore from '@/store/modules/permission'
import { ComponentInternalInstance } from "vue";
import { RouteOption, TagView, RouteLocationRaw } from "vue-router";
import ScrollPane from './ScrollPane.vue';
import { getNormalPath } from '@/utils/ruoyi';
import useTagsViewStore from '@/store/modules/tagsView';
import useSettingsStore from '@/store/modules/settings';
import usePermissionStore from '@/store/modules/permission';
import { RouteRecordRaw, TagView } from 'vue-router';
const visible = ref(false);
const top = ref(0);
const left = ref(0);
const selectedTag = ref<TagView>({});
const affixTags = ref<TagView[]>([]);
const scrollPaneRef = ref(ScrollPane);
const scrollPaneRef = ref<InstanceType<typeof ScrollPane>>();
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const route = useRoute();
@ -56,53 +55,54 @@ const theme = computed(() => useSettingsStore().theme);
watch(route, () => {
addTags();
moveToCurrentTag();
})
});
watch(visible, (value) => {
if (value) {
document.body.addEventListener('click', closeMenu);
} else {
document.body.removeEventListener('click', closeMenu);
}
})
});
const isActive = (r: TagView): boolean => {
return r.path === route.path;
}
};
const activeStyle = (tag: TagView) => {
if (!isActive(tag)) return {};
return {
"background-color": theme.value,
"border-color": theme.value
'background-color': theme.value,
'border-color': theme.value
};
}
};
const isAffix = (tag: TagView) => {
return tag.meta && tag.meta.affix;
}
};
const isFirstView = () => {
try {
return selectedTag.value.fullPath === '/index' || selectedTag.value.fullPath === visitedViews.value[1].fullPath;
} catch (err) {
return false;
}
}
};
const isLastView = () => {
try {
return selectedTag.value.fullPath === visitedViews.value[visitedViews.value.length - 1].fullPath;
} catch (err) {
return false;
}
}
const filterAffixTags = (routes:RouteOption [], basePath = '') => {
let tags:TagView[] = []
routes.forEach(route => {
};
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,
name: route.name as string,
meta: { ...route.meta }
})
});
}
if (route.children) {
const tempTags = filterAffixTags(route.children, route.path);
@ -110,11 +110,11 @@ const filterAffixTags = (routes:RouteOption [], basePath = '') => {
tags = [...tags, ...tempTags];
}
}
})
return tags
}
});
return tags;
};
const initTags = () => {
const res = filterAffixTags(routes.value);
const res = filterAffixTags(routes.value as any);
affixTags.value = res;
for (const tag of res) {
// Must have tag name
@ -122,75 +122,75 @@ const initTags = () => {
useTagsViewStore().addVisitedView(tag);
}
}
}
};
const addTags = () => {
const { name } = route;
if(route.query.title) {
route.meta.title = route.query.title;
if (route.query.title) {
route.meta.title = route.query.title as string;
}
if (name) {
useTagsViewStore().addView(route);
useTagsViewStore().addView(route as any);
if (route.meta.link) {
useTagsViewStore().addIframeView(route);
useTagsViewStore().addIframeView(route as any);
}
}
return false
}
return false;
};
const moveToCurrentTag = () => {
nextTick(() => {
for (const r of visitedViews.value) {
if (r.path === route.path) {
scrollPaneRef.value.moveToTarget(r);
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);
useTagsViewStore().delIframeView(route as any);
}
}
};
const closeSelectedTag = (view: TagView) => {
proxy?.$tab.closePage(view).then(({ visitedViews }: any) => {
if (isActive(view)) {
toLastView(visitedViews, view);
}
})
}
});
};
const closeRightTags = () => {
proxy?.$tab.closeRightPage(selectedTag.value).then(visitedViews => {
if (!visitedViews.find(i => i.fullPath === route.fullPath)) {
proxy?.$tab.closeRightPage(selectedTag.value).then((visitedViews) => {
if (!visitedViews.find((i) => i.fullPath === route.fullPath)) {
toLastView(visitedViews);
}
})
}
});
};
const closeLeftTags = () => {
proxy?.$tab.closeLeftPage(selectedTag.value).then(visitedViews => {
if (!visitedViews.find(i => i.fullPath === route.fullPath)) {
proxy?.$tab.closeLeftPage(selectedTag.value).then((visitedViews) => {
if (!visitedViews.find((i: TagView) => i.fullPath === route.fullPath)) {
toLastView(visitedViews);
}
})
}
});
};
const closeOthersTags = () => {
router.push(selectedTag.value as RouteLocationRaw).catch(() => { });
router.push(selectedTag.value).catch(() => {});
proxy?.$tab.closeOtherPage(selectedTag.value).then(() => {
moveToCurrentTag();
})
}
});
};
const closeAllTags = (view: TagView) => {
proxy?.$tab.closeAllPage().then(({ visitedViews }) => {
if (affixTags.value.some(tag => tag.path === route.path)) {
if (affixTags.value.some((tag) => tag.path === route.path)) {
return;
}
toLastView(visitedViews, view);
})
}
const toLastView = (visitedViews:TagView[], view?: TagView) => {
});
};
const toLastView = (visitedViews: TagView[], view?: TagView) => {
const latestView = visitedViews.slice(-1)[0];
if (latestView) {
router.push(latestView.fullPath as string);
@ -204,7 +204,7 @@ const toLastView = (visitedViews:TagView[], view?: TagView) => {
router.push('/');
}
}
}
};
const openMenu = (tag: TagView, e: MouseEvent) => {
const menuMinWidth = 105;
const offsetLeft = proxy?.$el.getBoundingClientRect().left; // container margin left
@ -218,22 +218,21 @@ const openMenu = (tag: TagView, e: MouseEvent) => {
left.value = l;
}
top.value = e.clientY
top.value = e.clientY;
visible.value = true;
selectedTag.value = tag;
}
};
const closeMenu = () => {
visible.value = false;
}
};
const handleScroll = () => {
closeMenu();
}
};
onMounted(() => {
initTags();
addTags();
})
});
</script>
<style lang="scss" scoped>
@ -242,7 +241,9 @@ onMounted(() => {
width: 100%;
background-color: var(--el-bg-color);
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-item {
display: inline-block;
@ -271,7 +272,7 @@ onMounted(() => {
color: #fff;
border-color: #42b983;
&::before {
content: "";
content: '';
background: #fff;
display: inline-block;
width: 8px;

View File

@ -3,12 +3,12 @@
<el-dialog v-model="state.isShowSearch" destroy-on-close :show-close="false">
<template #footer>
<el-autocomplete
ref="layoutMenuAutocompleteRef"
v-model="state.menuQuery"
:fetch-suggestions="menuSearch"
placeholder="搜索"
ref="layoutMenuAutocompleteRef"
@select="onHandleSelect"
:fit-input-width="true"
@select="onHandleSelect"
>
<template #prefix>
<svg-icon class-name="search-icon" icon-class="search" />
@ -29,12 +29,12 @@
import { getNormalPath } from '@/utils/ruoyi';
import { isHttp } from '@/utils/validate';
import usePermissionStore from '@/store/modules/permission';
import { RouteOption } from 'vue-router';
import { RouteRecordRaw } from 'vue-router';
type Router = Array<{
path: string;
icon: string;
title: string[];
}>
}>;
type SearchState<T = any> = {
isShowSearch: boolean;
menuQuery: string;
@ -47,7 +47,7 @@ const routes = computed(() => usePermissionStore().routes);
const state = reactive<SearchState>({
isShowSearch: false,
menuQuery: '',
menuList: [],
menuList: []
});
// 搜索弹窗打开
@ -75,9 +75,9 @@ const menuSearch = (queryString: string, cb: Function) => {
// Filter out the routes that can be displayed in the sidebar
// And generate the internationalized title
const generateRoutes = (routes: RouteOption[], basePath = '', prefixTitle: string[] = []) => {
let res: Router = []
routes.forEach(r => {
const generateRoutes = (routes: RouteRecordRaw[], basePath = '', prefixTitle: string[] = []) => {
let res: Router = [];
routes.forEach((r) => {
// skip hidden router
if (!r.hidden) {
const p = r.path.length > 0 && r.path[0] === '/' ? r.path : '/' + r.path;
@ -85,7 +85,7 @@ const generateRoutes = (routes: RouteOption[], basePath = '', prefixTitle: strin
path: !isHttp(r.path) ? getNormalPath(basePath + p) : r.path,
icon: r.meta?.icon,
title: [...prefixTitle]
}
};
if (r.meta && r.meta.title) {
data.title = [...data.title, r.meta.title];
if (r.redirect !== 'noRedirect') {
@ -102,27 +102,26 @@ const generateRoutes = (routes: RouteOption[], basePath = '', prefixTitle: strin
}
}
}
})
});
res.forEach((item: any) => {
if (item.title instanceof Array) {
item.title = item.title.join('/');
}
});
return res;
}
};
// 当前菜单选中时
const onHandleSelect = (val: any) => {
const paths = val.path;
if (isHttp(paths)) {
// http(s):// 路径新窗口打开
const pindex = paths.indexOf("http");
window.open(paths.substring(pindex, paths.length), "_blank");
const pindex = paths.indexOf('http');
window.open(paths.substring(pindex, paths.length), '_blank');
} else {
router.push(paths);
}
state.menuQuery = ''
state.menuQuery = '';
closeSearch();
};
// 暴露变量

View File

@ -1,12 +1,12 @@
<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-title">通知公告</div>
<div class="head-box-btn" @click="readAll">全部已读</div>
</div>
<div class="content-box" v-loading="state.loading">
<div v-loading="state.loading" class="content-box">
<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>{{ v.message }}</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>
</div>
</template>
<el-empty :description="'消息为空'" v-else></el-empty>
<el-empty v-else :description="'消息为空'"></el-empty>
</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>
</template>
<script setup lang="ts" name="layoutBreadcrumbUserNews">
import { ref } from "vue";
import { storeToRefs } from 'pinia'
import { nextTick, onMounted, reactive } from "vue";
import { storeToRefs } from 'pinia';
import useNoticeStore from '@/store/modules/notice';
const noticeStore = storeToRefs(useNoticeStore());
const {readAll} = useNoticeStore();
const { readAll } = useNoticeStore();
// 定义变量内容
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;
};
//点击消息,写入已读
const onNewsClick = (item: any) => {
newsList.value[item].read = true;
@ -58,7 +55,7 @@ const onNewsClick = (item: any) => {
// 前往通知中心点击
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(() => {

View File

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

View File

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

View File

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

View File

@ -1,4 +1,4 @@
import { createWebHistory, createRouter, RouteOption } from 'vue-router';
import { createWebHistory, createRouter, RouteRecordRaw } from 'vue-router';
/* Layout */
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',
component: Layout,
@ -92,7 +92,7 @@ export const constantRoutes: RouteOption[] = [
];
// 动态路由,基于用户权限动态去加载
export const dynamicRoutes: RouteOption[] = [
export const dynamicRoutes: RouteRecordRaw[] = [
{
path: '/system/user-auth',
component: Layout,

View File

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

View File

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

View File

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

View File

@ -11,7 +11,7 @@ export const useTagsViewStore = defineStore('tagsView', () => {
};
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(
Object.assign({}, view, {
title: view.meta?.title || 'no-name'
@ -20,12 +20,12 @@ export const useTagsViewStore = defineStore('tagsView', () => {
};
const delIframeView = (view: TagView): Promise<TagView[]> => {
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]);
});
};
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(
Object.assign({}, view, {
title: view.meta?.title || 'no-name'
@ -80,7 +80,7 @@ export const useTagsViewStore = defineStore('tagsView', () => {
const delOthersVisitedViews = (view: TagView): Promise<TagView[]> => {
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;
});
resolve([...visitedViews.value]);
@ -111,7 +111,7 @@ export const useTagsViewStore = defineStore('tagsView', () => {
};
const delAllVisitedViews = (): Promise<TagView[]> => {
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]);
});
};
@ -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) {
if (v.path === view.path) {
v = Object.assign(v, view);
@ -133,11 +133,11 @@ export const useTagsViewStore = defineStore('tagsView', () => {
};
const delRightTags = (view: TagView): Promise<TagView[]> => {
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) {
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)) {
return true;
}
@ -152,11 +152,11 @@ export const useTagsViewStore = defineStore('tagsView', () => {
};
const delLeftTags = (view: TagView): Promise<TagView[]> => {
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) {
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)) {
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;
if (!viewName) return;
if (cachedViews.value.includes(viewName)) return;

View File

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

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

@ -1,8 +1,9 @@
declare module '*.vue' {
import { DefineComponent } from 'vue';
const component: DefineComponent<{}, {}, any>;
export default component;
const Component: DefineComponent<{}, {}, any>;
export default Component;
}
declare module '*.avif' {
const src: string;
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 {
/** vue Instance */
@ -87,5 +87,76 @@ declare global {
pageNum: 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 {};

56
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 type RouteOption = {
hidden?: boolean;
permissions?: string[];
roles?: string[];
component?: any;
children?: RouteOption[];
alwaysShow?: boolean;
parentPath?: string;
meta?: {
title: string;
icon: string;
};
query?: string;
} & RouteRecordRaw;
declare interface _RouteLocationBase {
children?: RouteOption[];
}
declare interface RouteLocationOptions {
fullPath?: string;
}
declare interface TagView extends Partial<_RouteLocationBase> {
title?: string;
meta?: {
interface RouteMeta extends VRouteMeta {
link?: string;
title?: string;
affix?: boolean;
noCache?: boolean;
};
activeMenu?: string;
icon?: string;
breadcrumb?: boolean;
}
interface _RouteRecordBase {
hidden?: boolean | string | number;
permissions?: string[];
roles?: string[];
alwaysShow?: boolean;
query?: string;
}
interface _RouteLocationBase {
children?: _RouteRecordBase[];
path?: string;
}
interface TagView {
fullPath?: string;
name?: string;
path?: string;
title?: string;
meta?: RouteMeta;
query?: LocationQuery;
}
}

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 & {
readonly style: VueTypeValidableDef<CSSProperties>;
readonly fieldOption: VueTypeValidableDef<Array<FieldOption>>;
};
const propTypes = createTypes({

View File

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

View File

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

View File

@ -1,9 +1,9 @@
<template>
<div class="p-2">
<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-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-input v-model="queryParams.deptId" placeholder="请输入部门id" clearable style="width: 240px" @keyup.enter="handleQuery" />
</el-form-item>
@ -32,26 +32,26 @@
<template #header>
<el-row :gutter="10" class="mb8">
<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 :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 :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-col>
<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>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
<right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" :data="demoList" @selection-change="handleSelectionChange">
<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="userId" />
<el-table-column label="排序号" align="center" prop="orderNum" />
@ -60,19 +60,19 @@
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<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 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>
</template>
</el-table-column>
</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-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-item label="部门id" prop="deptId">
<el-input v-model="form.deptId" placeholder="请输入部门id" />
@ -129,8 +129,8 @@ const initFormData: DemoForm = {
userId: undefined,
orderNum: undefined,
testKey: undefined,
value: undefined,
}
value: undefined
};
const data = reactive<PageData<DemoForm, DemoQuery>>({
form: { ...initFormData },
queryParams: {
@ -140,27 +140,15 @@ const data = reactive<PageData<DemoForm, DemoQuery>>({
userId: undefined,
orderNum: undefined,
testKey: undefined,
value: undefined,
value: undefined
},
rules: {
id: [
{ required: true, message: "主键不能为空", trigger: "blur" }
],
deptId: [
{ required: true, message: "部门id不能为空", 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" }
],
id: [{ required: true, message: '主键不能为空', trigger: 'blur' }],
deptId: [{ required: true, message: '部门id不能为空', 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;
total.value = res.total;
loading.value = false;
}
};
/** 取消按钮 */
const cancel = () => {
reset();
dialog.visible = false;
}
};
/** 表单重置 */
const reset = () => {
form.value = { ...initFormData };
demoFormRef.value?.resetFields();
}
};
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
}
};
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
handleQuery();
}
};
/** 多选框选中数据 */
const handleSelectionChange = (selection: DemoVO[]) => {
ids.value = selection.map(item => item.id);
ids.value = selection.map((item) => item.id);
single.value = selection.length != 1;
multiple.value = !selection.length;
}
};
/** 新增按钮操作 */
const handleAdd = () => {
reset();
dialog.visible = true;
dialog.title = "添加测试单";
}
dialog.title = '添加测试单';
};
/** 修改按钮操作 */
const handleUpdate = async (row?: DemoVO) => {
reset();
const _id = row?.id || ids.value[0]
const _id = row?.id || ids.value[0];
const res = await getDemo(_id);
Object.assign(form.value, res.data);
dialog.visible = true;
dialog.title = "修改测试单";
}
dialog.title = '修改测试单';
};
/** 提交按钮 */
const submitForm = () => {
@ -229,32 +217,36 @@ const submitForm = () => {
if (valid) {
buttonLoading.value = true;
if (form.value.id) {
await updateDemo(form.value).finally(() => buttonLoading.value = false);
await updateDemo(form.value).finally(() => (buttonLoading.value = false));
} 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;
await getList();
}
});
}
};
/** 删除按钮操作 */
const handleDelete = async (row?: DemoVO) => {
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);
proxy?.$modal.msgSuccess("删除成功");
proxy?.$modal.msgSuccess('删除成功');
await getList();
}
};
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download('demo/demo/export', {
proxy?.download(
'demo/demo/export',
{
...queryParams.value
}, `demo_${new Date().getTime()}.xlsx`)
}
},
`demo_${new Date().getTime()}.xlsx`
);
};
onMounted(() => {
getList();

View File

@ -1,9 +1,9 @@
<template>
<div class="p-2">
<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-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-input v-model="queryParams.treeName" placeholder="请输入树节点名" clearable style="width: 240px" @keyup.enter="handleQuery" />
</el-form-item>
@ -20,21 +20,21 @@
<template #header>
<el-row :gutter="10" class="mb8">
<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 :span="1.5">
<el-button type="info" plain icon="Sort" @click="handleToggleExpandAll">展开/折叠</el-button>
</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>
</template>
<el-table
ref="treeTableRef"
v-loading="loading"
:data="treeList"
row-key="id"
:default-expand-all="isExpandAll"
:tree-props="{children: 'children', hasChildren: 'hasChildren'}"
ref="treeTableRef"
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
>
<el-table-column label="父id" align="center" prop="parentId" />
<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">
<template #default="scope">
<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 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 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>
</template>
</el-table-column>
</el-table>
</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-item label="父id" prop="parentId">
<el-tree-select
@ -89,18 +89,16 @@
</template>
<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';
type TreeOption = {
id: number;
treeName: string;
children?: TreeOption[];
}
const { proxy } = getCurrentInstance() as ComponentInternalInstance;;
};
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const treeList = ref<TreeVO[]>([]);
const treeOptions = ref<TreeOption[]>([]);
@ -111,46 +109,35 @@ const loading = ref(false);
const queryFormRef = ref<ElFormInstance>();
const treeFormRef = ref<ElFormInstance>();
const treeTableRef = ref<ElTableInstance>()
const treeTableRef = ref<ElTableInstance>();
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
const initFormData: TreeForm = {
id: undefined,
parentId: undefined,
deptId: undefined,
userId: undefined,
treeName: undefined,
}
treeName: undefined
};
const data = reactive<PageData<TreeForm, TreeQuery>>({
form: {...initFormData},
form: { ...initFormData },
queryParams: {
parentId: undefined,
deptId: undefined,
userId: undefined,
treeName: undefined,
treeName: undefined
},
rules: {
id: [
{ required: true, message: "主键不能为空", trigger: "blur" }
],
parentId: [
{ required: true, message: "父id不能为空", trigger: "blur" }
],
deptId: [
{ required: true, message: "部门id不能为空", trigger: "blur" }
],
userId: [
{ required: true, message: "用户id不能为空", trigger: "blur" }
],
treeName: [
{ required: true, message: "值不能为空", trigger: "blur" }
],
id: [{ required: true, message: '主键不能为空', trigger: 'blur' }],
parentId: [{ required: true, message: '父id不能为空', 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 () => {
loading.value = true;
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) {
treeList.value = data;
loading.value = false;
}
}
};
/** 查询测试树下拉树结构 */
const getTreeselect = async () => {
const res = await listTree();
treeOptions.value = [];
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);
}
};
// 取消按钮
const cancel = () => {
reset();
dialog.visible = false;
}
};
// 表单重置
const reset = () => {
form.value = {...initFormData}
form.value = { ...initFormData };
treeFormRef.value?.resetFields();
}
};
/** 搜索按钮操作 */
const handleQuery = () => {
getList();
}
};
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
handleQuery();
}
};
/** 新增按钮操作 */
const handleAdd = (row?: TreeVO) => {
@ -209,22 +196,22 @@ const handleAdd = (row?: TreeVO) => {
form.value.parentId = 0;
}
dialog.visible = true;
dialog.title = "添加测试树";
}
dialog.title = '添加测试树';
};
/** 展开/折叠操作 */
const handleToggleExpandAll = () => {
isExpandAll.value = !isExpandAll.value;
toggleExpandAll(treeList.value, isExpandAll.value)
}
toggleExpandAll(treeList.value, isExpandAll.value);
};
/** 展开/折叠操作 */
const toggleExpandAll = (data: TreeVO[], status: boolean) => {
data.forEach((item) => {
treeTableRef.value?.toggleRowExpansion(item, status)
if (item.children && item.children.length > 0) toggleExpandAll(item.children, status)
})
}
treeTableRef.value?.toggleRowExpansion(item, status);
if (item.children && item.children.length > 0) toggleExpandAll(item.children, status);
});
};
/** 修改按钮操作 */
const handleUpdate = async (row: TreeVO) => {
@ -236,8 +223,8 @@ const handleUpdate = async (row: TreeVO) => {
const res = await getTree(row.id);
Object.assign(form.value, res.data);
dialog.visible = true;
dialog.title = "修改测试树";
}
dialog.title = '修改测试树';
};
/** 提交按钮 */
const submitForm = () => {
@ -245,25 +232,25 @@ const submitForm = () => {
if (valid) {
buttonLoading.value = true;
if (form.value.id) {
await updateTree(form.value).finally(() => buttonLoading.value = false);
await updateTree(form.value).finally(() => (buttonLoading.value = false));
} 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;
await getList();
}
});
}
};
/** 删除按钮操作 */
const handleDelete = async (row: TreeVO) => {
await proxy?.$modal.confirm('是否确认删除测试树编号为"' + row.id + '"的数据项?');
loading.value = true;
await delTree(row.id).finally(() => loading.value = false);
await delTree(row.id).finally(() => (loading.value = false));
await getList();
proxy?.$modal.msgSuccess("删除成功");
}
proxy?.$modal.msgSuccess('删除成功');
};
onMounted(() => {
getList();

View File

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

View File

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

View File

@ -99,13 +99,13 @@
import { initWebSocket } from '@/utils/websocket';
onMounted(() => {
let protocol = window.location.protocol === 'https:' ? 'wss://' : 'ws://'
initWebSocket(protocol + window.location.host + import.meta.env.VITE_APP_BASE_API + "/resource/websocket");
let protocol = window.location.protocol === 'https:' ? 'wss://' : 'ws://';
initWebSocket(protocol + window.location.host + import.meta.env.VITE_APP_BASE_API + '/resource/websocket');
});
const goTarget = (url:string) => {
window.open(url, '__blank')
}
const goTarget = (url: string) => {
window.open(url, '__blank');
};
</script>
<style scoped lang="scss">
@ -131,7 +131,7 @@ const goTarget = (url:string) => {
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;
color: #676a6c;
overflow-x: hidden;

View File

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

View File

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

View File

@ -1,14 +1,14 @@
<template>
<div class="p-2">
<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-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-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 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 label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="登录状态" clearable style="width: 240px">
@ -39,22 +39,22 @@
<template #header>
<el-row :gutter="10" class="mb8">
<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-col>
<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 :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-col>
<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>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
<right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
</el-row>
</template>
@ -62,8 +62,8 @@
ref="loginInfoTableRef"
v-loading="loading"
:data="loginInfoList"
@selection-change="handleSelectionChange"
:default-sort="defaultSort"
@selection-change="handleSelectionChange"
@sort-change="handleSortChange"
>
<el-table-column type="selection" width="55" align="center" />
@ -99,18 +99,18 @@
</el-table-column>
</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>
</div>
</template>
<script setup name="Logininfor" lang="ts">
import { list, delLoginInfo, cleanLoginInfo, unlockLoginInfo } from "@/api/monitor/loginInfo";
import { LoginInfoQuery, LoginInfoVO } from "@/api/monitor/loginInfo/types";
import { list, delLoginInfo, cleanLoginInfo, unlockLoginInfo } from '@/api/monitor/loginInfo';
import { LoginInfoQuery, LoginInfoVO } from '@/api/monitor/loginInfo/types';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { sys_device_type } = toRefs<any>(proxy?.useDict("sys_device_type"));
const { sys_common_status } = toRefs<any>(proxy?.useDict("sys_common_status"));
const { sys_device_type } = toRefs<any>(proxy?.useDict('sys_device_type'));
const { sys_common_status } = toRefs<any>(proxy?.useDict('sys_common_status'));
const loginInfoList = ref<LoginInfoVO[]>([]);
const loading = ref(true);
@ -120,8 +120,8 @@ const single = ref(true);
const multiple = ref(true);
const selectName = ref<Array<string>>([]);
const total = ref(0);
const dateRange = ref<[DateModelType,DateModelType]>(['', '']);
const defaultSort = ref<any>({ prop: "loginTime", order: "descending" });
const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
const defaultSort = ref<any>({ prop: 'loginTime', order: 'descending' });
const queryFormRef = ref<ElFormInstance>();
const loginInfoTableRef = ref<ElTableInstance>();
@ -143,62 +143,66 @@ const getList = async () => {
loginInfoList.value = res.rows;
total.value = res.total;
loading.value = false;
}
};
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
}
};
/** 重置按钮操作 */
const resetQuery = () => {
dateRange.value = ['', ''];
queryFormRef.value?.resetFields();
queryParams.value.pageNum = 1;
loginInfoTableRef.value?.sort(defaultSort.value.prop, defaultSort.value.order);
}
};
/** 多选框选中数据 */
const handleSelectionChange = (selection: LoginInfoVO[]) => {
ids.value = selection.map(item => item.infoId);
ids.value = selection.map((item) => item.infoId);
multiple.value = !selection.length;
single.value = selection.length != 1;
selectName.value = selection.map(item => item.userName);
}
selectName.value = selection.map((item) => item.userName);
};
/** 排序触发事件 */
const handleSortChange = (column: any) => {
queryParams.value.orderByColumn = column.prop;
queryParams.value.isAsc = column.order;
getList();
}
};
/** 删除按钮操作 */
const handleDelete = async (row?: LoginInfoVO) => {
const infoIds = row?.infoId || ids.value;
await proxy?.$modal.confirm('是否确认删除访问编号为"' + infoIds + '"的数据项?');
await delLoginInfo(infoIds);
await getList();
proxy?.$modal.msgSuccess("删除成功");
}
proxy?.$modal.msgSuccess('删除成功');
};
/** 清空按钮操作 */
const handleClean = async () => {
await proxy?.$modal.confirm("是否确认清空所有登录日志数据项?");
await proxy?.$modal.confirm('是否确认清空所有登录日志数据项?');
await cleanLoginInfo();
await getList();
proxy?.$modal.msgSuccess("清空成功");
}
proxy?.$modal.msgSuccess('清空成功');
};
/** 解锁按钮操作 */
const handleUnlock = async () => {
const username = selectName.value;
await proxy?.$modal.confirm('是否确认解锁用户"' + username + '"数据项?');
await unlockLoginInfo(username);
proxy?.$modal.msgSuccess("用户" + username + "解锁成功");
}
proxy?.$modal.msgSuccess('用户' + username + '解锁成功');
};
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download("monitor/logininfor/export", {
...queryParams.value,
}, `config_${new Date().getTime()}.xlsx`);
}
proxy?.download(
'monitor/logininfor/export',
{
...queryParams.value
},
`config_${new Date().getTime()}.xlsx`
);
};
onMounted(() => {
getList();
})
});
</script>

View File

@ -2,7 +2,7 @@
<div class="p-2">
<div class="mb-[10px]">
<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-input v-model="queryParams.ipaddr" placeholder="请输入登录地址" clearable style="width: 200px" @keyup.enter="handleQuery" />
</el-form-item>
@ -20,7 +20,7 @@
<el-table
v-loading="loading"
: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">
<template #default="scope">
@ -48,14 +48,14 @@
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<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-tooltip>
</template>
</el-table-column>
</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>
</div>
</template>
@ -67,7 +67,7 @@ import api from "@/api/system/user";
import {to} from "await-to-js";
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 loading = ref(true);
@ -89,17 +89,17 @@ const getList = async () => {
onlineList.value = res.rows;
total.value = res.total;
loading.value = false;
}
};
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
}
};
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
handleQuery();
}
};
/** 强退按钮操作 */
const handleForceLogout = async (row: OnlineVO) => {
const [err] = await to(proxy?.$modal.confirm('是否确认强退名称为"' + row.userName + '"的用户?') as any);
@ -112,5 +112,5 @@ const handleForceLogout = async (row: OnlineVO) => {
onMounted(() => {
getList();
})
});
</script>

View File

@ -3,15 +3,15 @@
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="mb-[10px]">
<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-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 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 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 label="类型" prop="businessType">
<el-select v-model="queryParams.businessType" placeholder="操作类型" clearable style="width: 240px">
@ -47,17 +47,17 @@
<template #header>
<el-row :gutter="10" class="mb8">
<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-col>
<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 :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>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
<right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
</el-row>
</template>
@ -65,8 +65,8 @@
ref="operLogTableRef"
v-loading="loading"
:data="operlogList"
@selection-change="handleSelectionChange"
:default-sort="defaultSort"
@selection-change="handleSelectionChange"
@sort-change="handleSortChange"
>
<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">
<template #default="scope">
<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>
</template>
</el-table-column>
</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-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-row>
<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 :span="12">
<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-col>
<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-row>
</el-form>
@ -175,7 +175,7 @@ import { list, delOperlog, cleanOperlog } from '@/api/monitor/operlog';
import { OperLogForm, OperLogQuery, OperLogVO } from '@/api/monitor/operlog/types';
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 loading = ref(true);
@ -184,7 +184,7 @@ const ids = ref<Array<number | string>>([]);
const multiple = ref(true);
const total = ref(0);
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 queryFormRef = ref<ElFormInstance>();
@ -194,7 +194,6 @@ const dialog = reactive<DialogOption>({
title: ''
});
const data = reactive<PageData<OperLogForm, OperLogQuery>>({
form: {
operId: undefined,
@ -240,63 +239,67 @@ const getList = async () => {
operlogList.value = res.rows;
total.value = res.total;
loading.value = false;
}
};
/** 操作日志类型字典翻译 */
const typeFormat = (row: OperLogForm) => {
return proxy?.selectDictLabel(sys_oper_type.value, row.businessType);
}
};
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
}
};
/** 重置按钮操作 */
const resetQuery = () => {
dateRange.value = ['', ''];
queryFormRef.value?.resetFields();
queryParams.value.pageNum = 1;
operLogTableRef.value?.sort(defaultSort.value.prop, defaultSort.value.order);
}
};
/** 多选框选中数据 */
const handleSelectionChange = (selection: OperLogVO[]) => {
ids.value = selection.map(item => item.operId);
ids.value = selection.map((item) => item.operId);
multiple.value = !selection.length;
}
};
/** 排序触发事件 */
const handleSortChange = (column: any) => {
queryParams.value.orderByColumn = column.prop;
queryParams.value.isAsc = column.order;
getList();
}
};
/** 详细按钮操作 */
const handleView = (row: OperLogVO) => {
dialog.visible = true;
form.value = row;
}
};
/** 删除按钮操作 */
const handleDelete = async (row?: OperLogVO) => {
const operIds = row?.operId || ids.value;
await proxy?.$modal.confirm('是否确认删除日志编号为"' + operIds + '"的数据项?');
await delOperlog(operIds);
await getList();
proxy?.$modal.msgSuccess("删除成功");
}
proxy?.$modal.msgSuccess('删除成功');
};
/** 清空按钮操作 */
const handleClean = async () => {
await proxy?.$modal.confirm("是否确认清空所有操作日志数据项?");
await proxy?.$modal.confirm('是否确认清空所有操作日志数据项?');
await cleanOperlog();
await getList();
proxy?.$modal.msgSuccess("清空成功");
}
proxy?.$modal.msgSuccess('清空成功');
};
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download("monitor/operlog/export", {
...queryParams.value,
}, `config_${new Date().getTime()}.xlsx`);
}
proxy?.download(
'monitor/operlog/export',
{
...queryParams.value
},
`config_${new Date().getTime()}.xlsx`
);
};
onMounted(() => {
getList();
})
});
</script>

View File

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

View File

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

View File

@ -1,8 +1,8 @@
<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="search" v-show="showSearch">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="100px">
<div v-show="showSearch" class="search">
<el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="100px">
<el-form-item label="客户端key" prop="clientKey">
<el-input v-model="queryParams.clientKey" placeholder="请输入客户端key" clearable style="width: 240px" @keyup.enter="handleQuery" />
</el-form-item>
@ -26,28 +26,28 @@
<template #header>
<el-row :gutter="10" class="mb8">
<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 :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-col>
<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-col>
<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>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
<right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" :data="clientList" @selection-change="handleSelectionChange">
<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="客户端key" align="center" prop="clientKey" />
<el-table-column label="客户端秘钥" align="center" prop="clientSecret" />
@ -63,7 +63,7 @@
</el-table-column>
<el-table-column label="Token活跃超时时间" align="center" prop="activeTimeout" />
<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">
<el-switch v-model="scope.row.status" active-value="0" inactive-value="1" @change="handleStatusChange(scope.row)"></el-switch>
</template>
@ -71,19 +71,19 @@
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<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 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>
</template>
</el-table-column>
</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-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-item label="客户端key" prop="clientKey">
<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';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { sys_normal_disable } = toRefs<any>(proxy?.useDict("sys_normal_disable"));
const { sys_grant_type } = toRefs<any>(proxy?.useDict("sys_grant_type"));
const { sys_device_type } = toRefs<any>(proxy?.useDict("sys_device_type"));
const { sys_normal_disable } = toRefs<any>(proxy?.useDict('sys_normal_disable'));
const { sys_grant_type } = toRefs<any>(proxy?.useDict('sys_grant_type'));
const { sys_device_type } = toRefs<any>(proxy?.useDict('sys_device_type'));
const clientList = ref<ClientVO[]>([]);
const buttonLoading = ref(false);
@ -176,10 +176,10 @@ const initFormData: ClientForm = {
deviceType: undefined,
activeTimeout: undefined,
timeout: undefined,
status: undefined,
}
status: undefined
};
const data = reactive<PageData<ClientForm, ClientQuery>>({
form: {...initFormData},
form: { ...initFormData },
queryParams: {
pageNum: 1,
pageSize: 10,
@ -190,27 +190,15 @@ const data = reactive<PageData<ClientForm, ClientQuery>>({
deviceType: undefined,
activeTimeout: undefined,
timeout: undefined,
status: undefined,
status: undefined
},
rules: {
id: [
{ required: true, message: "id不能为空", trigger: "blur" }
],
clientId: [
{ required: true, message: "客户端id不能为空", trigger: "blur" }
],
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" }
],
id: [{ required: true, message: 'id不能为空', trigger: 'blur' }],
clientId: [{ required: true, message: '客户端id不能为空', trigger: 'blur' }],
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;
total.value = res.total;
loading.value = false;
}
};
/** 取消按钮 */
const cancel = () => {
reset();
dialog.visible = false;
}
};
/** 表单重置 */
const reset = () => {
form.value = {...initFormData};
form.value = { ...initFormData };
clientFormRef.value?.resetFields();
}
};
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
}
};
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
handleQuery();
}
};
/** 多选框选中数据 */
const handleSelectionChange = (selection: ClientVO[]) => {
ids.value = selection.map(item => item.id);
ids.value = selection.map((item) => item.id);
single.value = selection.length != 1;
multiple.value = !selection.length;
}
};
/** 新增按钮操作 */
const handleAdd = () => {
reset();
dialog.visible = true;
dialog.title = "添加客户端管理";
}
dialog.title = '添加客户端管理';
};
/** 修改按钮操作 */
const handleUpdate = async (row?: ClientVO) => {
reset();
const _id = row?.id || ids.value[0]
const _id = row?.id || ids.value[0];
const res = await getClient(_id);
Object.assign(form.value, res.data);
dialog.visible = true;
dialog.title = "修改客户端管理";
}
dialog.title = '修改客户端管理';
};
/** 提交按钮 */
const submitForm = () => {
@ -279,44 +267,48 @@ const submitForm = () => {
if (valid) {
buttonLoading.value = true;
if (form.value.id) {
await updateClient(form.value).finally(() => buttonLoading.value = false);
await updateClient(form.value).finally(() => (buttonLoading.value = false));
} 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;
await getList();
}
});
}
};
/** 删除按钮操作 */
const handleDelete = async (row?: ClientVO) => {
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);
proxy?.$modal.msgSuccess("删除成功");
proxy?.$modal.msgSuccess('删除成功');
await getList();
}
};
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download('system/client/export', {
proxy?.download(
'system/client/export',
{
...queryParams.value
}, `client_${new Date().getTime()}.xlsx`)
}
},
`client_${new Date().getTime()}.xlsx`
);
};
/** 状态修改 */
const handleStatusChange = async (row: ClientVO) => {
let text = row.status === "0" ? "启用" : "停用"
let text = row.status === '0' ? '启用' : '停用';
try {
await proxy?.$modal.confirm('确认要"' + text + '"吗?');
await changeStatus(row.id, row.status);
proxy?.$modal.msgSuccess(text + "成功");
proxy?.$modal.msgSuccess(text + '成功');
} catch (err) {
row.status = row.status === "0" ? "1" : "0";
row.status = row.status === '0' ? '1' : '0';
}
}
};
onMounted(() => {
getList();

View File

@ -1,9 +1,9 @@
<template>
<div class="p-2">
<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-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-input v-model="queryParams.configName" placeholder="请输入参数名称" clearable style="width: 240px" @keyup.enter="handleQuery" />
</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-select>
</el-form-item>
<el-form-item label="创建时间" style="width: 308px;">
<el-form-item label="创建时间" style="width: 308px">
<el-date-picker
v-model="dateRange"
value-format="YYYY-MM-DD HH:mm:ss"
@ -38,31 +38,31 @@
<template #header>
<el-row :gutter="10" class="mb8">
<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 :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-col>
<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-col>
<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 :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>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
<right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" :data="configList" @selection-change="handleSelectionChange">
<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="configKey" :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">
<template #default="scope">
<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 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>
</template>
</el-table-column>
</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-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-item label="参数名称" prop="configName">
<el-input v-model="form.configName" placeholder="请输入参数名称" />
@ -123,11 +123,11 @@
</template>
<script setup name="Config" lang="ts">
import { listConfig, getConfig, delConfig, addConfig, updateConfig, refreshCache } from "@/api/system/config";
import { ConfigForm, ConfigQuery, ConfigVO } from "@/api/system/config/types";
import { listConfig, getConfig, delConfig, addConfig, updateConfig, refreshCache } from '@/api/system/config';
import { ConfigForm, ConfigQuery, ConfigVO } from '@/api/system/config/types';
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 loading = ref(true);
@ -149,9 +149,9 @@ const initFormData: ConfigForm = {
configName: '',
configKey: '',
configValue: '',
configType: "Y",
configType: 'Y',
remark: ''
}
};
const data = reactive<PageData<ConfigForm, ConfigQuery>>({
form: { ...initFormData },
queryParams: {
@ -159,12 +159,12 @@ const data = reactive<PageData<ConfigForm, ConfigQuery>>({
pageSize: 10,
configName: '',
configKey: '',
configType: '',
configType: ''
},
rules: {
configName: [{ required: true, message: "参数名称不能为空", trigger: "blur" }],
configKey: [{ required: true, message: "参数键名不能为空", trigger: "blur" }],
configValue: [{ required: true, message: "参数键值不能为空", trigger: "blur" }]
configName: [{ required: true, message: '参数名称不能为空', trigger: 'blur' }],
configKey: [{ required: true, message: '参数键名不能为空', trigger: 'blur' }],
configValue: [{ required: true, message: '参数键值不能为空', trigger: 'blur' }]
}
});
@ -177,40 +177,40 @@ const getList = async () => {
configList.value = res.rows;
total.value = res.total;
loading.value = false;
}
};
/** 取消按钮 */
const cancel = () => {
reset();
dialog.visible = false;
}
};
/** 表单重置 */
const reset = () => {
form.value = { ...initFormData };
configFormRef.value?.resetFields();
}
};
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
}
};
/** 重置按钮操作 */
const resetQuery = () => {
dateRange.value = ['', ''];
queryFormRef.value?.resetFields();
handleQuery();
}
};
/** 多选框选中数据 */
const handleSelectionChange = (selection: ConfigVO[]) => {
ids.value = selection.map(item => item.configId);
ids.value = selection.map((item) => item.configId);
single.value = selection.length != 1;
multiple.value = !selection.length;
}
};
/** 新增按钮操作 */
const handleAdd = () => {
reset();
dialog.visible = true;
dialog.title = "添加参数";
}
dialog.title = '添加参数';
};
/** 修改按钮操作 */
const handleUpdate = async (row?: ConfigVO) => {
reset();
@ -218,40 +218,44 @@ const handleUpdate = async (row?: ConfigVO) => {
const res = await getConfig(configId);
Object.assign(form.value, res.data);
dialog.visible = true;
dialog.title = "修改参数";
}
dialog.title = '修改参数';
};
/** 提交按钮 */
const submitForm = () => {
configFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
form.value.configId ? await updateConfig(form.value) : await addConfig(form.value);
proxy?.$modal.msgSuccess("操作成功");
proxy?.$modal.msgSuccess('操作成功');
dialog.visible = false;
await getList();
}
});
}
};
/** 删除按钮操作 */
const handleDelete = async (row?: ConfigVO) => {
const configIds = row?.configId || ids.value;
await proxy?.$modal.confirm('是否确认删除参数编号为"' + configIds + '"的数据项?');
await delConfig(configIds);
await getList();
proxy?.$modal.msgSuccess("删除成功");
}
proxy?.$modal.msgSuccess('删除成功');
};
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download("system/config/export", {
proxy?.download(
'system/config/export',
{
...queryParams.value
}, `config_${new Date().getTime()}.xlsx`);
}
},
`config_${new Date().getTime()}.xlsx`
);
};
/** 刷新缓存按钮操作 */
const handleRefreshCache = async () => {
await refreshCache();
proxy?.$modal.msgSuccess("刷新缓存成功");
}
proxy?.$modal.msgSuccess('刷新缓存成功');
};
onMounted(() => {
getList();
})
});
</script>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,9 +1,9 @@
<template>
<div class="p-2">
<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-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-input v-model="queryParams.postCode" placeholder="请输入岗位编码" clearable style="width: 200px" @keyup.enter="handleQuery" />
</el-form-item>
@ -27,26 +27,26 @@
<template #header>
<el-row :gutter="10" class="mb8">
<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 :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 :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-col>
<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>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
<right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" :data="postList" @selection-change="handleSelectionChange">
<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="postName" />
<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">
<template #default="scope">
<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 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>
</template>
</el-table-column>
</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-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-item label="岗位名称" prop="postName">
<el-input v-model="form.postName" placeholder="请输入岗位名称" />
@ -107,11 +107,11 @@
</template>
<script setup name="Post" lang="ts">
import { listPost, addPost, delPost, getPost, updatePost } from "@/api/system/post";
import { PostForm, PostQuery, PostVO } from "@/api/system/post/types";
import { listPost, addPost, delPost, getPost, updatePost } from '@/api/system/post';
import { PostForm, PostQuery, PostVO } from '@/api/system/post/types';
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 loading = ref(true);
@ -134,9 +134,9 @@ const initFormData: PostForm = {
postCode: '',
postName: '',
postSort: 0,
status: "0",
status: '0',
remark: ''
}
};
const data = reactive<PageData<PostForm, PostQuery>>({
form: { ...initFormData },
@ -148,9 +148,9 @@ const data = reactive<PageData<PostForm, PostQuery>>({
status: ''
},
rules: {
postName: [{ required: true, message: "岗位名称不能为空", trigger: "blur" }],
postCode: [{ required: true, message: "岗位编码不能为空", trigger: "blur" }],
postSort: [{ required: true, message: "岗位顺序不能为空", trigger: "blur" }],
postName: [{ required: true, message: '岗位名称不能为空', trigger: 'blur' }],
postCode: [{ required: true, message: '岗位编码不能为空', trigger: 'blur' }],
postSort: [{ required: true, message: '岗位顺序不能为空', trigger: 'blur' }]
}
});
@ -163,39 +163,39 @@ const getList = async () => {
postList.value = res.rows;
total.value = res.total;
loading.value = false;
}
};
/** 取消按钮 */
const cancel = () => {
reset();
dialog.visible = false;
}
};
/** 表单重置 */
const reset = () => {
form.value = { ...initFormData };
postFormRef.value?.resetFields();
}
};
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
}
};
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
handleQuery();
}
};
/** 多选框选中数据 */
const handleSelectionChange = (selection: PostVO[]) => {
ids.value = selection.map(item => item.postId);
ids.value = selection.map((item) => item.postId);
single.value = selection.length != 1;
multiple.value = !selection.length;
}
};
/** 新增按钮操作 */
const handleAdd = () => {
reset();
dialog.visible = true;
dialog.title = "添加岗位";
}
dialog.title = '添加岗位';
};
/** 修改按钮操作 */
const handleUpdate = async (row?: PostVO) => {
reset();
@ -203,33 +203,37 @@ const handleUpdate = async (row?: PostVO) => {
const res = await getPost(postId);
Object.assign(form.value, res.data);
dialog.visible = true;
dialog.title = "修改岗位";
}
dialog.title = '修改岗位';
};
/** 提交按钮 */
const submitForm = () => {
postFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
form.value.postId ? await updatePost(form.value) : await addPost(form.value);
proxy?.$modal.msgSuccess("操作成功");
proxy?.$modal.msgSuccess('操作成功');
dialog.visible = false;
await getList();
}
});
}
};
/** 删除按钮操作 */
const handleDelete = async (row?: PostVO) => {
const postIds = row?.postId || ids.value;
await proxy?.$modal.confirm('是否确认删除岗位编号为"' + postIds + '"的数据项?');
await delPost(postIds);
await getList();
proxy?.$modal.msgSuccess("删除成功");
}
proxy?.$modal.msgSuccess('删除成功');
};
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download("system/post/export", {
proxy?.download(
'system/post/export',
{
...queryParams.value
}, `post_${new Date().getTime()}.xlsx`);
}
},
`post_${new Date().getTime()}.xlsx`
);
};
onMounted(() => {
getList();

View File

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

View File

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

View File

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

View File

@ -1,9 +1,9 @@
<template>
<div class="p-2">
<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-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-input v-model="queryParams.tenantId" placeholder="请输入租户编号" clearable style="width: 240px" @keyup.enter="handleQuery" />
</el-form-item>
@ -29,28 +29,28 @@
<template #header>
<el-row :gutter="10" class="mb8">
<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 :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-col>
<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-col>
<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>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
<right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" :data="tenantList" @selection-change="handleSelectionChange">
<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="contactUserName" />
<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">
<template #default="scope">
<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 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-tooltip>
<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>
</template>
</el-table-column>
</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-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-item label="企业名称" prop="companyName">
<el-input v-model="form.companyName" placeholder="请输入企业名称" />
@ -100,7 +100,7 @@
<el-input v-model="form.username" placeholder="请输入系统用户名" maxlength="30" />
</el-form-item>
<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 label="租户套餐" prop="packageId">
<el-select v-model="form.packageId" :disabled="!!form.tenantId" placeholder="请选择租户套餐" clearable style="width: 100%">
@ -108,7 +108,7 @@
</el-select>
</el-form-item>
<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-form-item>
<el-form-item label="用户数量" prop="accountCount">
@ -124,7 +124,7 @@
<el-input v-model="form.licenseNumber" placeholder="请输入统一社会信用代码" />
</el-form-item>
<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 label="备注" prop="remark">
<el-input v-model="form.remark" placeholder="请输入备注" />
@ -182,8 +182,8 @@ const initFormData: TenantForm = {
packageId: '',
expireTime: '',
accountCount: 0,
status: '0',
}
status: '0'
};
const data = reactive<PageData<TenantForm, TenantQuery>>({
form: { ...initFormData },
queryParams: {
@ -195,17 +195,17 @@ const data = reactive<PageData<TenantForm, TenantQuery>>({
companyName: ''
},
rules: {
id: [{ required: true, message: "id不能为空", trigger: "blur" }],
tenantId: [{ required: true, message: "租户编号不能为空", trigger: "blur" }],
contactUserName: [{ required: true, message: "联系人不能为空", trigger: "blur" }],
contactPhone: [{ required: true, message: "联系电话不能为空", trigger: "blur" }],
companyName: [{ required: true, message: "企业名称不能为空", trigger: "blur" }],
id: [{ required: true, message: 'id不能为空', trigger: 'blur' }],
tenantId: [{ required: true, message: '租户编号不能为空', trigger: 'blur' }],
contactUserName: [{ required: true, message: '联系人不能为空', trigger: 'blur' }],
contactPhone: [{ required: true, message: '联系电话不能为空', trigger: 'blur' }],
companyName: [{ required: true, message: '企业名称不能为空', trigger: 'blur' }],
username: [
{ required: true, message: "用户名不能为空", trigger: "blur" },
{ required: true, message: '用户名不能为空', trigger: 'blur' },
{ min: 2, max: 20, message: '用户名称长度必须介于 2 和 20 之间', trigger: 'blur' }
],
password: [
{ required: true, message: "密码不能为空", trigger: "blur" },
{ required: true, message: '密码不能为空', 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 res = await selectTenantPackage()
const res = await selectTenantPackage();
packageList.value = res.data;
}
};
/** 查询租户列表 */
const getList = async () => {
@ -226,60 +226,58 @@ const getList = async () => {
tenantList.value = res.rows;
total.value = res.total;
loading.value = false;
}
};
// 租户套餐状态修改
const handleStatusChange = async (row: TenantVO) => {
let text = row.status === "0" ? "启用" : "停用";
let text = row.status === '0' ? '启用' : '停用';
try {
await proxy?.$modal.confirm('确认要"' + text + '""' + row.companyName + '"租户吗?');
await changeTenantStatus(row.id, row.tenantId, row.status);
proxy?.$modal.msgSuccess(text + "成功");
proxy?.$modal.msgSuccess(text + '成功');
} catch {
row.status = row.status === "0" ? "1" : "0";
row.status = row.status === '0' ? '1' : '0';
}
}
};
// 取消按钮
const cancel = () => {
reset();
dialog.visible = false;
}
};
// 表单重置
const reset = () => {
form.value = { ...initFormData };
tenantFormRef.value?.resetFields();
}
};
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
}
};
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
handleQuery();
}
};
// 多选框选中数据
const handleSelectionChange = (selection: TenantVO[]) => {
ids.value = selection.map(item => item.id);
ids.value = selection.map((item) => item.id);
single.value = selection.length != 1;
multiple.value = !selection.length;
}
};
/** 新增按钮操作 */
const handleAdd = () => {
reset();
getTenantPackage();
dialog.visible = true;
dialog.title = "添加租户";
}
dialog.title = '添加租户';
};
/** 修改按钮操作 */
const handleUpdate = async (row?: TenantVO) => {
@ -287,10 +285,10 @@ const handleUpdate = async (row?: TenantVO) => {
await getTenantPackage();
const _id = row?.id || ids.value[0];
const res = await getTenant(_id);
Object.assign(form.value, res.data)
Object.assign(form.value, res.data);
dialog.visible = true;
dialog.title = "修改租户";
}
dialog.title = '修改租户';
};
/** 提交按钮 */
const submitForm = () => {
@ -298,28 +296,26 @@ const submitForm = () => {
if (valid) {
buttonLoading.value = true;
if (form.value.id) {
await updateTenant(form.value).finally(() => buttonLoading.value = false);
await updateTenant(form.value).finally(() => (buttonLoading.value = false));
} 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;
await getList();
}
});
}
};
/** 删除按钮操作 */
const handleDelete = async (row?: TenantVO) => {
const _ids = row?.id || ids.value;
await proxy?.$modal.confirm('是否确认删除租户编号为"' + _ids + '"的数据项?')
await proxy?.$modal.confirm('是否确认删除租户编号为"' + _ids + '"的数据项?');
loading.value = true;
await delTenant(_ids).finally(() => loading.value = false);
await delTenant(_ids).finally(() => (loading.value = false));
await getList();
proxy?.$modal.msgSuccess("删除成功");
}
proxy?.$modal.msgSuccess('删除成功');
};
/** 同步租户套餐按钮操作 */
const handleSyncTenantPackage = async (row: TenantVO) => {
@ -328,20 +324,26 @@ const handleSyncTenantPackage = async (row: TenantVO) => {
loading.value = true;
await syncTenantPackage(row.tenantId, row.packageId);
await getList();
proxy?.$modal.msgSuccess("同步成功");
} catch { return } finally {
proxy?.$modal.msgSuccess('同步成功');
} catch {
return;
} finally {
loading.value = false;
}
}
};
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download('system/tenant/export', {
proxy?.download(
'system/tenant/export',
{
...queryParams.value
}, `tenant_${new Date().getTime()}.xlsx`)
}
},
`tenant_${new Date().getTime()}.xlsx`
);
};
onMounted(() => {
getList();
})
});
</script>

View File

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

View File

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

View File

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

View File

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

View File

@ -17,37 +17,43 @@
</template>
<script setup lang="ts">
import { updateUserPwd } from "@/api/system/user";
import type { ResetPwdForm } from "@/api/system/user/types";
import { updateUserPwd } from '@/api/system/user';
import type { ResetPwdForm } from '@/api/system/user/types';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const pwdRef = ref<ElFormInstance>();
const user = ref<ResetPwdForm>({
oldPassword: "",
newPassword: "",
confirmPassword: ""
oldPassword: '',
newPassword: '',
confirmPassword: ''
});
const equalToPassword = (rule: any, value: string, callback: any) => {
if (user.value.newPassword !== value) {
callback(new Error("两次输入的密码不一致"));
callback(new Error('两次输入的密码不一致'));
} else {
callback();
}
};
const rules = ref({
oldPassword: [{ required: true, message: "旧密码不能为空", trigger: "blur" }],
newPassword: [{ required: true, message: "新密码不能为空", trigger: "blur" }, {
oldPassword: [{ required: true, message: '旧密码不能为空', trigger: 'blur' }],
newPassword: [
{ required: true, message: '新密码不能为空', trigger: 'blur' },
{
min: 6,
max: 20,
message: "长度在 6 到 20 个字符",
trigger: "blur"
}],
confirmPassword: [{ required: true, message: "确认密码不能为空", trigger: "blur" }, {
message: '长度在 6 到 20 个字符',
trigger: 'blur'
}
],
confirmPassword: [
{ required: true, message: '确认密码不能为空', trigger: 'blur' },
{
required: true,
validator: equalToPassword,
trigger: "blur"
}]
trigger: 'blur'
}
]
});
/** 提交按钮 */
@ -55,7 +61,7 @@ const submit = () => {
pwdRef.value?.validate(async (valid: boolean) => {
if (valid) {
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="140" align="center" prop="source" show-overflow-tooltip />
<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" />
</template>
</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="createTime" />
<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>
</template>
</el-table-column>
@ -20,25 +20,25 @@
<div id="git-user-binding">
<h4 class="provider-desc">你可以绑定以下第三方帐号</h4>
<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">
<svg-icon icon-class="wechat" />
</div>
<span class="app-name">WeiXin</span>
</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">
<svg-icon icon-class="maxkey" />
</div>
<span class="app-name">MaxKey</span>
</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">
<svg-icon icon-class="gitee" />
</div>
<span class="app-name">Gitee</span>
</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">
<svg-icon icon-class="github" />
</div>
@ -50,31 +50,32 @@
</template>
<script lang="ts" setup>
import { authUnlock, authBinding } from "@/api/system/social/auth";
import { PropType } from "vue";
import { authUnlock, authBinding } from '@/api/system/social/auth';
import { PropType } from 'vue';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const props = defineProps({
auths: {
type: Object as PropType<any>,
type: Object as PropType<any>
}
});
const auths = computed(() => props.auths);
const unlockAuth = (row: any) => {
ElMessageBox.confirm('您确定要解除"' + row.source + '"的账号绑定吗?')
.then(() => {
return authUnlock(row.id);
}).then((res: any) => {
})
.then((res: any) => {
if (res.code === 200) {
proxy?.$modal.msgSuccess("解绑成功");
proxy?.$modal.msgSuccess('解绑成功');
proxy?.$tab.refreshPage();
} else {
proxy?.$modal.msgError(res.msg);
}
}).catch(() => { });
})
.catch(() => {});
};
const authUrl = (source: string) => {
@ -111,7 +112,7 @@ const authUrl = (source: string) => {
margin-top: 10px;
}
.git-other-login-icon>img {
.git-other-login-icon > img {
height: 32px;
}
@ -122,15 +123,13 @@ a {
}
.provider-desc {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial,
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Liberation Sans",
"PingFang SC", "Microsoft YaHei", "Hiragino Sans GB", "Wenquanyi Micro Hei",
"WenQuanYi Zen Hei", "ST Heiti", SimHei, SimSun, "WenQuanYi Zen Hei Sharp",
sans-serif;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
'Liberation Sans', 'PingFang SC', 'Microsoft YaHei', 'Hiragino Sans GB', 'Wenquanyi Micro Hei', 'WenQuanYi Zen Hei', 'ST Heiti', SimHei, SimSun,
'WenQuanYi Zen Hei Sharp', sans-serif;
font-size: 1.071rem;
}
td>img {
td > img {
height: 20px;
width: 20px;
display: inline-block;

View File

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

View File

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

View File

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

View File

@ -35,22 +35,22 @@
<el-table-column label="插入" min-width="5%">
<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>
</el-table-column>
<el-table-column label="编辑" min-width="5%">
<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>
</el-table-column>
<el-table-column label="列表" min-width="5%">
<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>
</el-table-column>
<el-table-column label="查询" min-width="5%">
<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>
</el-table-column>
<el-table-column label="查询方式" min-width="10%">
@ -69,7 +69,7 @@
</el-table-column>
<el-table-column label="必填" min-width="5%">
<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>
</el-table-column>
<el-table-column label="显示类型" min-width="12%">
@ -104,7 +104,7 @@
</el-tab-pane>
</el-tabs>
<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 @click="close()">返回</el-button>
</div>
@ -118,7 +118,7 @@ import { DbColumnVO, DbTableVO } from '@/api/tool/gen/types';
import { optionselect as getDictOptionselect } from '@/api/system/dict/type';
import { DictTypeVO } from '@/api/system/dict/type/types';
import BasicInfoForm from './basicInfoForm.vue';
import GenInfoForm from "./genInfoForm.vue";
import GenInfoForm from './genInfoForm.vue';
const route = useRoute();
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@ -138,8 +138,8 @@ const submitForm = () => {
const basicForm = basicInfo.value?.$refs.basicInfoForm;
const genForm = genInfo.value?.$refs.genInfoForm;
Promise.all([basicForm, genForm].map(getFormPromise)).then(async res => {
const validateResult = res.every(item => !!item);
Promise.all([basicForm, genForm].map(getFormPromise)).then(async (res) => {
const validateResult = res.every((item) => !!item);
if (validateResult) {
const genTable: any = Object.assign({}, info.value);
genTable.columns = columns.value;
@ -155,24 +155,24 @@ const submitForm = () => {
close();
}
} else {
proxy?.$modal.msgError("表单校验未通过,请重新检查提交内容");
proxy?.$modal.msgError('表单校验未通过,请重新检查提交内容');
}
});
}
};
const getFormPromise = (form: any) => {
return new Promise(resolve => {
return new Promise((resolve) => {
form.validate((res: any) => {
resolve(res);
});
});
}
};
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);
}
};
(async () => {
const tableId = route.params && route.params.tableId as string;
const tableId = route.params && (route.params.tableId as string);
if (tableId) {
// 获取表详细信息
const res = await getGenTable(tableId);

View File

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

View File

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

View File

@ -1,9 +1,9 @@
<template>
<div class="p-2">
<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-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-select v-model="queryParams.dataName" filterable clearable placeholder="请选择/输入数据源名称" style="width: 200px">
<el-option key="" label="全部" value="" />
@ -39,20 +39,20 @@
<template #header>
<el-row :gutter="10" class="mb8">
<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 :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 :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 :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-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
<right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
</el-row>
</template>
@ -72,28 +72,28 @@
<el-table-column label="操作" align="center" width="330" class-name="small-padding fixed-width">
<template #default="scope">
<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 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 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 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 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>
</template>
</el-table-column>
</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-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-tab-pane
v-for="(value, key) in preview.data"
@ -101,7 +101,7 @@
:name="key.substring(key.lastIndexOf('/') + 1, key.indexOf('.vm'))"
: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;复制
</el-link>
<pre>{{ value }}</pre>
@ -129,7 +129,7 @@ const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
const uniqueId = ref("");
const uniqueId = ref('');
const dataNameList = ref<Array<string>>([]);
const queryFormRef = ref<ElFormInstance>();
@ -140,13 +140,13 @@ const queryParams = ref<TableQuery>({
pageSize: 10,
tableName: '',
tableComment: '',
dataName: ""
})
dataName: ''
});
const preview = ref<any>({
data: {},
activeName: 'domain.java'
})
});
const dialog = reactive<DialogOption>({
visible: false,
title: '代码预览'
@ -161,13 +161,13 @@ onActivated(() => {
queryFormRef.value?.resetFields();
getList();
}
})
});
/** 查询多数据源名称 */
const getDataNameList = async () => {
const res = await getDataNames()
const res = await getDataNames();
dataNameList.value = res.data;
}
};
/** 查询表集合 */
const getList = async () => {
@ -176,65 +176,65 @@ const getList = async () => {
tableList.value = res.rows;
total.value = res.total;
loading.value = false;
}
};
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
}
};
/** 生成代码操作 */
const handleGenTable = async (row?: TableVO) => {
const tbIds = row?.tableId || ids.value;
if (tbIds == "") {
if (tbIds == '') {
proxy?.$modal.msgError('请选择要生成的数据');
return;
}
if (row?.genType === "1") {
if (row?.genType === '1') {
await genCode(row.tableId);
proxy?.$modal.msgSuccess('成功生成到自定义路径:' + row.genPath);
} else {
proxy?.$download.zip('/tool/gen/batchGenCode?tableIdStr=' + tbIds, 'ruoyi.zip');
}
}
};
/** 同步数据库操作 */
const handleSynchDb = async (row: TableVO) => {
const tableId = row.tableId;
await proxy?.$modal.confirm('确认要强制同步"' + row.tableName + '"表结构吗?');
await synchDb(tableId);
proxy?.$modal.msgSuccess('同步成功');
}
};
/** 打开导入表弹窗 */
const openImportTable = () => {
importRef.value?.show(queryParams.value.dataName);
}
};
/** 重置按钮操作 */
const resetQuery = () => {
dateRange.value = ['', ''];
queryFormRef.value?.resetFields();
handleQuery();
}
};
/** 预览按钮 */
const handlePreview = async (row: TableVO) => {
const res = await previewTable(row.tableId);
preview.value.data = res.data;
dialog.visible = true;
preview.value.activeName = 'domain.java';
}
};
/** 复制代码成功 */
const copyTextSuccess = () => {
proxy?.$modal.msgSuccess('复制成功');
}
};
// 多选框选中数据
const handleSelectionChange = (selection: TableVO[]) => {
ids.value = selection.map(item => item.tableId);
ids.value = selection.map((item) => item.tableId);
single.value = selection.length != 1;
multiple.value = !selection.length;
}
};
/** 修改按钮操作 */
const handleEditTable = (row?: TableVO) => {
const tableId = row?.tableId || ids.value[0];
router.push({ path: '/tool/gen-edit/index/' + tableId, query: { pageNum: queryParams.value.pageNum } });
}
};
/** 删除按钮操作 */
const handleDelete = async (row?: TableVO) => {
const tableIds = row?.tableId || ids.value;
@ -242,10 +242,10 @@ const handleDelete = async (row?: TableVO) => {
await delTable(tableIds);
await getList();
proxy?.$modal.msgSuccess('删除成功');
}
};
onMounted(() => {
getList();
getDataNameList();
})
});
</script>

View File

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