fix
This commit is contained in:
@@ -48,7 +48,7 @@
|
||||
:data="state.dataList"
|
||||
v-loading="state.loading"
|
||||
style="width: 100%"
|
||||
row-key="id"
|
||||
row-key="id"
|
||||
@selection-change="handleSelectionChange"
|
||||
@sort-change="sortChangeHandle"
|
||||
border
|
||||
@@ -109,7 +109,7 @@ const resetQuery = () => {
|
||||
|
||||
// 导出excel
|
||||
const exportExcel = () => {
|
||||
downBlobFile('/admin/audit/export', Object.assign(state.queryForm,{ids:selectObjs}), 'audit.xlsx');
|
||||
downBlobFile('/admin/audit/export', Object.assign(state.queryForm, { ids: selectObjs }), 'audit.xlsx');
|
||||
};
|
||||
|
||||
// 多选事件
|
||||
|
||||
@@ -164,7 +164,7 @@ const collapseActive = ref('1');
|
||||
// 定义校验规则
|
||||
const dataRules = ref({
|
||||
clientId: [
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{ required: true, message: '编号不能为空', trigger: 'blur' },
|
||||
{ validator: rule.validatorLowercase, trigger: 'blur' },
|
||||
{
|
||||
@@ -175,11 +175,14 @@ const dataRules = ref({
|
||||
},
|
||||
],
|
||||
clientSecret: [
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{ required: true, message: '密钥不能为空', trigger: 'blur' },
|
||||
{ validator: rule.validatorLower, trigger: 'blur' },
|
||||
],
|
||||
scope: [{ validator: rule.overLength, trigger: 'blur' },{ required: true, message: '域不能为空', trigger: 'blur' }],
|
||||
scope: [
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{ required: true, message: '域不能为空', trigger: 'blur' },
|
||||
],
|
||||
authorizedGrantTypes: [{ required: true, message: '授权模式不能为空', trigger: 'blur' }],
|
||||
accessTokenValidity: [
|
||||
{ required: true, message: '令牌时效不能为空', trigger: 'blur' },
|
||||
@@ -193,7 +196,10 @@ const dataRules = ref({
|
||||
encFlag: [{ required: true, message: '是否开启密码加密传输', trigger: 'blur' }],
|
||||
onlineQuantity: [{ required: true, message: '是否允许同时在线', trigger: 'blur' }],
|
||||
autoapprove: [{ required: true, message: '自动放行不能为空', trigger: 'blur' }],
|
||||
webServerRedirectUri: [{ validator: rule.overLength, trigger: 'blur' },{ required: true, message: '回调地址不能为空', trigger: 'blur' }],
|
||||
webServerRedirectUri: [
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{ required: true, message: '回调地址不能为空', trigger: 'blur' },
|
||||
],
|
||||
});
|
||||
|
||||
// 打开弹窗
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
:data="state.dataList"
|
||||
@selection-change="handleSelectionChange"
|
||||
style="width: 100%"
|
||||
row-key="id"
|
||||
row-key="id"
|
||||
v-loading="state.loading"
|
||||
border
|
||||
:cell-style="tableStyle.cellStyle"
|
||||
@@ -127,7 +127,7 @@ const resetQuery = () => {
|
||||
|
||||
// 导出excel
|
||||
const exportExcel = () => {
|
||||
downBlobFile('/admin/client/export', Object.assign(state.queryForm,{ids:selectObjs}), 'client.xlsx');
|
||||
downBlobFile('/admin/client/export', Object.assign(state.queryForm, { ids: selectObjs }), 'client.xlsx');
|
||||
};
|
||||
|
||||
// 多选事件
|
||||
|
||||
@@ -1,50 +1,46 @@
|
||||
<template>
|
||||
<el-dialog v-model="visible" :title="dataForm.id ? $t('common.editBtn') : $t('common.addBtn')" width="600">
|
||||
<el-form ref="dicDialogFormRef" :model="dataForm" label-width="120px" :rules="dataRules" v-loading="loading">
|
||||
<el-form-item :label="$t('dictItem.dictType')" prop="dictType">
|
||||
<el-input v-model="dataForm.dictType" clearable disabled
|
||||
:placeholder="$t('dictItem.inputDictTypeTip')"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('dictItem.label')" prop="label">
|
||||
<el-input v-model="dataForm.label" :placeholder="$t('dictItem.inputLabelTip')" clearable></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('dictItem.itemValue')" prop="value">
|
||||
<el-input v-model="dataForm.value" :placeholder="$t('dictItem.inputItemValueTip')" clearable></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('dictItem.description')" prop="description">
|
||||
<el-input v-model="dataForm.description" :placeholder="$t('dictItem.inputDescriptionTip')" clearable></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('dictItem.sortOrder')" prop="sortOrder">
|
||||
<el-input-number v-model="dataForm.sortOrder" :placeholder="$t('dictItem.inputSortOrderTip')"
|
||||
clearable></el-input-number>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('dictItem.remarks')" prop="remarks">
|
||||
<el-input type="textarea" maxlength="100" :rows="3" v-model="dataForm.remarks"
|
||||
:placeholder="$t('dictItem.inputRemarksTip')"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态颜色" prop="fontCss">
|
||||
<el-input maxlength="100" v-model="dataForm.fontCss"
|
||||
placeholder="状态颜色"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-dialog v-model="visible" :title="dataForm.id ? $t('common.editBtn') : $t('common.addBtn')" width="600">
|
||||
<el-form ref="dicDialogFormRef" :model="dataForm" label-width="120px" :rules="dataRules" v-loading="loading">
|
||||
<el-form-item :label="$t('dictItem.dictType')" prop="dictType">
|
||||
<el-input v-model="dataForm.dictType" clearable disabled :placeholder="$t('dictItem.inputDictTypeTip')"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('dictItem.label')" prop="label">
|
||||
<el-input v-model="dataForm.label" :placeholder="$t('dictItem.inputLabelTip')" clearable></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('dictItem.itemValue')" prop="value">
|
||||
<el-input v-model="dataForm.value" :placeholder="$t('dictItem.inputItemValueTip')" clearable></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('dictItem.description')" prop="description">
|
||||
<el-input v-model="dataForm.description" :placeholder="$t('dictItem.inputDescriptionTip')" clearable></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('dictItem.sortOrder')" prop="sortOrder">
|
||||
<el-input-number v-model="dataForm.sortOrder" :placeholder="$t('dictItem.inputSortOrderTip')" clearable></el-input-number>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('dictItem.remarks')" prop="remarks">
|
||||
<el-input type="textarea" maxlength="100" :rows="3" v-model="dataForm.remarks" :placeholder="$t('dictItem.inputRemarksTip')"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态颜色" prop="fontCss">
|
||||
<el-input maxlength="100" v-model="dataForm.fontCss" placeholder="状态颜色"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="visible = false">{{ $t('common.cancelButtonText') }}</el-button>
|
||||
<el-button type="primary" @click="onSubmit" :disabled="loading">{{ $t('common.confirmButtonText') }}</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="dict-item-form">
|
||||
import {useI18n} from 'vue-i18n';
|
||||
import {getItemObj, addItemObj, putItemObj, validateDictItemLabel} from '/@/api/admin/dict';
|
||||
import {useMessage} from '/@/hooks/message';
|
||||
import {rule} from "/@/utils/validate";
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { getItemObj, addItemObj, putItemObj, validateDictItemLabel } from '/@/api/admin/dict';
|
||||
import { useMessage } from '/@/hooks/message';
|
||||
import { rule } from '/@/utils/validate';
|
||||
|
||||
// 定义子组件向父组件传值/事件
|
||||
const emit = defineEmits(['refresh']);
|
||||
const {t} = useI18n();
|
||||
const { t } = useI18n();
|
||||
|
||||
// 定义变量内容
|
||||
const dicDialogFormRef = ref();
|
||||
@@ -53,86 +49,97 @@ const visible = ref(false);
|
||||
const loading = ref(false);
|
||||
|
||||
const dataForm = reactive({
|
||||
id: '',
|
||||
dictId: '',
|
||||
dictType: '',
|
||||
value: '',
|
||||
label: '',
|
||||
description: '',
|
||||
sortOrder: 0,
|
||||
remarks: '',
|
||||
fontCss: '',
|
||||
id: '',
|
||||
dictId: '',
|
||||
dictType: '',
|
||||
value: '',
|
||||
label: '',
|
||||
description: '',
|
||||
sortOrder: 0,
|
||||
remarks: '',
|
||||
fontCss: '',
|
||||
});
|
||||
|
||||
const dataRules = reactive({
|
||||
dictType: [{validator: rule.overLength, trigger: 'blur'}, {
|
||||
required: true,
|
||||
message: '请点选左侧字典项',
|
||||
trigger: 'blur'
|
||||
}],
|
||||
value: [{validator: rule.overLength, trigger: 'blur'}, {required: true, message: '数据值不能为空', trigger: 'blur'}],
|
||||
label: [
|
||||
{validator: rule.overLength, trigger: 'blur'},
|
||||
{required: true, message: '标签不能为空', trigger: 'blur'},
|
||||
{
|
||||
validator: (rule: any, value: any, callback: any) => {
|
||||
validateDictItemLabel(rule, value, callback, dataForm.dictType, dataForm.id !== '');
|
||||
},
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
description: [{validator: rule.overLength, trigger: 'blur'}, {
|
||||
required: true,
|
||||
message: '描述不能为空',
|
||||
trigger: 'blur'
|
||||
}],
|
||||
sortOrder: [{validator: rule.overLength, trigger: 'blur'}, {
|
||||
required: true,
|
||||
message: '排序不能为空',
|
||||
trigger: 'blur'
|
||||
}],
|
||||
dictType: [
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{
|
||||
required: true,
|
||||
message: '请点选左侧字典项',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
value: [
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{ required: true, message: '数据值不能为空', trigger: 'blur' },
|
||||
],
|
||||
label: [
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{ required: true, message: '标签不能为空', trigger: 'blur' },
|
||||
{
|
||||
validator: (rule: any, value: any, callback: any) => {
|
||||
validateDictItemLabel(rule, value, callback, dataForm.dictType, dataForm.id !== '');
|
||||
},
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
description: [
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{
|
||||
required: true,
|
||||
message: '描述不能为空',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
sortOrder: [
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{
|
||||
required: true,
|
||||
message: '排序不能为空',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// 打开弹窗
|
||||
const openDialog = (row: any, dictForm: any) => {
|
||||
visible.value = true;
|
||||
dataForm.id = '';
|
||||
visible.value = true;
|
||||
dataForm.id = '';
|
||||
|
||||
nextTick(() => {
|
||||
dicDialogFormRef.value?.resetFields();
|
||||
if (dictForm) {
|
||||
dataForm.dictId = dictForm.dictId;
|
||||
dataForm.dictType = dictForm.dictType;
|
||||
}
|
||||
});
|
||||
if (row?.id) {
|
||||
getItemObj(row.id).then((res) => {
|
||||
Object.assign(dataForm, res.data);
|
||||
});
|
||||
}
|
||||
nextTick(() => {
|
||||
dicDialogFormRef.value?.resetFields();
|
||||
if (dictForm) {
|
||||
dataForm.dictId = dictForm.dictId;
|
||||
dataForm.dictType = dictForm.dictType;
|
||||
}
|
||||
});
|
||||
if (row?.id) {
|
||||
getItemObj(row.id).then((res) => {
|
||||
Object.assign(dataForm, res.data);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 提交
|
||||
const onSubmit = async () => {
|
||||
const valid = await dicDialogFormRef.value.validate().catch(() => {
|
||||
});
|
||||
if (!valid) return false;
|
||||
const valid = await dicDialogFormRef.value.validate().catch(() => {});
|
||||
if (!valid) return false;
|
||||
|
||||
try {
|
||||
loading.value = true;
|
||||
dataForm.id ? await putItemObj(dataForm) : await addItemObj(dataForm);
|
||||
useMessage().success(t(dataForm.id ? 'common.editSuccessText' : 'common.addSuccessText'));
|
||||
visible.value = false;
|
||||
emit('refresh');
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
try {
|
||||
loading.value = true;
|
||||
dataForm.id ? await putItemObj(dataForm) : await addItemObj(dataForm);
|
||||
useMessage().success(t(dataForm.id ? 'common.editSuccessText' : 'common.addSuccessText'));
|
||||
visible.value = false;
|
||||
emit('refresh');
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 暴露变量
|
||||
defineExpose({
|
||||
openDialog,
|
||||
openDialog,
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -54,7 +54,7 @@ const dataForm = reactive({
|
||||
|
||||
const dataRules = reactive({
|
||||
dictType: [
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{ required: true, message: '类型不能为空', trigger: 'blur' },
|
||||
{ validator: rule.validatorNameCn, trigger: 'blur' },
|
||||
{
|
||||
@@ -65,7 +65,10 @@ const dataRules = reactive({
|
||||
},
|
||||
],
|
||||
systemFlag: [{ required: true, message: '字典类型不能为空', trigger: 'blur' }],
|
||||
description: [{ validator: rule.overLength, trigger: 'blur' },{ required: true, message: '描述不能为空', trigger: 'blur' }],
|
||||
description: [
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{ required: true, message: '描述不能为空', trigger: 'blur' },
|
||||
],
|
||||
});
|
||||
|
||||
// 打开弹窗
|
||||
|
||||
@@ -46,7 +46,7 @@ const form = reactive({
|
||||
// 定义校验规则
|
||||
const dataRules = ref({
|
||||
name: [
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{ required: true, message: 'name不能为空', trigger: 'blur' },
|
||||
{ validator: rule.noChinese, trigger: 'blur' },
|
||||
{
|
||||
@@ -57,7 +57,7 @@ const dataRules = ref({
|
||||
},
|
||||
],
|
||||
zhCn: [
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{ required: true, message: '中文不能为空', trigger: 'blur' },
|
||||
{
|
||||
validator: (rule: any, value: any, callback: any) => {
|
||||
@@ -67,7 +67,7 @@ const dataRules = ref({
|
||||
},
|
||||
],
|
||||
en: [
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{ required: true, message: '英文不能为空', trigger: 'blur' },
|
||||
{ validator: rule.letter, trigger: 'blur' },
|
||||
{
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
@selection-change="handleSelectionChange"
|
||||
@sort-change="sortChangeHandle"
|
||||
style="width: 100%"
|
||||
row-key="id"
|
||||
row-key="id"
|
||||
v-loading="state.loading"
|
||||
border
|
||||
:cell-style="tableStyle.cellStyle"
|
||||
@@ -139,7 +139,7 @@ const handleRefreshCache = () => {
|
||||
|
||||
// 导出excel
|
||||
const exportExcel = () => {
|
||||
downBlobFile('/admin/i18n/export', Object.assign(state.queryForm,{ids:selectObjs}), 'i18n.xlsx');
|
||||
downBlobFile('/admin/i18n/export', Object.assign(state.queryForm, { ids: selectObjs }), 'i18n.xlsx');
|
||||
};
|
||||
|
||||
// 多选事件
|
||||
|
||||
@@ -1,86 +1,87 @@
|
||||
<template>
|
||||
<el-drawer v-model="visible" :title="data.title" size="30%">
|
||||
<div class="w-full">
|
||||
<div class="coding inverse-toggle px-5 pt-4 shadow-lg text-gray-100 text-sm font-mono subpixel-antialiased
|
||||
bg-gray-800 pb-6 pt-4 rounded-lg leading-normal overflow-hidden">
|
||||
<div class="top mb-2 flex">
|
||||
<div class="h-3 w-3 bg-red-500 rounded-full"></div>
|
||||
<div class="ml-2 h-3 w-3 bg-orange-300 rounded-full"></div>
|
||||
<div class="ml-2 h-3 w-3 bg-green-500 rounded-full"></div>
|
||||
</div>
|
||||
<div class="mt-4 flex">
|
||||
<span class="text-green-400">{{ $t('syslog.createTime') }}: </span>
|
||||
<p class="flex-1 typing items-center pl-2 whitespace-normal overflow-hidden break-all">
|
||||
{{ data.createTime }}
|
||||
<br>
|
||||
</p>
|
||||
</div>
|
||||
<div class="mt-4 flex">
|
||||
<span class="text-green-400">{{ $t('syslog.createBy') }}: </span>
|
||||
<p class="flex-1 typing items-center pl-2 whitespace-normal overflow-hidden break-all">
|
||||
{{ data.createBy }}
|
||||
<br>
|
||||
</p>
|
||||
</div>
|
||||
<div class="mt-4 flex">
|
||||
<span class="text-green-400">{{ $t('syslog.requestUri') }}: </span>
|
||||
<p class="flex-1 typing items-center pl-2 whitespace-normal overflow-hidden break-all">
|
||||
{{ data.requestUri }}
|
||||
<br>
|
||||
</p>
|
||||
</div>
|
||||
<div class="mt-4 flex">
|
||||
<span class="text-green-400">{{ $t('syslog.remoteAddr') }}: </span>
|
||||
<p class="flex-1 typing items-center pl-2 whitespace-normal overflow-hidden break-all">
|
||||
{{ data.remoteAddr }}
|
||||
<br>
|
||||
</p>
|
||||
</div>
|
||||
<div class="mt-4 flex">
|
||||
<span class="text-green-400">{{ $t('syslog.method') }}: </span>
|
||||
<p class="flex-1 typing items-center pl-2 whitespace-normal overflow-hidden break-all">
|
||||
{{ data.method }}
|
||||
<br>
|
||||
</p>
|
||||
</div>
|
||||
<div class="mt-4 flex">
|
||||
<span class="text-green-400">{{ $t('syslog.serviceId') }}: </span>
|
||||
<p class="flex-1 typing items-center pl-2 whitespace-normal overflow-hidden break-all">
|
||||
{{ data.serviceId }}
|
||||
<br>
|
||||
</p>
|
||||
</div>
|
||||
<div class="mt-4 flex">
|
||||
<span class="text-green-400">{{ $t('syslog.time') }}: </span>
|
||||
<p class="flex-1 typing items-center pl-2 whitespace-normal overflow-hidden break-all">
|
||||
{{ data.time }}/ms
|
||||
<br>
|
||||
</p>
|
||||
</div>
|
||||
<div class="mt-4 flex">
|
||||
<span class="text-green-400">{{ $t('syslog.ua') }}: </span>
|
||||
<p class="flex-1 typing items-center pl-2 whitespace-normal overflow-hidden break-all">
|
||||
{{ data.userAgent }}
|
||||
<br>
|
||||
</p>
|
||||
</div>
|
||||
<div class="mt-4 flex" v-if="data.params">
|
||||
<span class="text-green-400">{{ $t('syslog.params') }}: </span>
|
||||
<p class="flex-1 typing items-center pl-2 whitespace-normal overflow-hidden break-all">
|
||||
{{ data.params }}
|
||||
<br>
|
||||
</p>
|
||||
</div>
|
||||
<div class="mt-4 flex" v-if="data.exception">
|
||||
<span class="text-green-400">{{ data.logType === '0' ? $t('syslog.result') : $t('syslog.exception') }}: </span>
|
||||
<p class="flex-1 typing items-center pl-2 whitespace-normal overflow-hidden break-all">
|
||||
{{ data.exception }}
|
||||
<br>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-drawer>
|
||||
<el-drawer v-model="visible" :title="data.title" size="30%">
|
||||
<div class="w-full">
|
||||
<div
|
||||
class="coding inverse-toggle px-5 pt-4 shadow-lg text-gray-100 text-sm font-mono subpixel-antialiased bg-gray-800 pb-6 pt-4 rounded-lg leading-normal overflow-hidden"
|
||||
>
|
||||
<div class="top mb-2 flex">
|
||||
<div class="h-3 w-3 bg-red-500 rounded-full"></div>
|
||||
<div class="ml-2 h-3 w-3 bg-orange-300 rounded-full"></div>
|
||||
<div class="ml-2 h-3 w-3 bg-green-500 rounded-full"></div>
|
||||
</div>
|
||||
<div class="mt-4 flex">
|
||||
<span class="text-green-400">{{ $t('syslog.createTime') }}: </span>
|
||||
<p class="flex-1 typing items-center pl-2 whitespace-normal overflow-hidden break-all">
|
||||
{{ data.createTime }}
|
||||
<br />
|
||||
</p>
|
||||
</div>
|
||||
<div class="mt-4 flex">
|
||||
<span class="text-green-400">{{ $t('syslog.createBy') }}: </span>
|
||||
<p class="flex-1 typing items-center pl-2 whitespace-normal overflow-hidden break-all">
|
||||
{{ data.createBy }}
|
||||
<br />
|
||||
</p>
|
||||
</div>
|
||||
<div class="mt-4 flex">
|
||||
<span class="text-green-400">{{ $t('syslog.requestUri') }}: </span>
|
||||
<p class="flex-1 typing items-center pl-2 whitespace-normal overflow-hidden break-all">
|
||||
{{ data.requestUri }}
|
||||
<br />
|
||||
</p>
|
||||
</div>
|
||||
<div class="mt-4 flex">
|
||||
<span class="text-green-400">{{ $t('syslog.remoteAddr') }}: </span>
|
||||
<p class="flex-1 typing items-center pl-2 whitespace-normal overflow-hidden break-all">
|
||||
{{ data.remoteAddr }}
|
||||
<br />
|
||||
</p>
|
||||
</div>
|
||||
<div class="mt-4 flex">
|
||||
<span class="text-green-400">{{ $t('syslog.method') }}: </span>
|
||||
<p class="flex-1 typing items-center pl-2 whitespace-normal overflow-hidden break-all">
|
||||
{{ data.method }}
|
||||
<br />
|
||||
</p>
|
||||
</div>
|
||||
<div class="mt-4 flex">
|
||||
<span class="text-green-400">{{ $t('syslog.serviceId') }}: </span>
|
||||
<p class="flex-1 typing items-center pl-2 whitespace-normal overflow-hidden break-all">
|
||||
{{ data.serviceId }}
|
||||
<br />
|
||||
</p>
|
||||
</div>
|
||||
<div class="mt-4 flex">
|
||||
<span class="text-green-400">{{ $t('syslog.time') }}: </span>
|
||||
<p class="flex-1 typing items-center pl-2 whitespace-normal overflow-hidden break-all">
|
||||
{{ data.time }}/ms
|
||||
<br />
|
||||
</p>
|
||||
</div>
|
||||
<div class="mt-4 flex">
|
||||
<span class="text-green-400">{{ $t('syslog.ua') }}: </span>
|
||||
<p class="flex-1 typing items-center pl-2 whitespace-normal overflow-hidden break-all">
|
||||
{{ data.userAgent }}
|
||||
<br />
|
||||
</p>
|
||||
</div>
|
||||
<div class="mt-4 flex" v-if="data.params">
|
||||
<span class="text-green-400">{{ $t('syslog.params') }}: </span>
|
||||
<p class="flex-1 typing items-center pl-2 whitespace-normal overflow-hidden break-all">
|
||||
{{ data.params }}
|
||||
<br />
|
||||
</p>
|
||||
</div>
|
||||
<div class="mt-4 flex" v-if="data.exception">
|
||||
<span class="text-green-400">{{ data.logType === '0' ? $t('syslog.result') : $t('syslog.exception') }}: </span>
|
||||
<p class="flex-1 typing items-center pl-2 whitespace-normal overflow-hidden break-all">
|
||||
{{ data.exception }}
|
||||
<br />
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="log-detail">
|
||||
@@ -89,12 +90,12 @@ const visible = ref(false);
|
||||
const data = reactive({} as any);
|
||||
|
||||
const openDialog = (row: any) => {
|
||||
visible.value = true;
|
||||
Object.assign(data, row);
|
||||
visible.value = true;
|
||||
Object.assign(data, row);
|
||||
};
|
||||
|
||||
// 暴露变量
|
||||
defineExpose({
|
||||
openDialog,
|
||||
openDialog,
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
export default {
|
||||
syslog: {
|
||||
index: '#',
|
||||
logType: 'logType',
|
||||
title: 'title',
|
||||
remoteAddr: 'remoteAddr',
|
||||
method: 'method',
|
||||
ua: 'browser',
|
||||
serviceId: 'serviceId',
|
||||
time: 'time',
|
||||
params: 'params',
|
||||
createTime: 'createTime',
|
||||
requestUri: 'requestUri',
|
||||
exception: 'exception',
|
||||
createBy: 'createBy',
|
||||
action: 'action',
|
||||
inputLogTypeTip: 'select logType',
|
||||
inputStartPlaceholderTip: 'Start Time',
|
||||
inputEndPlaceholderTip: 'End TIme',
|
||||
result: 'result'
|
||||
},
|
||||
syslog: {
|
||||
index: '#',
|
||||
logType: 'logType',
|
||||
title: 'title',
|
||||
remoteAddr: 'remoteAddr',
|
||||
method: 'method',
|
||||
ua: 'browser',
|
||||
serviceId: 'serviceId',
|
||||
time: 'time',
|
||||
params: 'params',
|
||||
createTime: 'createTime',
|
||||
requestUri: 'requestUri',
|
||||
exception: 'exception',
|
||||
createBy: 'createBy',
|
||||
action: 'action',
|
||||
inputLogTypeTip: 'select logType',
|
||||
inputStartPlaceholderTip: 'Start Time',
|
||||
inputEndPlaceholderTip: 'End TIme',
|
||||
result: 'result',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
export default {
|
||||
syslog: {
|
||||
index: '#',
|
||||
logType: '类型',
|
||||
title: '标题',
|
||||
remoteAddr: 'IP地址',
|
||||
method: '请求方式',
|
||||
ua: '浏览器',
|
||||
serviceId: '客户端',
|
||||
time: '耗时',
|
||||
params: '请求参数',
|
||||
createTime: '请求时间',
|
||||
requestUri: '请求地址',
|
||||
exception: '异常信息',
|
||||
createBy: '操作人',
|
||||
action: '操作',
|
||||
inputLogTypeTip: '请选择类型',
|
||||
inputStartPlaceholderTip: '开始时间',
|
||||
inputEndPlaceholderTip: '结束时间',
|
||||
result: '结果',
|
||||
},
|
||||
syslog: {
|
||||
index: '#',
|
||||
logType: '类型',
|
||||
title: '标题',
|
||||
remoteAddr: 'IP地址',
|
||||
method: '请求方式',
|
||||
ua: '浏览器',
|
||||
serviceId: '客户端',
|
||||
time: '耗时',
|
||||
params: '请求参数',
|
||||
createTime: '请求时间',
|
||||
requestUri: '请求地址',
|
||||
exception: '异常信息',
|
||||
createBy: '操作人',
|
||||
action: '操作',
|
||||
inputLogTypeTip: '请选择类型',
|
||||
inputStartPlaceholderTip: '开始时间',
|
||||
inputEndPlaceholderTip: '结束时间',
|
||||
result: '结果',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,114 +1,108 @@
|
||||
<template>
|
||||
<div class="layout-padding">
|
||||
<div class="layout-padding-auto layout-padding-view">
|
||||
<!-- 顶部折线图-->
|
||||
<log-line-chart/>
|
||||
<div class="layout-padding">
|
||||
<div class="layout-padding-auto layout-padding-view">
|
||||
<!-- 顶部折线图-->
|
||||
<log-line-chart />
|
||||
|
||||
<el-row class="mt-4 ml10" v-show="showSearch">
|
||||
<el-form :inline="true" :model="state.queryForm" @keyup.enter="getDataList" ref="queryRef">
|
||||
<el-form-item :label="$t('syslog.logType')" prop="logType">
|
||||
<el-select :placeholder="$t('syslog.inputLogTypeTip')" clearable
|
||||
v-model="state.queryForm.logType">
|
||||
<el-option :key="item.value" :label="item.label" :value="item.value" v-for="item in log_type"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('syslog.createTime')" prop="createTime">
|
||||
<el-date-picker
|
||||
:end-placeholder="$t('syslog.inputEndPlaceholderTip')"
|
||||
:start-placeholder="$t('syslog.inputStartPlaceholderTip')"
|
||||
range-separator="To"
|
||||
type="datetimerange"
|
||||
v-model="state.queryForm.createTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="getDataList" icon="Search" type="primary">{{ $t('common.queryBtn') }}</el-button>
|
||||
<el-button @click="resetQuery" icon="Refresh">{{ $t('common.resetBtn') }}</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-row>
|
||||
<el-row class="mt-4 ml10" v-show="showSearch">
|
||||
<el-form :inline="true" :model="state.queryForm" @keyup.enter="getDataList" ref="queryRef">
|
||||
<el-form-item :label="$t('syslog.logType')" prop="logType">
|
||||
<el-select :placeholder="$t('syslog.inputLogTypeTip')" clearable v-model="state.queryForm.logType">
|
||||
<el-option :key="item.value" :label="item.label" :value="item.value" v-for="item in log_type" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('syslog.createTime')" prop="createTime">
|
||||
<el-date-picker
|
||||
:end-placeholder="$t('syslog.inputEndPlaceholderTip')"
|
||||
:start-placeholder="$t('syslog.inputStartPlaceholderTip')"
|
||||
range-separator="To"
|
||||
type="datetimerange"
|
||||
v-model="state.queryForm.createTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="getDataList" icon="Search" type="primary">{{ $t('common.queryBtn') }}</el-button>
|
||||
<el-button @click="resetQuery" icon="Refresh">{{ $t('common.resetBtn') }}</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-row>
|
||||
|
||||
<el-row>
|
||||
<div class="mb-2" style="width: 100%">
|
||||
<el-button :disabled="multiple" v-auth="'sys_log_del'" @click="handleDelete(selectObjs)" class="ml10"
|
||||
icon="Delete" type="primary">
|
||||
{{ $t('common.delBtn') }}
|
||||
</el-button>
|
||||
<right-toolbar
|
||||
:export="'sys_log_export'"
|
||||
@exportExcel="exportExcel"
|
||||
@queryTable="getDataList"
|
||||
class="ml10"
|
||||
style="float: right; margin-right: 20px"
|
||||
v-model:showSearch="showSearch"
|
||||
></right-toolbar>
|
||||
</div>
|
||||
</el-row>
|
||||
<el-table
|
||||
ref="tableRef"
|
||||
:data="state.dataList"
|
||||
@selection-change="handleSelectionChange"
|
||||
@sort-change="sortChangeHandle"
|
||||
v-loading="state.loading"
|
||||
border
|
||||
:cell-style="tableStyle.cellStyle"
|
||||
:header-cell-style="tableStyle.headerCellStyle"
|
||||
>
|
||||
<el-table-column align="center" type="selection" width="40"/>
|
||||
<el-table-column :label="$t('syslog.index')" type="index" width="60"/>
|
||||
<el-table-column :label="$t('syslog.logType')" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
<dict-tag :options="log_type" :value="scope.row.logType"></dict-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('syslog.title')" prop="title" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column :label="$t('syslog.remoteAddr')" prop="remoteAddr" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column :label="$t('syslog.method')" prop="method" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column :label="$t('syslog.time')" prop="time" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
<span v-if="scope.row.time">{{ scope.row.time }}/ms</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('syslog.createTime')" prop="createTime" show-overflow-tooltip sortable="custom"
|
||||
width="200"></el-table-column>
|
||||
<el-table-column :label="$t('syslog.createBy')" prop="createBy" show-overflow-tooltip sortable="custom"
|
||||
width="200"></el-table-column>
|
||||
<el-table-column :label="$t('common.action')" width="150">
|
||||
<template #default="scope">
|
||||
<el-button icon="view" @click="LogDetailRef.openDialog(scope.row)" size="small" text type="primary">
|
||||
{{ $t('common.detailBtn') }}
|
||||
</el-button>
|
||||
<el-button v-auth="'sys_log_del'" icon="delete" @click="handleDelete([scope.row.id])" size="small" text
|
||||
type="primary">
|
||||
{{ $t('common.delBtn') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-row>
|
||||
<div class="mb-2" style="width: 100%">
|
||||
<el-button :disabled="multiple" v-auth="'sys_log_del'" @click="handleDelete(selectObjs)" class="ml10" icon="Delete" type="primary">
|
||||
{{ $t('common.delBtn') }}
|
||||
</el-button>
|
||||
<right-toolbar
|
||||
:export="'sys_log_export'"
|
||||
@exportExcel="exportExcel"
|
||||
@queryTable="getDataList"
|
||||
class="ml10"
|
||||
style="float: right; margin-right: 20px"
|
||||
v-model:showSearch="showSearch"
|
||||
></right-toolbar>
|
||||
</div>
|
||||
</el-row>
|
||||
<el-table
|
||||
ref="tableRef"
|
||||
:data="state.dataList"
|
||||
@selection-change="handleSelectionChange"
|
||||
@sort-change="sortChangeHandle"
|
||||
v-loading="state.loading"
|
||||
border
|
||||
:cell-style="tableStyle.cellStyle"
|
||||
:header-cell-style="tableStyle.headerCellStyle"
|
||||
>
|
||||
<el-table-column align="center" type="selection" width="40" />
|
||||
<el-table-column :label="$t('syslog.index')" type="index" width="60" />
|
||||
<el-table-column :label="$t('syslog.logType')" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
<dict-tag :options="log_type" :value="scope.row.logType"></dict-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('syslog.title')" prop="title" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column :label="$t('syslog.remoteAddr')" prop="remoteAddr" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column :label="$t('syslog.method')" prop="method" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column :label="$t('syslog.time')" prop="time" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
<span v-if="scope.row.time">{{ scope.row.time }}/ms</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('syslog.createTime')" prop="createTime" show-overflow-tooltip sortable="custom" width="200"></el-table-column>
|
||||
<el-table-column :label="$t('syslog.createBy')" prop="createBy" show-overflow-tooltip sortable="custom" width="200"></el-table-column>
|
||||
<el-table-column :label="$t('common.action')" width="150">
|
||||
<template #default="scope">
|
||||
<el-button icon="view" @click="LogDetailRef.openDialog(scope.row)" size="small" text type="primary">
|
||||
{{ $t('common.detailBtn') }}
|
||||
</el-button>
|
||||
<el-button v-auth="'sys_log_del'" icon="delete" @click="handleDelete([scope.row.id])" size="small" text type="primary">
|
||||
{{ $t('common.delBtn') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination @current-change="currentChangeHandle" @size-change="sizeChangeHandle"
|
||||
v-bind="state.pagination"></pagination>
|
||||
<log-detail ref="LogDetailRef"></log-detail>
|
||||
</div>
|
||||
</div>
|
||||
<pagination @current-change="currentChangeHandle" @size-change="sizeChangeHandle" v-bind="state.pagination"></pagination>
|
||||
<log-detail ref="LogDetailRef"></log-detail>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {BasicTableProps, useTable} from '/@/hooks/table';
|
||||
import {delObj, pageList} from '/@/api/admin/log';
|
||||
import {useI18n} from 'vue-i18n';
|
||||
import {useMessage, useMessageBox} from '/@/hooks/message';
|
||||
import {useDict} from '/@/hooks/dict';
|
||||
import { BasicTableProps, useTable } from '/@/hooks/table';
|
||||
import { delObj, pageList } from '/@/api/admin/log';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useMessage, useMessageBox } from '/@/hooks/message';
|
||||
import { useDict } from '/@/hooks/dict';
|
||||
|
||||
const LogDetail = defineAsyncComponent(() => import('./detail.vue'));
|
||||
const LogLineChart = defineAsyncComponent(() => import('./line-chart.vue'));
|
||||
|
||||
const LogDetailRef = ref();
|
||||
|
||||
const {log_type} = useDict('log_type');
|
||||
const { log_type } = useDict('log_type');
|
||||
|
||||
const {t} = useI18n();
|
||||
const { t } = useI18n();
|
||||
|
||||
// 定义变量内容
|
||||
const queryRef = ref();
|
||||
@@ -121,82 +115,75 @@ const multiple = ref(true);
|
||||
let tableRef = ref(null);
|
||||
|
||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||
queryForm: {
|
||||
logType: '',
|
||||
createTime: '',
|
||||
serviceId: '',
|
||||
},
|
||||
selectObjs: [],
|
||||
pageList: pageList,
|
||||
descs: ['create_time'],
|
||||
createdIsNeed: false,
|
||||
queryForm: {
|
||||
logType: '',
|
||||
createTime: '',
|
||||
serviceId: '',
|
||||
},
|
||||
selectObjs: [],
|
||||
pageList: pageList,
|
||||
descs: ['create_time'],
|
||||
createdIsNeed: false,
|
||||
});
|
||||
|
||||
// table hook
|
||||
const {
|
||||
downBlobFile,
|
||||
getDataList,
|
||||
currentChangeHandle: baseCurrentChangeHandle,
|
||||
sortChangeHandle,
|
||||
sizeChangeHandle,
|
||||
tableStyle
|
||||
} = useTable(state);
|
||||
const { downBlobFile, getDataList, currentChangeHandle: baseCurrentChangeHandle, sortChangeHandle, sizeChangeHandle, tableStyle } = useTable(state);
|
||||
|
||||
// 分页事件
|
||||
const currentChangeHandle = (page: number) => {
|
||||
// Reset table scroll position to top
|
||||
tableRef.value?.setScrollTop(0);
|
||||
// Call the original handler
|
||||
baseCurrentChangeHandle(page);
|
||||
// Reset table scroll position to top
|
||||
tableRef.value?.setScrollTop(0);
|
||||
// Call the original handler
|
||||
baseCurrentChangeHandle(page);
|
||||
};
|
||||
|
||||
// 清空搜索条件
|
||||
const resetQuery = () => {
|
||||
queryRef.value?.resetFields();
|
||||
getDataList();
|
||||
queryRef.value?.resetFields();
|
||||
getDataList();
|
||||
};
|
||||
|
||||
// 导出excel
|
||||
const exportExcel = () => {
|
||||
downBlobFile('/admin/log/export', state.queryForm, 'log.xlsx');
|
||||
downBlobFile('/admin/log/export', state.queryForm, 'log.xlsx');
|
||||
};
|
||||
|
||||
// 多选事件
|
||||
const handleSelectionChange = (objs: { id: string }[]) => {
|
||||
selectObjs.value = objs.map(({id}) => id);
|
||||
multiple.value = !objs.length;
|
||||
selectObjs.value = objs.map(({ id }) => id);
|
||||
multiple.value = !objs.length;
|
||||
};
|
||||
|
||||
// 删除操作
|
||||
const handleDelete = async (ids: string[]) => {
|
||||
try {
|
||||
await useMessageBox().confirm(t('common.delConfirmText'));
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await useMessageBox().confirm(t('common.delConfirmText'));
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await delObj(ids);
|
||||
getDataList();
|
||||
useMessage().success(t('common.delSuccessText'));
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg);
|
||||
}
|
||||
try {
|
||||
await delObj(ids);
|
||||
getDataList();
|
||||
useMessage().success(t('common.delSuccessText'));
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg);
|
||||
}
|
||||
};
|
||||
|
||||
// onMounted 通过路由参数给 serviceId 赋值
|
||||
const route = useRoute();
|
||||
onMounted(() => {
|
||||
const {serviceId} = route.query;
|
||||
if (serviceId) {
|
||||
state.queryForm.serviceId = serviceId;
|
||||
}
|
||||
getDataList();
|
||||
const { serviceId } = route.query;
|
||||
if (serviceId) {
|
||||
state.queryForm.serviceId = serviceId;
|
||||
}
|
||||
getDataList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
pre code.hljs {
|
||||
width: 65%;
|
||||
width: 65%;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -95,7 +95,7 @@ const form = reactive({
|
||||
// 定义校验规则
|
||||
const dataRules = reactive({
|
||||
publicName: [
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{ required: true, message: '名称不能为空', trigger: 'blur' },
|
||||
{
|
||||
validator: (rule: any, value: any, callback: any) => {
|
||||
@@ -105,7 +105,7 @@ const dataRules = reactive({
|
||||
},
|
||||
],
|
||||
publicKey: [
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{ required: true, message: '参数键不能为空', trigger: 'blur' },
|
||||
{ validator: rule.validatorCapital, trigger: 'blur' },
|
||||
{
|
||||
@@ -115,7 +115,10 @@ const dataRules = reactive({
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
publicValue: [{ validator: rule.overLength, trigger: 'blur' },{ required: true, message: '参数值不能为空', trigger: 'blur' }],
|
||||
publicValue: [
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{ required: true, message: '参数值不能为空', trigger: 'blur' },
|
||||
],
|
||||
status: [{ required: true, message: '状态不能为空', trigger: 'blur' }],
|
||||
publicType: [{ required: true, message: '类型不能为空', trigger: 'blur' }],
|
||||
systemFlag: [{ required: true, message: '类型不能为空', trigger: 'blur' }],
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
:data="state.dataList"
|
||||
@selection-change="handleSelectionChange"
|
||||
style="width: 100%"
|
||||
row-key="publicId"
|
||||
row-key="publicId"
|
||||
v-loading="state.loading"
|
||||
border
|
||||
:cell-style="tableStyle.cellStyle"
|
||||
@@ -158,7 +158,7 @@ const handleSelectable = (row: any) => {
|
||||
|
||||
// 导出excel
|
||||
const exportExcel = () => {
|
||||
downBlobFile('/admin/param/export', Object.assign(state.queryForm,{ids:selectObjs}), 'param.xlsx');
|
||||
downBlobFile('/admin/param/export', Object.assign(state.queryForm, { ids: selectObjs }), 'param.xlsx');
|
||||
};
|
||||
|
||||
const handleRefreshCache = () => {
|
||||
|
||||
@@ -1,137 +1,136 @@
|
||||
<template>
|
||||
<el-dialog :title="form.sensitiveId ? '编辑' : '新增'" v-model="visible"
|
||||
:close-on-click-modal="false" draggable>
|
||||
<el-form ref="dataFormRef" :model="form" :rules="dataRules" formDialogRef label-width="90px" v-loading="loading">
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="24" class="mb20">
|
||||
<el-form-item label="敏感词" prop="sensitiveWord">
|
||||
<el-input v-model="form.sensitiveWord" placeholder="请输入敏感词"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-dialog :title="form.sensitiveId ? '编辑' : '新增'" v-model="visible" :close-on-click-modal="false" draggable>
|
||||
<el-form ref="dataFormRef" :model="form" :rules="dataRules" formDialogRef label-width="90px" v-loading="loading">
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="24" class="mb20">
|
||||
<el-form-item label="敏感词" prop="sensitiveWord">
|
||||
<el-input v-model="form.sensitiveWord" placeholder="请输入敏感词" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="24" class="mb20">
|
||||
<el-form-item label="类型" prop="sensitiveType">
|
||||
<el-radio-group v-model="form.sensitiveType">
|
||||
<el-radio :label="item.value" v-for="(item, index) in sensitive_type" border :key="index">{{ item.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" class="mb20">
|
||||
<el-form-item label="类型" prop="sensitiveType">
|
||||
<el-radio-group v-model="form.sensitiveType">
|
||||
<el-radio :label="item.value" v-for="(item, index) in sensitive_type" border :key="index">{{ item.label }} </el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="24" class="mb20">
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" placeholder="请输入备注"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="visible = false">取消</el-button>
|
||||
<el-button type="primary" @click="onSubmit" :disabled="loading">确认</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<el-col :span="24" class="mb20">
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="visible = false">取消</el-button>
|
||||
<el-button type="primary" @click="onSubmit" :disabled="loading">确认</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="SysSensitiveWordDialog">
|
||||
import {useDict} from '/@/hooks/dict';
|
||||
import {useMessage} from "/@/hooks/message";
|
||||
import {getObj, addObj, putObj, validateWord} from '/@/api/admin/sensitive'
|
||||
import {rule} from '/@/utils/validate';
|
||||
import { useDict } from '/@/hooks/dict';
|
||||
import { useMessage } from '/@/hooks/message';
|
||||
import { getObj, addObj, putObj, validateWord } from '/@/api/admin/sensitive';
|
||||
import { rule } from '/@/utils/validate';
|
||||
|
||||
const emit = defineEmits(['refresh']);
|
||||
|
||||
// 定义变量内容
|
||||
const dataFormRef = ref();
|
||||
const visible = ref(false)
|
||||
const loading = ref(false)
|
||||
const visible = ref(false);
|
||||
const loading = ref(false);
|
||||
// 定义字典
|
||||
const {sensitive_type} = useDict('sensitive_type')
|
||||
const { sensitive_type } = useDict('sensitive_type');
|
||||
|
||||
// 提交表单数据
|
||||
const form = reactive({
|
||||
sensitiveId: '',
|
||||
sensitiveWord: '',
|
||||
sensitiveType: '0',
|
||||
remark: '',
|
||||
sensitiveId: '',
|
||||
sensitiveWord: '',
|
||||
sensitiveType: '0',
|
||||
remark: '',
|
||||
});
|
||||
|
||||
// 定义校验规则
|
||||
const dataRules = ref({
|
||||
sensitiveWord: [{validator: rule.overLength, trigger: 'blur'}
|
||||
, {
|
||||
validator: (rule: any, value: any, callback: any) => {
|
||||
validateWord(rule, value, callback, form.sensitiveId !== '');
|
||||
}
|
||||
, trigger: 'blur'
|
||||
}
|
||||
, {
|
||||
required: true,
|
||||
message: '敏感词不能为空',
|
||||
trigger: 'blur'
|
||||
}],
|
||||
sensitiveType: [{required: true, message: '类型不能为空', trigger: 'blur'}],
|
||||
remark: [{validator: rule.overLength, trigger: 'blur'}],
|
||||
})
|
||||
sensitiveWord: [
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{
|
||||
validator: (rule: any, value: any, callback: any) => {
|
||||
validateWord(rule, value, callback, form.sensitiveId !== '');
|
||||
},
|
||||
trigger: 'blur',
|
||||
},
|
||||
{
|
||||
required: true,
|
||||
message: '敏感词不能为空',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
sensitiveType: [{ required: true, message: '类型不能为空', trigger: 'blur' }],
|
||||
remark: [{ validator: rule.overLength, trigger: 'blur' }],
|
||||
});
|
||||
|
||||
// 打开弹窗
|
||||
const openDialog = (sensitiveId: string) => {
|
||||
visible.value = true
|
||||
form.sensitiveId = ''
|
||||
visible.value = true;
|
||||
form.sensitiveId = '';
|
||||
|
||||
// 重置表单数据
|
||||
nextTick(() => {
|
||||
dataFormRef.value?.resetFields();
|
||||
});
|
||||
// 重置表单数据
|
||||
nextTick(() => {
|
||||
dataFormRef.value?.resetFields();
|
||||
});
|
||||
|
||||
// 获取sysSensitiveWord信息
|
||||
if (sensitiveId) {
|
||||
form.sensitiveId = sensitiveId
|
||||
getsysSensitiveWordData(sensitiveId)
|
||||
}
|
||||
// 获取sysSensitiveWord信息
|
||||
if (sensitiveId) {
|
||||
form.sensitiveId = sensitiveId;
|
||||
getsysSensitiveWordData(sensitiveId);
|
||||
}
|
||||
};
|
||||
|
||||
// 提交
|
||||
const onSubmit = async () => {
|
||||
// 立即设置 loading,防止重复点击
|
||||
if (loading.value) return;
|
||||
loading.value = true;
|
||||
// 立即设置 loading,防止重复点击
|
||||
if (loading.value) return;
|
||||
loading.value = true;
|
||||
|
||||
try {
|
||||
const valid = await dataFormRef.value.validate().catch(() => {
|
||||
});
|
||||
if (!valid) {
|
||||
loading.value = false;
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
const valid = await dataFormRef.value.validate().catch(() => {});
|
||||
if (!valid) {
|
||||
loading.value = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
form.sensitiveId ? await putObj(form) : await addObj(form);
|
||||
useMessage().success(form.sensitiveId ? '修改成功' : '添加成功');
|
||||
visible.value = false;
|
||||
emit('refresh');
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
form.sensitiveId ? await putObj(form) : await addObj(form);
|
||||
useMessage().success(form.sensitiveId ? '修改成功' : '添加成功');
|
||||
visible.value = false;
|
||||
emit('refresh');
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// 初始化表单数据
|
||||
const getsysSensitiveWordData = (sensitiveId: string) => {
|
||||
// 获取数据
|
||||
loading.value = true
|
||||
getObj({sensitiveId}).then((res: any) => {
|
||||
|
||||
Object.assign(form, res.data)
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
// 获取数据
|
||||
loading.value = true;
|
||||
getObj({ sensitiveId })
|
||||
.then((res: any) => {
|
||||
Object.assign(form, res.data);
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
// 暴露变量
|
||||
defineExpose({
|
||||
openDialog
|
||||
openDialog,
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -1,165 +1,167 @@
|
||||
<template>
|
||||
<div class="layout-padding">
|
||||
<div class="layout-padding-auto layout-padding-view">
|
||||
<el-row v-show="showSearch">
|
||||
<el-form :model="state.queryForm" ref="queryRef" :inline="true" @keyup.enter="getDataList">
|
||||
<el-form-item label="敏感词" prop="sensitiveWord">
|
||||
<el-input placeholder="请输入敏感词" v-model="state.queryForm.sensitiveWord"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="类型" prop="sensitiveType">
|
||||
<el-radio-group v-model="state.queryForm.sensitiveType">
|
||||
<el-radio :label="item.value" v-for="(item, index) in sensitive_type" border :key="index">{{ item.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button icon="search" type="primary" @click="getDataList">
|
||||
查询
|
||||
</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<div class="mb8" style="width: 100%">
|
||||
<el-button icon="folder-add" type="primary" class="ml10" @click="formDialogRef.openDialog()"
|
||||
v-auth="'admin_sysSensitiveWord_add'">
|
||||
新 增
|
||||
</el-button>
|
||||
<el-button plain :disabled="multiple" icon="Delete" type="primary"
|
||||
v-auth="'admin_sysSensitiveWord_del'" @click="handleDelete(selectObjs)">
|
||||
删 除
|
||||
</el-button>
|
||||
<el-button plain icon="Check" type="primary"
|
||||
v-auth="'admin_sysSensitiveWord_del'" @click="matchDialogRef.openDialog()">
|
||||
匹配测试
|
||||
</el-button>
|
||||
<el-button plain @click="handleRefreshCache()" class="ml10" icon="refresh-left" type="primary">
|
||||
{{ $t('common.refreshCacheBtn') }}
|
||||
</el-button>
|
||||
<div class="layout-padding">
|
||||
<div class="layout-padding-auto layout-padding-view">
|
||||
<el-row v-show="showSearch">
|
||||
<el-form :model="state.queryForm" ref="queryRef" :inline="true" @keyup.enter="getDataList">
|
||||
<el-form-item label="敏感词" prop="sensitiveWord">
|
||||
<el-input placeholder="请输入敏感词" v-model="state.queryForm.sensitiveWord" />
|
||||
</el-form-item>
|
||||
<el-form-item label="类型" prop="sensitiveType">
|
||||
<el-radio-group v-model="state.queryForm.sensitiveType">
|
||||
<el-radio :label="item.value" v-for="(item, index) in sensitive_type" border :key="index">{{ item.label }} </el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button icon="search" type="primary" @click="getDataList"> 查询 </el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<div class="mb8" style="width: 100%">
|
||||
<el-button icon="folder-add" type="primary" class="ml10" @click="formDialogRef.openDialog()" v-auth="'admin_sysSensitiveWord_add'">
|
||||
新 增
|
||||
</el-button>
|
||||
<el-button plain :disabled="multiple" icon="Delete" type="primary" v-auth="'admin_sysSensitiveWord_del'" @click="handleDelete(selectObjs)">
|
||||
删 除
|
||||
</el-button>
|
||||
<el-button plain icon="Check" type="primary" v-auth="'admin_sysSensitiveWord_del'" @click="matchDialogRef.openDialog()">
|
||||
匹配测试
|
||||
</el-button>
|
||||
<el-button plain @click="handleRefreshCache()" class="ml10" icon="refresh-left" type="primary">
|
||||
{{ $t('common.refreshCacheBtn') }}
|
||||
</el-button>
|
||||
|
||||
<right-toolbar v-model:showSearch="showSearch" :export="'admin_sysSensitiveWord_export'"
|
||||
@exportExcel="exportExcel" class="ml10 mr20" style="float: right;"
|
||||
@queryTable="getDataList"></right-toolbar>
|
||||
</div>
|
||||
</el-row>
|
||||
<el-table :data="state.dataList" v-loading="state.loading" border
|
||||
:cell-style="tableStyle.cellStyle" :header-cell-style="tableStyle.headerCellStyle"
|
||||
@selection-change="selectionChangHandle"
|
||||
@sort-change="sortChangeHandle">
|
||||
<el-table-column type="selection" width="40" align="center"/>
|
||||
<el-table-column type="index" label="#" width="40"/>
|
||||
<el-table-column prop="sensitiveWord" label="敏感词" show-overflow-tooltip/>
|
||||
<el-table-column prop="sensitiveType" label="类型" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
<dict-tag :options="sensitive_type" :value="scope.row.sensitiveType"></dict-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="remark" label="备注" width="200" show-overflow-tooltip/>
|
||||
<el-table-column label="操作" width="150">
|
||||
<template #default="scope">
|
||||
<el-button icon="edit-pen" text type="primary" v-auth="'admin_sysSensitiveWord_edit'"
|
||||
@click="formDialogRef.openDialog(scope.row.sensitiveId)">编辑
|
||||
</el-button>
|
||||
<el-button icon="delete" text type="primary" v-auth="'admin_sysSensitiveWord_del'"
|
||||
@click="handleDelete([scope.row.sensitiveId])">删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" v-bind="state.pagination"/>
|
||||
</div>
|
||||
<right-toolbar
|
||||
v-model:showSearch="showSearch"
|
||||
:export="'admin_sysSensitiveWord_export'"
|
||||
@exportExcel="exportExcel"
|
||||
class="ml10 mr20"
|
||||
style="float: right"
|
||||
@queryTable="getDataList"
|
||||
></right-toolbar>
|
||||
</div>
|
||||
</el-row>
|
||||
<el-table
|
||||
:data="state.dataList"
|
||||
v-loading="state.loading"
|
||||
border
|
||||
:cell-style="tableStyle.cellStyle"
|
||||
:header-cell-style="tableStyle.headerCellStyle"
|
||||
@selection-change="selectionChangHandle"
|
||||
@sort-change="sortChangeHandle"
|
||||
>
|
||||
<el-table-column type="selection" width="40" align="center" />
|
||||
<el-table-column type="index" label="#" width="40" />
|
||||
<el-table-column prop="sensitiveWord" label="敏感词" show-overflow-tooltip />
|
||||
<el-table-column prop="sensitiveType" label="类型" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
<dict-tag :options="sensitive_type" :value="scope.row.sensitiveType"></dict-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="remark" label="备注" width="200" show-overflow-tooltip />
|
||||
<el-table-column label="操作" width="150">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
icon="edit-pen"
|
||||
text
|
||||
type="primary"
|
||||
v-auth="'admin_sysSensitiveWord_edit'"
|
||||
@click="formDialogRef.openDialog(scope.row.sensitiveId)"
|
||||
>编辑
|
||||
</el-button>
|
||||
<el-button icon="delete" text type="primary" v-auth="'admin_sysSensitiveWord_del'" @click="handleDelete([scope.row.sensitiveId])"
|
||||
>删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" v-bind="state.pagination" />
|
||||
</div>
|
||||
|
||||
<!-- 编辑、新增 -->
|
||||
<form-dialog ref="formDialogRef" @refresh="getDataList(false)"/>
|
||||
<!-- 编辑、新增 -->
|
||||
<form-dialog ref="formDialogRef" @refresh="getDataList(false)" />
|
||||
|
||||
<!-- 匹配测试 -->
|
||||
<match-dialog ref="matchDialogRef" @refresh="getDataList(false)"/>
|
||||
</div>
|
||||
<!-- 匹配测试 -->
|
||||
<match-dialog ref="matchDialogRef" @refresh="getDataList(false)" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="systemSysSensitiveWord">
|
||||
import {BasicTableProps, useTable} from "/@/hooks/table";
|
||||
import {fetchList, delObjs, refreshObj} from "/@/api/admin/sensitive";
|
||||
import {useMessage, useMessageBox} from "/@/hooks/message";
|
||||
import {useDict} from '/@/hooks/dict';
|
||||
import { BasicTableProps, useTable } from '/@/hooks/table';
|
||||
import { fetchList, delObjs, refreshObj } from '/@/api/admin/sensitive';
|
||||
import { useMessage, useMessageBox } from '/@/hooks/message';
|
||||
import { useDict } from '/@/hooks/dict';
|
||||
|
||||
// 引入组件
|
||||
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
|
||||
const MatchDialog = defineAsyncComponent(() => import('./match.vue'));
|
||||
// 定义查询字典
|
||||
|
||||
const {sensitive_type} = useDict('sensitive_type')
|
||||
const { sensitive_type } = useDict('sensitive_type');
|
||||
// 定义变量内容
|
||||
const formDialogRef = ref()
|
||||
const matchDialogRef = ref()
|
||||
const formDialogRef = ref();
|
||||
const matchDialogRef = ref();
|
||||
// 搜索变量
|
||||
const queryRef = ref()
|
||||
const showSearch = ref(true)
|
||||
const queryRef = ref();
|
||||
const showSearch = ref(true);
|
||||
// 多选变量
|
||||
const selectObjs = ref([]) as any
|
||||
const multiple = ref(true)
|
||||
const selectObjs = ref([]) as any;
|
||||
const multiple = ref(true);
|
||||
|
||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||
queryForm: {},
|
||||
pageList: fetchList
|
||||
})
|
||||
queryForm: {},
|
||||
pageList: fetchList,
|
||||
});
|
||||
|
||||
// table hook
|
||||
const {
|
||||
getDataList,
|
||||
currentChangeHandle,
|
||||
sizeChangeHandle,
|
||||
sortChangeHandle,
|
||||
downBlobFile,
|
||||
tableStyle
|
||||
} = useTable(state)
|
||||
const { getDataList, currentChangeHandle, sizeChangeHandle, sortChangeHandle, downBlobFile, tableStyle } = useTable(state);
|
||||
|
||||
// 清空搜索条件
|
||||
const resetQuery = () => {
|
||||
// 清空搜索条件
|
||||
queryRef.value?.resetFields()
|
||||
// 清空多选
|
||||
selectObjs.value = []
|
||||
getDataList()
|
||||
}
|
||||
// 清空搜索条件
|
||||
queryRef.value?.resetFields();
|
||||
// 清空多选
|
||||
selectObjs.value = [];
|
||||
getDataList();
|
||||
};
|
||||
|
||||
// 导出excel
|
||||
const exportExcel = () => {
|
||||
downBlobFile('/admin/sysSensitiveWord/export', Object.assign(state.queryForm, {ids: selectObjs}), 'sysSensitiveWord.xlsx')
|
||||
}
|
||||
downBlobFile('/admin/sysSensitiveWord/export', Object.assign(state.queryForm, { ids: selectObjs }), 'sysSensitiveWord.xlsx');
|
||||
};
|
||||
|
||||
// 多选事件
|
||||
const selectionChangHandle = (objs: { sensitiveId: string }[]) => {
|
||||
selectObjs.value = objs.map(({sensitiveId}) => sensitiveId);
|
||||
multiple.value = !objs.length;
|
||||
selectObjs.value = objs.map(({ sensitiveId }) => sensitiveId);
|
||||
multiple.value = !objs.length;
|
||||
};
|
||||
|
||||
// 删除操作
|
||||
const handleDelete = async (ids: string[]) => {
|
||||
try {
|
||||
await useMessageBox().confirm('此操作将永久删除');
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await useMessageBox().confirm('此操作将永久删除');
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await delObjs(ids);
|
||||
getDataList();
|
||||
useMessage().success('删除成功');
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg);
|
||||
}
|
||||
try {
|
||||
await delObjs(ids);
|
||||
getDataList();
|
||||
useMessage().success('删除成功');
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg);
|
||||
}
|
||||
};
|
||||
|
||||
// 刷新缓存
|
||||
const handleRefreshCache = async () => {
|
||||
try {
|
||||
await refreshObj();
|
||||
getDataList();
|
||||
useMessage().success('刷新成功');
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg);
|
||||
}
|
||||
}
|
||||
try {
|
||||
await refreshObj();
|
||||
getDataList();
|
||||
useMessage().success('刷新成功');
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,127 +1,128 @@
|
||||
<template>
|
||||
<el-dialog title="匹配测试" v-model="visible"
|
||||
:close-on-click-modal="false" draggable>
|
||||
<el-form ref="dataFormRef" :model="form" :rules="dataRules" formDialogRef label-width="90px" v-loading="loading">
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="24" class="mb20">
|
||||
<el-form-item label="敏感词" prop="sensitiveWord">
|
||||
<el-input type="textarea" rows="3" v-model="form.sensitiveWord" placeholder="请输入敏感词"
|
||||
@blur="onSubmit"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" class="mb20">
|
||||
<el-form-item prop="result">
|
||||
<template #label>
|
||||
匹配结果
|
||||
<tip content="可点击敏感词加入白名单"/>
|
||||
</template>
|
||||
<div v-html="matchResult" @click="handleChildClick"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="visible = false">取消</el-button>
|
||||
<el-button type="primary" @click="onSubmit" :disabled="loading">确认</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<el-dialog title="匹配测试" v-model="visible" :close-on-click-modal="false" draggable>
|
||||
<el-form ref="dataFormRef" :model="form" :rules="dataRules" formDialogRef label-width="90px" v-loading="loading">
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="24" class="mb20">
|
||||
<el-form-item label="敏感词" prop="sensitiveWord">
|
||||
<el-input type="textarea" rows="3" v-model="form.sensitiveWord" placeholder="请输入敏感词" @blur="onSubmit" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" class="mb20">
|
||||
<el-form-item prop="result">
|
||||
<template #label>
|
||||
匹配结果
|
||||
<tip content="可点击敏感词加入白名单" />
|
||||
</template>
|
||||
<div v-html="matchResult" @click="handleChildClick" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="visible = false">取消</el-button>
|
||||
<el-button type="primary" @click="onSubmit" :disabled="loading">确认</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="SysSensitiveWordDialog">
|
||||
import {useMessage} from "/@/hooks/message";
|
||||
import {testObj, addObj, getObj} from '/@/api/admin/sensitive'
|
||||
import {rule} from '/@/utils/validate';
|
||||
import { useMessage } from '/@/hooks/message';
|
||||
import { testObj, addObj, getObj } from '/@/api/admin/sensitive';
|
||||
import { rule } from '/@/utils/validate';
|
||||
|
||||
const emit = defineEmits(['refresh']);
|
||||
|
||||
// 定义变量内容
|
||||
const dataFormRef = ref();
|
||||
const visible = ref(false)
|
||||
const loading = ref(false)
|
||||
const matchResult = ref('')
|
||||
const visible = ref(false);
|
||||
const loading = ref(false);
|
||||
const matchResult = ref('');
|
||||
|
||||
// 提交表单数据
|
||||
const form = reactive({
|
||||
sensitiveId: '',
|
||||
sensitiveWord: '',
|
||||
sensitiveId: '',
|
||||
sensitiveWord: '',
|
||||
});
|
||||
|
||||
// 定义校验规则
|
||||
const dataRules = ref({
|
||||
sensitiveWord: [{validator: rule.overLength, trigger: 'blur'}, {
|
||||
required: true,
|
||||
message: '敏感词不能为空',
|
||||
trigger: 'blur'
|
||||
}]
|
||||
})
|
||||
sensitiveWord: [
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{
|
||||
required: true,
|
||||
message: '敏感词不能为空',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// 打开弹窗
|
||||
const openDialog = (id: string) => {
|
||||
visible.value = true
|
||||
form.sensitiveId = ''
|
||||
visible.value = true;
|
||||
form.sensitiveId = '';
|
||||
|
||||
// 重置表单数据
|
||||
nextTick(() => {
|
||||
dataFormRef.value?.resetFields();
|
||||
});
|
||||
// 重置表单数据
|
||||
nextTick(() => {
|
||||
dataFormRef.value?.resetFields();
|
||||
});
|
||||
};
|
||||
|
||||
const handleChildClick = async (event: any) => {
|
||||
try {
|
||||
if (event.target.tagName.toLowerCase() === 'a' && event.target.classList.contains('link-error')) {
|
||||
const {data} = await getObj({sensitiveWord: event.target.innerText, sensitiveType: '1'})
|
||||
if (data) {
|
||||
useMessage().error('数据已存在,请勿重新添加');
|
||||
return
|
||||
}
|
||||
await addObj({sensitiveWord: event.target.innerText, sensitiveType: '1'})
|
||||
useMessage().success('白名单添加成功');
|
||||
emit('refresh');
|
||||
}
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (event.target.tagName.toLowerCase() === 'a' && event.target.classList.contains('link-error')) {
|
||||
const { data } = await getObj({ sensitiveWord: event.target.innerText, sensitiveType: '1' });
|
||||
if (data) {
|
||||
useMessage().error('数据已存在,请勿重新添加');
|
||||
return;
|
||||
}
|
||||
await addObj({ sensitiveWord: event.target.innerText, sensitiveType: '1' });
|
||||
useMessage().success('白名单添加成功');
|
||||
emit('refresh');
|
||||
}
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg);
|
||||
}
|
||||
};
|
||||
|
||||
// 提交
|
||||
const onSubmit = async () => {
|
||||
const valid = await dataFormRef.value.validate().catch(() => {
|
||||
});
|
||||
if (!valid) return false;
|
||||
const valid = await dataFormRef.value.validate().catch(() => {});
|
||||
if (!valid) return false;
|
||||
|
||||
try {
|
||||
loading.value = true;
|
||||
const {data} = await testObj(form);
|
||||
// 要处理的字符串
|
||||
matchResult.value = data;
|
||||
// 遍历关键词数组,并进行替换
|
||||
matchResult.value = matchResult.value.map((item: string) => {
|
||||
let modifiedItem = item;
|
||||
data.forEach((word: string) => {
|
||||
let regex = new RegExp(word, 'g');
|
||||
modifiedItem = modifiedItem.replace(regex, `
|
||||
try {
|
||||
loading.value = true;
|
||||
const { data } = await testObj(form);
|
||||
// 要处理的字符串
|
||||
matchResult.value = data;
|
||||
// 遍历关键词数组,并进行替换
|
||||
matchResult.value = matchResult.value.map((item: string) => {
|
||||
let modifiedItem = item;
|
||||
data.forEach((word: string) => {
|
||||
let regex = new RegExp(word, 'g');
|
||||
modifiedItem = modifiedItem.replace(
|
||||
regex,
|
||||
`
|
||||
<div class="tooltip tooltip-open tooltip-bottom" data-tip="触发敏感词">
|
||||
<a class="link link-error" @click="$emit('click-child','${word}')">${word}</a>
|
||||
</div>
|
||||
`);
|
||||
});
|
||||
return modifiedItem;
|
||||
});
|
||||
useMessage().success('操作成功');
|
||||
emit('refresh');
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
`
|
||||
);
|
||||
});
|
||||
return modifiedItem;
|
||||
});
|
||||
useMessage().success('操作成功');
|
||||
emit('refresh');
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// 暴露变量
|
||||
defineExpose({
|
||||
openDialog
|
||||
openDialog,
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -81,14 +81,23 @@ const form = reactive({
|
||||
// 定义校验规则
|
||||
const dataRules = ref({
|
||||
type: [{ required: true, message: '类型不能为空', trigger: 'blur' }],
|
||||
appId: [{ validator: rule.overLength, trigger: 'blur' },{ required: true, message: 'appId不能为空', trigger: 'blur' }],
|
||||
remark: [{ validator: rule.overLength, trigger: 'blur' },{ required: true, message: '描述不能为空', trigger: 'blur' }],
|
||||
appId: [
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{ required: true, message: 'appId不能为空', trigger: 'blur' },
|
||||
],
|
||||
remark: [
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{ required: true, message: '描述不能为空', trigger: 'blur' },
|
||||
],
|
||||
redirectUrl: [
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{ required: true, message: '回调地址不能为空', trigger: 'blur' },
|
||||
{ validator: rule.url, trigger: 'blur' },
|
||||
],
|
||||
appSecret: [{ validator: rule.overLength, trigger: 'blur' },{ required: true, message: 'appSecret不能为空', trigger: 'blur' }],
|
||||
appSecret: [
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{ required: true, message: 'appSecret不能为空', trigger: 'blur' },
|
||||
],
|
||||
});
|
||||
|
||||
// 打开弹窗
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
v-loading="state.loading"
|
||||
:data="state.dataList"
|
||||
style="width: 100%"
|
||||
row-key="id"
|
||||
row-key="id"
|
||||
@selection-change="handleSelectionChange"
|
||||
border
|
||||
:cell-style="tableStyle.cellStyle"
|
||||
@@ -120,7 +120,7 @@ const resetQuery = () => {
|
||||
|
||||
// 导出excel
|
||||
const exportExcel = () => {
|
||||
downBlobFile('/admin/social/export', Object.assign(state.queryForm,{ids:selectObjs}), 'social.xlsx');
|
||||
downBlobFile('/admin/social/export', Object.assign(state.queryForm, { ids: selectObjs }), 'social.xlsx');
|
||||
};
|
||||
|
||||
// 多选事件
|
||||
|
||||
@@ -1,200 +1,191 @@
|
||||
<template>
|
||||
<el-dialog :title="form.id ? $t('common.editBtn') : $t('common.addBtn')" v-model="visible"
|
||||
:close-on-click-modal="false" draggable width="600">
|
||||
<el-form ref="dataFormRef" :model="form" :rules="dataRules" formDialogRef label-width="90px" v-loading="loading">
|
||||
<el-row>
|
||||
<el-col :span="24" class="mb20" v-if="!form.id">
|
||||
<el-form-item :label="t('area.pid')" prop="pid">
|
||||
<china-area class="w-full" :placeholder="t('area.inputPidByTip')" :plus="true" @change="handleChange"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-dialog :title="form.id ? $t('common.editBtn') : $t('common.addBtn')" v-model="visible" :close-on-click-modal="false" draggable width="600">
|
||||
<el-form ref="dataFormRef" :model="form" :rules="dataRules" formDialogRef label-width="90px" v-loading="loading">
|
||||
<el-row>
|
||||
<el-col :span="24" class="mb20" v-if="!form.id">
|
||||
<el-form-item :label="t('area.pid')" prop="pid">
|
||||
<china-area class="w-full" :placeholder="t('area.inputPidByTip')" :plus="true" @change="handleChange" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="24" class="mb20">
|
||||
<el-form-item :label="t('area.name')" prop="name">
|
||||
<el-input v-model="form.name" :placeholder="t('area.inputNameByTip')"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" class="mb20">
|
||||
<el-form-item :label="t('area.name')" prop="name">
|
||||
<el-input v-model="form.name" :placeholder="t('area.inputNameByTip')" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="24" class="mb20">
|
||||
<el-form-item :label="t('area.adcode')" prop="adcode">
|
||||
<el-input-number v-model="form.adcode" :placeholder="t('area.inputAdCodeByTip')" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="24" class="mb20">
|
||||
<el-form-item :label="t('area.adcode')" prop="adcode">
|
||||
<el-input-number v-model="form.adcode" :placeholder="t('area.inputAdCodeByTip')"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" class="mb20">
|
||||
<el-form-item :label="t('area.areaType')" prop="areaType">
|
||||
<el-select v-model="form.areaType">
|
||||
<el-option :key="item.value" :label="item.label" :value="item.value" v-for="item in area_type_dict"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="24" class="mb20">
|
||||
<el-form-item :label="t('area.areaType')" prop="areaType">
|
||||
<el-select v-model="form.areaType">
|
||||
<el-option :key="item.value" :label="item.label" :value="item.value"
|
||||
v-for="item in area_type_dict"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" class="mb20">
|
||||
<el-form-item :label="t('area.areaSort')" prop="areaSort">
|
||||
<el-input-number v-model="form.areaSort" :placeholder="t('area.inputAreaSortByTip')" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="24" class="mb20">
|
||||
<el-form-item :label="t('area.areaSort')" prop="areaSort">
|
||||
<el-input-number v-model="form.areaSort" :placeholder="t('area.inputAreaSortByTip')"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" class="mb20">
|
||||
<el-form-item :label="t('area.hot')" prop="hot">
|
||||
<el-radio-group v-model="form.hot">
|
||||
<el-radio :key="index" :label="item.value" border v-for="(item, index) in yes_no_type">{{ item.label }} </el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="24" class="mb20">
|
||||
<el-form-item :label="t('area.hot')" prop="hot">
|
||||
<el-radio-group v-model="form.hot">
|
||||
<el-radio :key="index" :label="item.value" border v-for="(item, index) in yes_no_type">{{
|
||||
item.label
|
||||
}}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="24" class="mb20">
|
||||
<el-form-item :label="t('area.areaStatus')" prop="areaStatus">
|
||||
<el-radio-group v-model="form.areaStatus">
|
||||
<el-radio :key="index" :label="item.value" border v-for="(item, index) in yes_no_type">{{
|
||||
item.label
|
||||
}}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="visible = false">{{ $t('common.cancelButtonText') }}</el-button>
|
||||
<el-button @click="onSubmit" type="primary" :disabled="loading">{{ $t('common.confirmButtonText') }}</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<el-col :span="24" class="mb20">
|
||||
<el-form-item :label="t('area.areaStatus')" prop="areaStatus">
|
||||
<el-radio-group v-model="form.areaStatus">
|
||||
<el-radio :key="index" :label="item.value" border v-for="(item, index) in yes_no_type">{{ item.label }} </el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="visible = false">{{ $t('common.cancelButtonText') }}</el-button>
|
||||
<el-button @click="onSubmit" type="primary" :disabled="loading">{{ $t('common.confirmButtonText') }}</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="SysAreaDialog">
|
||||
import {useMessage} from "/@/hooks/message";
|
||||
import {getObj, addObj, putObj, validateExist} from '/@/api/admin/sysArea'
|
||||
import {useDict} from "/@/hooks/dict";
|
||||
import {useI18n} from "vue-i18n";
|
||||
import {rule} from "/@/utils/validate";
|
||||
import { useMessage } from '/@/hooks/message';
|
||||
import { getObj, addObj, putObj, validateExist } from '/@/api/admin/sysArea';
|
||||
import { useDict } from '/@/hooks/dict';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { rule } from '/@/utils/validate';
|
||||
|
||||
const ChinaArea = defineAsyncComponent(() => import("/@/components/ChinaArea/index.vue"));
|
||||
const ChinaArea = defineAsyncComponent(() => import('/@/components/ChinaArea/index.vue'));
|
||||
const emit = defineEmits(['refresh']);
|
||||
const { t } = useI18n();
|
||||
|
||||
// 定义变量内容
|
||||
const dataFormRef = ref();
|
||||
const visible = ref(false)
|
||||
const loading = ref(false)
|
||||
const visible = ref(false);
|
||||
const loading = ref(false);
|
||||
// 定义字典
|
||||
const {yes_no_type} = useDict('yes_no_type')
|
||||
const { yes_no_type } = useDict('yes_no_type');
|
||||
const area_type_dict = [
|
||||
{value: '0', label: '国家'},
|
||||
{value: '1', label: '省份'},
|
||||
{value: '2', label: '城市'},
|
||||
{value: '3', label: '县区'},
|
||||
{value: '4', label: '街道'}
|
||||
]
|
||||
{ value: '0', label: '国家' },
|
||||
{ value: '1', label: '省份' },
|
||||
{ value: '2', label: '城市' },
|
||||
{ value: '3', label: '县区' },
|
||||
{ value: '4', label: '街道' },
|
||||
];
|
||||
|
||||
// 提交表单数据
|
||||
const form = reactive({
|
||||
id: '',
|
||||
pid: 100000,
|
||||
name: '',
|
||||
letter: '',
|
||||
adcode: 0,
|
||||
location: '',
|
||||
areaSort: 0,
|
||||
areaStatus: '1',
|
||||
areaType: '2',
|
||||
hot: '0',
|
||||
cityCode: '',
|
||||
id: '',
|
||||
pid: 100000,
|
||||
name: '',
|
||||
letter: '',
|
||||
adcode: 0,
|
||||
location: '',
|
||||
areaSort: 0,
|
||||
areaStatus: '1',
|
||||
areaType: '2',
|
||||
hot: '0',
|
||||
cityCode: '',
|
||||
});
|
||||
|
||||
// 定义校验规则
|
||||
const dataRules = ref({
|
||||
name: [
|
||||
{required: true, message: '地区名称不能为空', trigger: 'blur'},
|
||||
{min: 2, max: 20, message: '长度在 3 到 30 个字符', trigger: 'blur'},
|
||||
{
|
||||
validator: (rule: any, value: any, callback: any) => {
|
||||
validateExist(rule, value, callback, form.id !== '');
|
||||
},
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
adcode: [
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{required: true, message: '编码不能为空', trigger: 'blur'},
|
||||
{
|
||||
validator: (rule: any, value: any, callback: any) => {
|
||||
validateExist(rule, value, callback, form.id !== '');
|
||||
},
|
||||
trigger: 'blur',
|
||||
},
|
||||
]
|
||||
name: [
|
||||
{ required: true, message: '地区名称不能为空', trigger: 'blur' },
|
||||
{ min: 2, max: 20, message: '长度在 3 到 30 个字符', trigger: 'blur' },
|
||||
{
|
||||
validator: (rule: any, value: any, callback: any) => {
|
||||
validateExist(rule, value, callback, form.id !== '');
|
||||
},
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
adcode: [
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{ required: true, message: '编码不能为空', trigger: 'blur' },
|
||||
{
|
||||
validator: (rule: any, value: any, callback: any) => {
|
||||
validateExist(rule, value, callback, form.id !== '');
|
||||
},
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// 打开弹窗
|
||||
const openDialog = (id: string) => {
|
||||
visible.value = true
|
||||
form.id = ''
|
||||
visible.value = true;
|
||||
form.id = '';
|
||||
|
||||
// 重置表单数据
|
||||
nextTick(() => {
|
||||
dataFormRef.value?.resetFields();
|
||||
});
|
||||
// 重置表单数据
|
||||
nextTick(() => {
|
||||
dataFormRef.value?.resetFields();
|
||||
});
|
||||
|
||||
// 获取sysArea信息
|
||||
if (id) {
|
||||
form.id = id
|
||||
getsysAreaData(id)
|
||||
}
|
||||
// 获取sysArea信息
|
||||
if (id) {
|
||||
form.id = id;
|
||||
getsysAreaData(id);
|
||||
}
|
||||
};
|
||||
|
||||
// 提交
|
||||
const onSubmit = async () => {
|
||||
// 立即设置 loading,防止重复点击
|
||||
if (loading.value) return;
|
||||
loading.value = true;
|
||||
// 立即设置 loading,防止重复点击
|
||||
if (loading.value) return;
|
||||
loading.value = true;
|
||||
|
||||
try {
|
||||
const valid = await dataFormRef.value.validate().catch(() => {
|
||||
});
|
||||
if (!valid) {
|
||||
loading.value = false;
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
const valid = await dataFormRef.value.validate().catch(() => {});
|
||||
if (!valid) {
|
||||
loading.value = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
form.id ? await putObj(form) : await addObj(form);
|
||||
useMessage().success(form.id ? t('common.editSuccessText') : t('common.addSuccessText'));
|
||||
visible.value = false;
|
||||
emit('refresh');
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
form.id ? await putObj(form) : await addObj(form);
|
||||
useMessage().success(form.id ? t('common.editSuccessText') : t('common.addSuccessText'));
|
||||
visible.value = false;
|
||||
emit('refresh');
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// 初始化表单数据
|
||||
const getsysAreaData = (id: string) => {
|
||||
// 获取数据
|
||||
loading.value = true
|
||||
getObj({id: id}).then((res: any) => {
|
||||
Object.assign(form, res.data)
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
// 获取数据
|
||||
loading.value = true;
|
||||
getObj({ id: id })
|
||||
.then((res: any) => {
|
||||
Object.assign(form, res.data);
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
// 地区选择
|
||||
const handleChange = (data: string) => {
|
||||
let dataArray = data.split(",");
|
||||
form.pid = dataArray[dataArray.length - 1];
|
||||
}
|
||||
let dataArray = data.split(',');
|
||||
form.pid = dataArray[dataArray.length - 1];
|
||||
};
|
||||
|
||||
// 暴露变量
|
||||
defineExpose({
|
||||
openDialog
|
||||
openDialog,
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -4,11 +4,11 @@ export default {
|
||||
id: 'id',
|
||||
pid: 'pid',
|
||||
name: 'name',
|
||||
adcode:'adcode',
|
||||
areaType:'areaType',
|
||||
areaSort:'areaSort',
|
||||
hot:'hot',
|
||||
areaStatus:'areaStatus',
|
||||
adcode: 'adcode',
|
||||
areaType: 'areaType',
|
||||
areaSort: 'areaSort',
|
||||
hot: 'hot',
|
||||
areaStatus: 'areaStatus',
|
||||
inputAdCodeByTip: 'input adcode',
|
||||
inputPidByTip: 'input pid',
|
||||
inputNameByTip: 'input name',
|
||||
|
||||
@@ -4,11 +4,11 @@ export default {
|
||||
id: '主键',
|
||||
pid: '父级地区',
|
||||
name: '名称',
|
||||
adcode:'编码',
|
||||
areaType:'类型',
|
||||
areaSort:'排序值',
|
||||
hot:'热门',
|
||||
areaStatus:'有效',
|
||||
adcode: '编码',
|
||||
areaType: '类型',
|
||||
areaSort: '排序值',
|
||||
hot: '热门',
|
||||
areaStatus: '有效',
|
||||
inputAdCodeByTip: '请选择编码',
|
||||
inputPidByTip: '请选择父级地区',
|
||||
inputNameByTip: '请输入地区名称',
|
||||
|
||||
@@ -1,174 +1,173 @@
|
||||
<template>
|
||||
<div class="layout-padding">
|
||||
<div class="layout-padding-auto layout-padding-view">
|
||||
<el-row v-show="showSearch">
|
||||
<el-form :model="state.queryForm" ref="queryRef" :inline="true" @keyup.enter="getDataList">
|
||||
<el-form-item :label="t('area.pid')" prop="adcode">
|
||||
<china-area :type="3" :placeholder="t('area.inputPidByTip')" v-model="pid" :plus="true" @change="handleChange"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('area.name')" prop="name">
|
||||
<el-input :placeholder="t('area.inputNameByTip')" v-model="state.queryForm.name"/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button icon="search" type="primary" @click="getDataList">
|
||||
{{ $t('common.queryBtn') }}
|
||||
</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">{{ $t('common.resetBtn') }}</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<div class="mb8" style="width: 100%">
|
||||
<el-button icon="folder-add" type="primary" class="ml10" @click="formDialogRef.openDialog()"
|
||||
v-auth="'sys_sysArea_add'">
|
||||
{{ $t('common.addBtn') }}
|
||||
</el-button>
|
||||
<el-button plain :disabled="multiple" icon="Delete" type="primary"
|
||||
v-auth="'sys_sysArea_del'" @click="handleDelete(selectObjs)">
|
||||
{{ $t('common.delBtn') }}
|
||||
</el-button>
|
||||
<right-toolbar v-model:showSearch="showSearch" :export="'sys_sysArea_export'"
|
||||
@exportExcel="exportExcel" class="ml10 mr20" style="float: right;"
|
||||
@queryTable="getDataList"></right-toolbar>
|
||||
</div>
|
||||
</el-row>
|
||||
<el-table :data="state.dataList" v-loading="state.loading" border
|
||||
:cell-style="tableStyle.cellStyle" :header-cell-style="tableStyle.headerCellStyle"
|
||||
@selection-change="selectionChangHandle"
|
||||
@sort-change="sortChangeHandle">
|
||||
<el-table-column type="selection" width="40" align="center"/>
|
||||
<el-table-column type="index" label="#" width="40"/>
|
||||
<el-table-column prop="name" :label="t('area.name')" show-overflow-tooltip/>
|
||||
<el-table-column prop="adcode" :label="t('area.adcode')" show-overflow-tooltip/>
|
||||
<el-table-column prop="areaType" :label="t('area.areaType')" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
<dict-tag :options="area_type_dict" :value="scope.row.areaType"></dict-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="hot" :label="t('area.hot')" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
<dict-tag :options="yes_no_type" :value="scope.row.hot"></dict-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="areaStatus" :label="t('area.areaStatus')" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
<dict-tag :options="yes_no_type" :value="scope.row.areaStatus"></dict-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="areaSort" :label="t('area.areaSort')" width="100" sortable="custom"
|
||||
show-overflow-tooltip/>
|
||||
<el-table-column :label="$t('common.action')" width="150">
|
||||
<template #default="scope">
|
||||
<el-button icon="edit-pen" text type="primary" v-auth="'sys_sysArea_edit'"
|
||||
@click="formDialogRef.openDialog(scope.row.id)">{{ $t('common.editBtn') }}
|
||||
</el-button>
|
||||
<el-button icon="delete" text type="primary" v-auth="'sys_sysArea_del'"
|
||||
@click="handleDelete([scope.row.id])">{{ $t('common.delBtn') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" v-bind="state.pagination"/>
|
||||
</div>
|
||||
<div class="layout-padding">
|
||||
<div class="layout-padding-auto layout-padding-view">
|
||||
<el-row v-show="showSearch">
|
||||
<el-form :model="state.queryForm" ref="queryRef" :inline="true" @keyup.enter="getDataList">
|
||||
<el-form-item :label="t('area.pid')" prop="adcode">
|
||||
<china-area :type="3" :placeholder="t('area.inputPidByTip')" v-model="pid" :plus="true" @change="handleChange" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('area.name')" prop="name">
|
||||
<el-input :placeholder="t('area.inputNameByTip')" v-model="state.queryForm.name" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button icon="search" type="primary" @click="getDataList">
|
||||
{{ $t('common.queryBtn') }}
|
||||
</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">{{ $t('common.resetBtn') }}</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<div class="mb8" style="width: 100%">
|
||||
<el-button icon="folder-add" type="primary" class="ml10" @click="formDialogRef.openDialog()" v-auth="'sys_sysArea_add'">
|
||||
{{ $t('common.addBtn') }}
|
||||
</el-button>
|
||||
<el-button plain :disabled="multiple" icon="Delete" type="primary" v-auth="'sys_sysArea_del'" @click="handleDelete(selectObjs)">
|
||||
{{ $t('common.delBtn') }}
|
||||
</el-button>
|
||||
<right-toolbar
|
||||
v-model:showSearch="showSearch"
|
||||
:export="'sys_sysArea_export'"
|
||||
@exportExcel="exportExcel"
|
||||
class="ml10 mr20"
|
||||
style="float: right"
|
||||
@queryTable="getDataList"
|
||||
></right-toolbar>
|
||||
</div>
|
||||
</el-row>
|
||||
<el-table
|
||||
:data="state.dataList"
|
||||
v-loading="state.loading"
|
||||
border
|
||||
:cell-style="tableStyle.cellStyle"
|
||||
:header-cell-style="tableStyle.headerCellStyle"
|
||||
@selection-change="selectionChangHandle"
|
||||
@sort-change="sortChangeHandle"
|
||||
>
|
||||
<el-table-column type="selection" width="40" align="center" />
|
||||
<el-table-column type="index" label="#" width="40" />
|
||||
<el-table-column prop="name" :label="t('area.name')" show-overflow-tooltip />
|
||||
<el-table-column prop="adcode" :label="t('area.adcode')" show-overflow-tooltip />
|
||||
<el-table-column prop="areaType" :label="t('area.areaType')" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
<dict-tag :options="area_type_dict" :value="scope.row.areaType"></dict-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="hot" :label="t('area.hot')" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
<dict-tag :options="yes_no_type" :value="scope.row.hot"></dict-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="areaStatus" :label="t('area.areaStatus')" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
<dict-tag :options="yes_no_type" :value="scope.row.areaStatus"></dict-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="areaSort" :label="t('area.areaSort')" width="100" sortable="custom" show-overflow-tooltip />
|
||||
<el-table-column :label="$t('common.action')" width="150">
|
||||
<template #default="scope">
|
||||
<el-button icon="edit-pen" text type="primary" v-auth="'sys_sysArea_edit'" @click="formDialogRef.openDialog(scope.row.id)"
|
||||
>{{ $t('common.editBtn') }}
|
||||
</el-button>
|
||||
<el-button icon="delete" text type="primary" v-auth="'sys_sysArea_del'" @click="handleDelete([scope.row.id])"
|
||||
>{{ $t('common.delBtn') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" v-bind="state.pagination" />
|
||||
</div>
|
||||
|
||||
<!-- 编辑、新增 -->
|
||||
<form-dialog ref="formDialogRef" @refresh="getDataList(false)"/>
|
||||
|
||||
</div>
|
||||
<!-- 编辑、新增 -->
|
||||
<form-dialog ref="formDialogRef" @refresh="getDataList(false)" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="systemSysArea">
|
||||
import {BasicTableProps, useTable} from "/@/hooks/table";
|
||||
import {delObjs, fetchList} from "/@/api/admin/sysArea";
|
||||
import {useMessage, useMessageBox} from "/@/hooks/message";
|
||||
import {useDict} from '/@/hooks/dict';
|
||||
import {useI18n} from "vue-i18n";
|
||||
import { BasicTableProps, useTable } from '/@/hooks/table';
|
||||
import { delObjs, fetchList } from '/@/api/admin/sysArea';
|
||||
import { useMessage, useMessageBox } from '/@/hooks/message';
|
||||
import { useDict } from '/@/hooks/dict';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const {t} = useI18n();
|
||||
const { t } = useI18n();
|
||||
|
||||
// 省市区查询组件
|
||||
const ChinaArea = defineAsyncComponent(() => import("/@/components/ChinaArea/index.vue"));
|
||||
const ChinaArea = defineAsyncComponent(() => import('/@/components/ChinaArea/index.vue'));
|
||||
// 引入组件
|
||||
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
|
||||
// 定义查询字典
|
||||
const {yes_no_type} = useDict('yes_no_type');
|
||||
const { yes_no_type } = useDict('yes_no_type');
|
||||
const area_type_dict = [
|
||||
{value: '0', label: '国家'},
|
||||
{value: '1', label: '省份'},
|
||||
{value: '2', label: '城市'},
|
||||
{value: '3', label: '县区'},
|
||||
{value: '4', label: '街道'}
|
||||
]
|
||||
{ value: '0', label: '国家' },
|
||||
{ value: '1', label: '省份' },
|
||||
{ value: '2', label: '城市' },
|
||||
{ value: '3', label: '县区' },
|
||||
{ value: '4', label: '街道' },
|
||||
];
|
||||
// 定义变量内容
|
||||
const formDialogRef = ref()
|
||||
const formDialogRef = ref();
|
||||
// 搜索变量
|
||||
const queryRef = ref()
|
||||
const showSearch = ref(true)
|
||||
const queryRef = ref();
|
||||
const showSearch = ref(true);
|
||||
// 多选变量
|
||||
const selectObjs = ref([]) as any
|
||||
const multiple = ref(true)
|
||||
const pid = ref()
|
||||
const selectObjs = ref([]) as any;
|
||||
const multiple = ref(true);
|
||||
const pid = ref();
|
||||
|
||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||
queryForm: {
|
||||
adcode: '',
|
||||
name: ''
|
||||
},
|
||||
ascs: ['adcode'],
|
||||
pageList: fetchList
|
||||
})
|
||||
queryForm: {
|
||||
adcode: '',
|
||||
name: '',
|
||||
},
|
||||
ascs: ['adcode'],
|
||||
pageList: fetchList,
|
||||
});
|
||||
|
||||
// table hook
|
||||
const {
|
||||
getDataList,
|
||||
currentChangeHandle,
|
||||
sizeChangeHandle,
|
||||
sortChangeHandle,
|
||||
downBlobFile,
|
||||
tableStyle
|
||||
} = useTable(state)
|
||||
const { getDataList, currentChangeHandle, sizeChangeHandle, sortChangeHandle, downBlobFile, tableStyle } = useTable(state);
|
||||
|
||||
// 清空搜索条件
|
||||
const resetQuery = () => {
|
||||
// 清空搜索条件
|
||||
queryRef.value?.resetFields()
|
||||
pid.value = ''
|
||||
// 清空多选
|
||||
selectObjs.value = []
|
||||
getDataList()
|
||||
}
|
||||
// 清空搜索条件
|
||||
queryRef.value?.resetFields();
|
||||
pid.value = '';
|
||||
// 清空多选
|
||||
selectObjs.value = [];
|
||||
getDataList();
|
||||
};
|
||||
|
||||
// 导出excel
|
||||
const exportExcel = () => {
|
||||
downBlobFile('/admin/sysArea/export', Object.assign(state.queryForm, {ids: selectObjs}), 'sysArea.xlsx')
|
||||
}
|
||||
downBlobFile('/admin/sysArea/export', Object.assign(state.queryForm, { ids: selectObjs }), 'sysArea.xlsx');
|
||||
};
|
||||
|
||||
// 多选事件
|
||||
const selectionChangHandle = (objs: { id: string }[]) => {
|
||||
selectObjs.value = objs.map(({id}) => id);
|
||||
multiple.value = !objs.length;
|
||||
selectObjs.value = objs.map(({ id }) => id);
|
||||
multiple.value = !objs.length;
|
||||
};
|
||||
|
||||
// 删除操作
|
||||
const handleDelete = async (ids: string[]) => {
|
||||
try {
|
||||
await useMessageBox().confirm(t('common.delConfirmText'));
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await useMessageBox().confirm(t('common.delConfirmText'));
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await delObjs(ids);
|
||||
getDataList();
|
||||
useMessage().success(t('common.delSuccessText'));
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg);
|
||||
}
|
||||
try {
|
||||
await delObjs(ids);
|
||||
getDataList();
|
||||
useMessage().success(t('common.delSuccessText'));
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg);
|
||||
}
|
||||
};
|
||||
|
||||
// 地区查询
|
||||
const handleChange = (data: string) => {
|
||||
let dataArray = data.split(",");
|
||||
state.queryForm.adcode = dataArray[dataArray.length - 1];
|
||||
}
|
||||
let dataArray = data.split(',');
|
||||
state.queryForm.adcode = dataArray[dataArray.length - 1];
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { getObj, deptTree, addObj, putObj } from '/@/api/admin/dept';
|
||||
import { useMessage } from '/@/hooks/message';
|
||||
import {rule} from "/@/utils/validate";
|
||||
import { rule } from '/@/utils/validate';
|
||||
|
||||
// 定义子组件向父组件传值/事件
|
||||
const emit = defineEmits(['refresh']);
|
||||
@@ -52,7 +52,10 @@ const loading = ref(false);
|
||||
|
||||
const dataRules = ref({
|
||||
parentId: [{ required: true, message: '上级部门不能为空', trigger: 'blur' }],
|
||||
name: [{ validator: rule.overLength, trigger: 'blur' },{ required: true, message: '部门名称不能为空', trigger: 'blur' }],
|
||||
name: [
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{ required: true, message: '部门名称不能为空', trigger: 'blur' },
|
||||
],
|
||||
sortOrder: [{ required: true, message: '排序不能为空', trigger: 'blur' }],
|
||||
});
|
||||
|
||||
|
||||
@@ -12,9 +12,9 @@ export default {
|
||||
inputLeaderIdTip: 'input leader',
|
||||
inputsortOrderTip: 'input sortOrder',
|
||||
importTip: 'import dept',
|
||||
addNodeText:'add dept',
|
||||
editNodeText:'edit dept',
|
||||
delNodeText:'delete dept',
|
||||
addNodeText: 'add dept',
|
||||
editNodeText: 'edit dept',
|
||||
delNodeText: 'delete dept',
|
||||
view: 'tree/table view',
|
||||
tenantNodeErrorText: 'The current node cannot be operated. You need to maintain it in tenant management',
|
||||
},
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
export default {
|
||||
sysdept: {
|
||||
name: '部门名称',
|
||||
parentId: '上级部门',
|
||||
createTime: '创建时间',
|
||||
weight: '排序',
|
||||
sortOrder: '排序',
|
||||
leaderId: '部门负责人',
|
||||
inputdeptNameTip: '请输入部门名称',
|
||||
inputnameTip: '请输入部门名称',
|
||||
inputLeaderIdTip: '请输入部门领导',
|
||||
inputparentIdTip: '请选择上级部门',
|
||||
inputsortOrderTip: '请输入排序',
|
||||
importTip: '导入部门',
|
||||
addNodeText: '添加部门',
|
||||
editNodeText: '编辑部门',
|
||||
delNodeText: '删除部门',
|
||||
tenantNodeErrorText: '当前节点不可操作,请在租户管理功能中维护',
|
||||
view: '树/表视图'
|
||||
},
|
||||
sysdept: {
|
||||
name: '部门名称',
|
||||
parentId: '上级部门',
|
||||
createTime: '创建时间',
|
||||
weight: '排序',
|
||||
sortOrder: '排序',
|
||||
leaderId: '部门负责人',
|
||||
inputdeptNameTip: '请输入部门名称',
|
||||
inputnameTip: '请输入部门名称',
|
||||
inputLeaderIdTip: '请输入部门领导',
|
||||
inputparentIdTip: '请选择上级部门',
|
||||
inputsortOrderTip: '请输入排序',
|
||||
importTip: '导入部门',
|
||||
addNodeText: '添加部门',
|
||||
editNodeText: '编辑部门',
|
||||
delNodeText: '删除部门',
|
||||
tenantNodeErrorText: '当前节点不可操作,请在租户管理功能中维护',
|
||||
view: '树/表视图',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -130,6 +130,6 @@ const resetQuery = () => {
|
||||
</script>
|
||||
<style scoped>
|
||||
:deep(.el-table__body tr td) {
|
||||
text-align: left !important;
|
||||
text-align: left !important;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -1,50 +1,45 @@
|
||||
<template>
|
||||
<el-table
|
||||
ref="tableRef"
|
||||
:data="state.dataList"
|
||||
v-loading="state.loading"
|
||||
style="width: 100%"
|
||||
row-key="id"
|
||||
default-expand-all
|
||||
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
|
||||
border
|
||||
:cell-style="tableStyle.cellStyle"
|
||||
:header-cell-style="tableStyle?.headerCellStyle"
|
||||
>
|
||||
<el-table-column :label="$t('sysdept.name')" prop="name" width="400" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column :label="$t('sysdept.weight')" prop="weight" show-overflow-tooltip width="80"></el-table-column>
|
||||
<el-table-column prop="createTime" :label="$t('sysdept.createTime')" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column :label="$t('common.action')" show-overflow-tooltip width="250">
|
||||
<template #default="scope">
|
||||
<el-button text type="primary" icon="folder-add" @click="deptDialogRef.openDialog('add', scope.row?.id)"
|
||||
v-auth="'sys_dept_add'">
|
||||
{{ $t('common.addBtn') }}
|
||||
</el-button
|
||||
>
|
||||
<el-button text type="primary" icon="edit-pen" @click="deptDialogRef.openDialog('edit', scope.row?.id)"
|
||||
v-auth="'sys_dept_edit'">{{
|
||||
$t('common.editBtn')
|
||||
}}
|
||||
</el-button>
|
||||
<el-button text type="primary" icon="delete" @click="handleDelete(scope.row)" v-auth="'sys_dept_del'">
|
||||
{{ $t('common.delBtn') }}
|
||||
</el-button
|
||||
>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<dept-form ref="deptDialogRef" @refresh="getDataList()"/>
|
||||
<el-table
|
||||
ref="tableRef"
|
||||
:data="state.dataList"
|
||||
v-loading="state.loading"
|
||||
style="width: 100%"
|
||||
row-key="id"
|
||||
default-expand-all
|
||||
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
|
||||
border
|
||||
:cell-style="tableStyle.cellStyle"
|
||||
:header-cell-style="tableStyle?.headerCellStyle"
|
||||
>
|
||||
<el-table-column :label="$t('sysdept.name')" prop="name" width="400" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column :label="$t('sysdept.weight')" prop="weight" show-overflow-tooltip width="80"></el-table-column>
|
||||
<el-table-column prop="createTime" :label="$t('sysdept.createTime')" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column :label="$t('common.action')" show-overflow-tooltip width="250">
|
||||
<template #default="scope">
|
||||
<el-button text type="primary" icon="folder-add" @click="deptDialogRef.openDialog('add', scope.row?.id)" v-auth="'sys_dept_add'">
|
||||
{{ $t('common.addBtn') }}
|
||||
</el-button>
|
||||
<el-button text type="primary" icon="edit-pen" @click="deptDialogRef.openDialog('edit', scope.row?.id)" v-auth="'sys_dept_edit'"
|
||||
>{{ $t('common.editBtn') }}
|
||||
</el-button>
|
||||
<el-button text type="primary" icon="delete" @click="handleDelete(scope.row)" v-auth="'sys_dept_del'">
|
||||
{{ $t('common.delBtn') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<dept-form ref="deptDialogRef" @refresh="getDataList()" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="systemDept">
|
||||
import {BasicTableProps, useTable} from '/@/hooks/table';
|
||||
import {deptTree, delObj} from '/@/api/admin/dept';
|
||||
import {useMessage, useMessageBox} from '/@/hooks/message';
|
||||
import {useI18n} from 'vue-i18n';
|
||||
import { BasicTableProps, useTable } from '/@/hooks/table';
|
||||
import { deptTree, delObj } from '/@/api/admin/dept';
|
||||
import { useMessage, useMessageBox } from '/@/hooks/message';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
// 引入组件
|
||||
const DeptForm = defineAsyncComponent(() => import('./form.vue'));
|
||||
const {t} = useI18n();
|
||||
const { t } = useI18n();
|
||||
// 定义变量内容
|
||||
const tableRef = ref(); // 表格引用
|
||||
const deptDialogRef = ref(); // 部门对话框引用
|
||||
@@ -58,33 +53,33 @@ const isExpand = ref(false); // 是否展开
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
const queryDeptTree = (params?: any) => {
|
||||
return deptTree(params);
|
||||
return deptTree(params);
|
||||
};
|
||||
|
||||
/**
|
||||
* 定义响应式表格数据
|
||||
*/
|
||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||
pageList: queryDeptTree, // 页面列表数据
|
||||
queryForm: {
|
||||
deptName: '', // 部门名称
|
||||
},
|
||||
isPage: false, // 是否分页
|
||||
descs: ['create_time'], // 排序字段
|
||||
pageList: queryDeptTree, // 页面列表数据
|
||||
queryForm: {
|
||||
deptName: '', // 部门名称
|
||||
},
|
||||
isPage: false, // 是否分页
|
||||
descs: ['create_time'], // 排序字段
|
||||
});
|
||||
|
||||
/**
|
||||
* 使用 useTable 定义表格相关操作
|
||||
*/
|
||||
const {getDataList, tableStyle} = useTable(state);
|
||||
const { getDataList, tableStyle } = useTable(state);
|
||||
|
||||
/**
|
||||
* 展开/折叠部门树方法
|
||||
*/
|
||||
const handleExpand = async () => {
|
||||
isExpand.value = !isExpand.value;
|
||||
const dataList = await deptTree();
|
||||
toggleExpand(dataList.data, isExpand.value);
|
||||
isExpand.value = !isExpand.value;
|
||||
const dataList = await deptTree();
|
||||
toggleExpand(dataList.data, isExpand.value);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -93,12 +88,12 @@ const handleExpand = async () => {
|
||||
* @param unfold - 是否展开
|
||||
*/
|
||||
const toggleExpand = (children: any[], unfold = true) => {
|
||||
for (const key in children) {
|
||||
tableRef.value?.toggleRowExpansion(children[key], unfold);
|
||||
if (children[key].children) {
|
||||
toggleExpand(children[key].children!, unfold);
|
||||
}
|
||||
}
|
||||
for (const key in children) {
|
||||
tableRef.value?.toggleRowExpansion(children[key], unfold);
|
||||
if (children[key].children) {
|
||||
toggleExpand(children[key].children!, unfold);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -106,32 +101,32 @@ const toggleExpand = (children: any[], unfold = true) => {
|
||||
* @param row - 当前行数据
|
||||
*/
|
||||
const handleDelete = async (row: any) => {
|
||||
try {
|
||||
await useMessageBox().confirm(t('common.delConfirmText'));
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await useMessageBox().confirm(t('common.delConfirmText'));
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await delObj(row.id);
|
||||
getDataList();
|
||||
useMessage().success(t('common.delSuccessText'));
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg);
|
||||
}
|
||||
try {
|
||||
await delObj(row.id);
|
||||
getDataList();
|
||||
useMessage().success(t('common.delSuccessText'));
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg);
|
||||
}
|
||||
};
|
||||
|
||||
const handleAdd = ()=>{
|
||||
deptDialogRef.value.openDialog('add')
|
||||
}
|
||||
const handleAdd = () => {
|
||||
deptDialogRef.value.openDialog('add');
|
||||
};
|
||||
|
||||
/**
|
||||
* 暴露组件中的一些方法和变量
|
||||
*/
|
||||
defineExpose({
|
||||
handleAdd, // 新增时间
|
||||
state, // 响应式表格数据
|
||||
getDataList, // 获取列表数据方法
|
||||
handleExpand // 展开/折叠部门树方法
|
||||
handleAdd, // 新增时间
|
||||
state, // 响应式表格数据
|
||||
getDataList, // 获取列表数据方法
|
||||
handleExpand, // 展开/折叠部门树方法
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -1,127 +1,134 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
:title="state.ruleForm.menuId ? $t('common.editBtn') : $t('common.addBtn')"
|
||||
v-model="visible"
|
||||
width="600"
|
||||
:close-on-click-modal="false"
|
||||
draggable
|
||||
>
|
||||
<el-form ref="menuDialogFormRef" :model="state.ruleForm" :rules="dataRules" label-width="100px" v-loading="loading">
|
||||
<el-form-item :label="$t('sysmenu.menuType')" prop="menuType">
|
||||
<el-radio-group v-model="state.ruleForm.menuType">
|
||||
<el-radio border label="0">菜单</el-radio>
|
||||
<el-radio border label="1">按钮</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('sysmenu.parentId')" prop="parentId">
|
||||
<el-tree-select
|
||||
v-model="state.ruleForm.parentId"
|
||||
:data="state.parentData"
|
||||
:render-after-expand="false"
|
||||
:props="{ value: 'id', label: 'name', children: 'children' }"
|
||||
class="w100"
|
||||
clearable
|
||||
check-strictly
|
||||
:placeholder="$t('sysmenu.inputParentIdTip')"
|
||||
>
|
||||
</el-tree-select>
|
||||
</el-form-item>
|
||||
<el-form-item prop="name">
|
||||
<template #label>
|
||||
{{ state.ruleForm.menuType === '0' ? t('sysmenu.name') : t('sysmenu.buttonName') }}
|
||||
</template>
|
||||
<el-input v-model="state.ruleForm.name" clearable :placeholder="$t('sysmenu.inputNameTip')"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('sysmenu.path')" prop="path" v-if="state.ruleForm.menuType === '0'">
|
||||
<el-input v-model="state.ruleForm.path" :placeholder="$t('sysmenu.inputPathTip')"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('sysmenu.permission')" prop="permission" v-if="state.ruleForm.menuType === '1'">
|
||||
<template #label>
|
||||
{{ t('sysmenu.permission') }}
|
||||
<tip content="对应后台接口@PreAuthorize注解入参字符串"></tip>
|
||||
</template>
|
||||
<el-input v-model="state.ruleForm.permission" maxlength="100" :placeholder="$t('sysmenu.inputPermissionTip')"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('sysmenu.sortOrder')" prop="sortOrder">
|
||||
<el-input-number v-model="state.ruleForm.sortOrder" :min="0" controls-position="right"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('sysmenu.icon')" prop="icon" v-if="state.ruleForm.menuType === '0'">
|
||||
<IconSelector :placeholder="$t('sysmenu.inputIconTip')" v-model="state.ruleForm.icon"/>
|
||||
</el-form-item>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="keepAlive" v-if="state.ruleForm.menuType === '0'">
|
||||
<template #label> {{ $t('sysmenu.keepAlive') }}
|
||||
<tip content="组件保留状态,避免重新渲染"/>
|
||||
</template>
|
||||
<el-radio-group v-model="state.ruleForm.keepAlive">
|
||||
<el-radio border label="0">否</el-radio>
|
||||
<el-radio border label="1">是</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="visible" v-if="state.ruleForm.menuType === '0'">
|
||||
<template #label> {{ $t('sysmenu.visible') }}
|
||||
<tip content="左侧菜单树是否显示"/>
|
||||
</template>
|
||||
<el-radio-group v-model="state.ruleForm.visible">
|
||||
<el-radio border label="0">否</el-radio>
|
||||
<el-radio border label="1">是</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row class="mt-4">
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="param" v-if="state.ruleForm.menuType === '0'">
|
||||
<template #label> {{ $t('sysmenu.param') }}
|
||||
<tip content="多个路径指向同一个组件"/>
|
||||
</template>
|
||||
<el-radio-group v-model="state.ruleForm.param">
|
||||
<el-radio border label="0">否</el-radio>
|
||||
<el-radio border label="1">是</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="embedded"
|
||||
v-if="state.ruleForm.menuType === '0' && state.ruleForm.path?.startsWith('http')">
|
||||
<template #label> {{ $t('sysmenu.embedded') }}
|
||||
<tip content="iframe嵌套还是打开独立的Tab"/>
|
||||
</template>
|
||||
<el-radio-group v-model="state.ruleForm.embedded">
|
||||
<el-radio border label="0">否</el-radio>
|
||||
<el-radio border label="1">是</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-dialog
|
||||
:title="state.ruleForm.menuId ? $t('common.editBtn') : $t('common.addBtn')"
|
||||
v-model="visible"
|
||||
width="600"
|
||||
:close-on-click-modal="false"
|
||||
draggable
|
||||
>
|
||||
<el-form ref="menuDialogFormRef" :model="state.ruleForm" :rules="dataRules" label-width="100px" v-loading="loading">
|
||||
<el-form-item :label="$t('sysmenu.menuType')" prop="menuType">
|
||||
<el-radio-group v-model="state.ruleForm.menuType">
|
||||
<el-radio border label="0">菜单</el-radio>
|
||||
<el-radio border label="1">按钮</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('sysmenu.parentId')" prop="parentId">
|
||||
<el-tree-select
|
||||
v-model="state.ruleForm.parentId"
|
||||
:data="state.parentData"
|
||||
:render-after-expand="false"
|
||||
:props="{ value: 'id', label: 'name', children: 'children' }"
|
||||
class="w100"
|
||||
clearable
|
||||
check-strictly
|
||||
:placeholder="$t('sysmenu.inputParentIdTip')"
|
||||
>
|
||||
</el-tree-select>
|
||||
</el-form-item>
|
||||
<el-form-item prop="name">
|
||||
<template #label>
|
||||
{{ state.ruleForm.menuType === '0' ? t('sysmenu.name') : t('sysmenu.buttonName') }}
|
||||
</template>
|
||||
<el-input v-model="state.ruleForm.name" clearable :placeholder="$t('sysmenu.inputNameTip')"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('sysmenu.path')" prop="path" v-if="state.ruleForm.menuType === '0'">
|
||||
<el-input v-model="state.ruleForm.path" :placeholder="$t('sysmenu.inputPathTip')" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('sysmenu.permission')" prop="permission" v-if="state.ruleForm.menuType === '1'">
|
||||
<template #label>
|
||||
{{ t('sysmenu.permission') }}
|
||||
<tip content="对应后台接口@PreAuthorize注解入参字符串"></tip>
|
||||
</template>
|
||||
<el-input v-model="state.ruleForm.permission" maxlength="100" :placeholder="$t('sysmenu.inputPermissionTip')" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('sysmenu.sortOrder')" prop="sortOrder">
|
||||
<el-input-number v-model="state.ruleForm.sortOrder" :min="0" controls-position="right" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('sysmenu.icon')" prop="icon" v-if="state.ruleForm.menuType === '0'">
|
||||
<IconSelector :placeholder="$t('sysmenu.inputIconTip')" v-model="state.ruleForm.icon" />
|
||||
</el-form-item>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="keepAlive" v-if="state.ruleForm.menuType === '0'">
|
||||
<template #label>
|
||||
{{ $t('sysmenu.keepAlive') }}
|
||||
<tip content="组件保留状态,避免重新渲染" />
|
||||
</template>
|
||||
<el-radio-group v-model="state.ruleForm.keepAlive">
|
||||
<el-radio border label="0">否</el-radio>
|
||||
<el-radio border label="1">是</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="visible" v-if="state.ruleForm.menuType === '0'">
|
||||
<template #label>
|
||||
{{ $t('sysmenu.visible') }}
|
||||
<tip content="左侧菜单树是否显示" />
|
||||
</template>
|
||||
<el-radio-group v-model="state.ruleForm.visible">
|
||||
<el-radio border label="0">否</el-radio>
|
||||
<el-radio border label="1">是</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row class="mt-4">
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="param" v-if="state.ruleForm.menuType === '0'">
|
||||
<template #label>
|
||||
{{ $t('sysmenu.param') }}
|
||||
<tip content="多个路径指向同一个组件" />
|
||||
</template>
|
||||
<el-radio-group v-model="state.ruleForm.param">
|
||||
<el-radio border label="0">否</el-radio>
|
||||
<el-radio border label="1">是</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="embedded" v-if="state.ruleForm.menuType === '0' && state.ruleForm.path?.startsWith('http')">
|
||||
<template #label>
|
||||
{{ $t('sysmenu.embedded') }}
|
||||
<tip content="iframe嵌套还是打开独立的Tab" />
|
||||
</template>
|
||||
<el-radio-group v-model="state.ruleForm.embedded">
|
||||
<el-radio border label="0">否</el-radio>
|
||||
<el-radio border label="1">是</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-form-item class="mt-4" :label="$t('sysmenu.component')" prop="component" v-if="state.ruleForm.menuType === '0'
|
||||
&& state.ruleForm.param === '1'">
|
||||
<el-input v-model="state.ruleForm.component" :placeholder="$t('sysmenu.inputComponentTip')"/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-form-item
|
||||
class="mt-4"
|
||||
:label="$t('sysmenu.component')"
|
||||
prop="component"
|
||||
v-if="state.ruleForm.menuType === '0' && state.ruleForm.param === '1'"
|
||||
>
|
||||
<el-input v-model="state.ruleForm.component" :placeholder="$t('sysmenu.inputComponentTip')" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="visible = false">{{ $t('common.cancelButtonText') }}</el-button>
|
||||
<el-button type="primary" @click="onSubmit" :disabled="loading">{{ $t('common.confirmButtonText') }}</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="systemMenuDialog">
|
||||
import {useI18n} from 'vue-i18n';
|
||||
import {getObj, pageList, putObj, addObj, validateExist} from '/@/api/admin/menu';
|
||||
import {useMessage} from '/@/hooks/message';
|
||||
import {rule, validateNull} from "/@/utils/validate";
|
||||
import Tip from "/@/components/Tip/index.vue";
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { getObj, pageList, putObj, addObj, validateExist } from '/@/api/admin/menu';
|
||||
import { useMessage } from '/@/hooks/message';
|
||||
import { rule, validateNull } from '/@/utils/validate';
|
||||
import Tip from '/@/components/Tip/index.vue';
|
||||
|
||||
// 定义子组件向父组件传值/事件
|
||||
const emit = defineEmits(['refresh']);
|
||||
const {t} = useI18n();
|
||||
const { t } = useI18n();
|
||||
// 引入组件
|
||||
const IconSelector = defineAsyncComponent(() => import('/@/components/IconSelector/index.vue'));
|
||||
|
||||
@@ -132,148 +139,160 @@ const menuDialogFormRef = ref();
|
||||
const originalName = ref(''); // To store the original menu name for comparison during edits
|
||||
// 定义需要的数据
|
||||
const state = reactive({
|
||||
ruleForm: {
|
||||
menuId: '',
|
||||
name: '',
|
||||
permission: '',
|
||||
parentId: '',
|
||||
icon: '',
|
||||
path: '',
|
||||
param: '0',
|
||||
component: '',
|
||||
sortOrder: 0,
|
||||
menuType: '1',
|
||||
keepAlive: '0',
|
||||
visible: '1',
|
||||
embedded: '0',
|
||||
},
|
||||
parentData: [] as any[], // 上级菜单数据
|
||||
ruleForm: {
|
||||
menuId: '',
|
||||
name: '',
|
||||
permission: '',
|
||||
parentId: '',
|
||||
icon: '',
|
||||
path: '',
|
||||
param: '0',
|
||||
component: '',
|
||||
sortOrder: 0,
|
||||
menuType: '1',
|
||||
keepAlive: '0',
|
||||
visible: '1',
|
||||
embedded: '0',
|
||||
},
|
||||
parentData: [] as any[], // 上级菜单数据
|
||||
});
|
||||
|
||||
// 表单校验规则
|
||||
const dataRules = reactive({
|
||||
menuType: [{required: true, message: '菜单类型不能为空', trigger: 'blur'}],
|
||||
parentId: [{required: true, message: '上级菜单不能为空', trigger: 'blur'}],
|
||||
name: [{validator: rule.overLength, trigger: 'blur'}, {
|
||||
required: true,
|
||||
message: '菜单不能为空',
|
||||
trigger: 'blur'
|
||||
}, {
|
||||
validator: (rule: any, value: any, callback: any) => {
|
||||
// 如果是按钮类型菜单,跳过名称唯一性校验
|
||||
if (state.ruleForm.menuType === '1') {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
// 如果是编辑状态且菜单名称未改变,跳过校验
|
||||
if (state.ruleForm.menuId !== '' && value === originalName.value) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
// 其他情况下,验证菜单名称唯一性
|
||||
validateExist(rule, value, callback, false);
|
||||
},
|
||||
trigger: 'blur',
|
||||
}],
|
||||
path: [{validator: rule.overLength, trigger: 'blur'}, {required: true, message: '路径不能为空', trigger: 'blur'}],
|
||||
permission: [{validator: rule.overLength, trigger: 'blur'}, {
|
||||
required: true,
|
||||
message: '权限代码不能为空',
|
||||
trigger: 'blur'
|
||||
}, {
|
||||
validator: (rule: any, value: any, callback: any) => {
|
||||
validateExist(rule, value, callback, state.ruleForm.menuId !== '');
|
||||
},
|
||||
trigger: 'blur',
|
||||
}],
|
||||
sortOrder: [{required: true, message: '排序不能为空', trigger: 'blur'}],
|
||||
component: [{min: 5, max: 255, message: '组件名称长度必须介于 5 和 255 之间', trigger: 'blur'},
|
||||
{
|
||||
validator: (rule: any, value: any, callback: any) => {
|
||||
if (state.ruleForm.menuType === '0' && state.ruleForm.param === '1' && validateNull(state.ruleForm.component)) {
|
||||
callback(new Error('请输入组件名称'));
|
||||
} else {
|
||||
return callback();
|
||||
}
|
||||
},
|
||||
trigger: 'blur',
|
||||
}],
|
||||
menuType: [{ required: true, message: '菜单类型不能为空', trigger: 'blur' }],
|
||||
parentId: [{ required: true, message: '上级菜单不能为空', trigger: 'blur' }],
|
||||
name: [
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{
|
||||
required: true,
|
||||
message: '菜单不能为空',
|
||||
trigger: 'blur',
|
||||
},
|
||||
{
|
||||
validator: (rule: any, value: any, callback: any) => {
|
||||
// 如果是按钮类型菜单,跳过名称唯一性校验
|
||||
if (state.ruleForm.menuType === '1') {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
// 如果是编辑状态且菜单名称未改变,跳过校验
|
||||
if (state.ruleForm.menuId !== '' && value === originalName.value) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
// 其他情况下,验证菜单名称唯一性
|
||||
validateExist(rule, value, callback, false);
|
||||
},
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
path: [
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{ required: true, message: '路径不能为空', trigger: 'blur' },
|
||||
],
|
||||
permission: [
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{
|
||||
required: true,
|
||||
message: '权限代码不能为空',
|
||||
trigger: 'blur',
|
||||
},
|
||||
{
|
||||
validator: (rule: any, value: any, callback: any) => {
|
||||
validateExist(rule, value, callback, state.ruleForm.menuId !== '');
|
||||
},
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
sortOrder: [{ required: true, message: '排序不能为空', trigger: 'blur' }],
|
||||
component: [
|
||||
{ min: 5, max: 255, message: '组件名称长度必须介于 5 和 255 之间', trigger: 'blur' },
|
||||
{
|
||||
validator: (rule: any, value: any, callback: any) => {
|
||||
if (state.ruleForm.menuType === '0' && state.ruleForm.param === '1' && validateNull(state.ruleForm.component)) {
|
||||
callback(new Error('请输入组件名称'));
|
||||
} else {
|
||||
return callback();
|
||||
}
|
||||
},
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// 打开弹窗
|
||||
const openDialog = (type: string, row?: any) => {
|
||||
state.ruleForm.menuId = '';
|
||||
visible.value = true;
|
||||
originalName.value = ''; // Reset the original name
|
||||
state.ruleForm.menuId = '';
|
||||
visible.value = true;
|
||||
originalName.value = ''; // Reset the original name
|
||||
|
||||
nextTick(() => {
|
||||
menuDialogFormRef.value?.resetFields();
|
||||
state.ruleForm.parentId = row?.id || '-1';
|
||||
});
|
||||
nextTick(() => {
|
||||
menuDialogFormRef.value?.resetFields();
|
||||
state.ruleForm.parentId = row?.id || '-1';
|
||||
});
|
||||
|
||||
if (row?.id && type === 'edit') {
|
||||
state.ruleForm.menuId = row.id;
|
||||
// 获取当前节点菜单信息
|
||||
getMenuDetail(row.id);
|
||||
}
|
||||
// 渲染上级菜单列表树
|
||||
getAllMenuData();
|
||||
if (row?.id && type === 'edit') {
|
||||
state.ruleForm.menuId = row.id;
|
||||
// 获取当前节点菜单信息
|
||||
getMenuDetail(row.id);
|
||||
}
|
||||
// 渲染上级菜单列表树
|
||||
getAllMenuData();
|
||||
};
|
||||
|
||||
// 获取菜单节点的详细信息
|
||||
const getMenuDetail = (id: string) => {
|
||||
getObj({menuId: id}).then((res) => {
|
||||
if (res.data[0].component) {
|
||||
state.ruleForm.param = '1'
|
||||
}
|
||||
originalName.value = res.data[0].name; // Store the original name
|
||||
Object.assign(state.ruleForm, res.data[0]);
|
||||
});
|
||||
getObj({ menuId: id }).then((res) => {
|
||||
if (res.data[0].component) {
|
||||
state.ruleForm.param = '1';
|
||||
}
|
||||
originalName.value = res.data[0].name; // Store the original name
|
||||
Object.assign(state.ruleForm, res.data[0]);
|
||||
});
|
||||
};
|
||||
|
||||
// 从后端获取菜单信息(含层级)
|
||||
const getAllMenuData = () => {
|
||||
state.parentData = [];
|
||||
pageList({
|
||||
type: '0',
|
||||
}).then((res) => {
|
||||
let menu = {
|
||||
id: '-1',
|
||||
name: '根菜单',
|
||||
children: [],
|
||||
};
|
||||
menu.children = res.data;
|
||||
state.parentData.push(menu);
|
||||
});
|
||||
state.parentData = [];
|
||||
pageList({
|
||||
type: '0',
|
||||
}).then((res) => {
|
||||
let menu = {
|
||||
id: '-1',
|
||||
name: '根菜单',
|
||||
children: [],
|
||||
};
|
||||
menu.children = res.data;
|
||||
state.parentData.push(menu);
|
||||
});
|
||||
};
|
||||
|
||||
// 保存数据
|
||||
const onSubmit = async () => {
|
||||
// 立即设置 loading,防止重复点击
|
||||
if (loading.value) return;
|
||||
loading.value = true;
|
||||
|
||||
try {
|
||||
const valid = await menuDialogFormRef.value.validate().catch(() => {
|
||||
});
|
||||
if (!valid) {
|
||||
loading.value = false;
|
||||
return false;
|
||||
}
|
||||
// 立即设置 loading,防止重复点击
|
||||
if (loading.value) return;
|
||||
loading.value = true;
|
||||
|
||||
state.ruleForm.menuId ? await putObj(state.ruleForm) : await addObj(state.ruleForm);
|
||||
useMessage().success(t(state.ruleForm.menuId ? 'common.editSuccessText' : 'common.addSuccessText'));
|
||||
visible.value = false;
|
||||
emit('refresh');
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
try {
|
||||
const valid = await menuDialogFormRef.value.validate().catch(() => {});
|
||||
if (!valid) {
|
||||
loading.value = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
state.ruleForm.menuId ? await putObj(state.ruleForm) : await addObj(state.ruleForm);
|
||||
useMessage().success(t(state.ruleForm.menuId ? 'common.editSuccessText' : 'common.addSuccessText'));
|
||||
visible.value = false;
|
||||
emit('refresh');
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 暴露变量 只有暴漏出来的变量 父组件才能使用
|
||||
defineExpose({
|
||||
openDialog,
|
||||
openDialog,
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -1,37 +1,37 @@
|
||||
export default {
|
||||
sysmenu: {
|
||||
index: '#',
|
||||
name: 'menu name',
|
||||
buttonName: 'button name',
|
||||
sortOrder: 'sortOrder',
|
||||
path: 'path',
|
||||
menuType: 'menuType',
|
||||
keepAlive: 'keepAlive',
|
||||
permission: 'permission',
|
||||
inputNameTip: 'input name',
|
||||
parentId: 'parent menu',
|
||||
embedded: 'embedded',
|
||||
param: 'param',
|
||||
component: 'component',
|
||||
visible: 'visible',
|
||||
icon: 'icon',
|
||||
inputMenuIdTip: 'input menuId',
|
||||
inputPermissionTip: 'input permission',
|
||||
inputPathTip: 'input path',
|
||||
inputParentIdTip: 'input parentId',
|
||||
inputIconTip: 'input icon',
|
||||
inputVisibleTip: 'input visible',
|
||||
inputSortOrderTip: 'input sortOrder',
|
||||
inputKeepAliveTip: 'input keepAlive',
|
||||
inputMenuTypeTip: 'input menuType',
|
||||
inputCreateByTip: 'input createBy',
|
||||
inputCreateTimeTip: 'input createTime',
|
||||
inputUpdateByTip: 'input updateBy',
|
||||
inputUpdateTimeTip: 'input updateTime',
|
||||
inputDelFlagTip: 'input delFlag',
|
||||
inputTenantIdTip: 'input tenantId',
|
||||
inputEmbeddedTip: 'input embedded',
|
||||
inputComponentTip: 'input component',
|
||||
deleteDisabledTip: 'menu inclusion subordinates cannot be deleted',
|
||||
},
|
||||
sysmenu: {
|
||||
index: '#',
|
||||
name: 'menu name',
|
||||
buttonName: 'button name',
|
||||
sortOrder: 'sortOrder',
|
||||
path: 'path',
|
||||
menuType: 'menuType',
|
||||
keepAlive: 'keepAlive',
|
||||
permission: 'permission',
|
||||
inputNameTip: 'input name',
|
||||
parentId: 'parent menu',
|
||||
embedded: 'embedded',
|
||||
param: 'param',
|
||||
component: 'component',
|
||||
visible: 'visible',
|
||||
icon: 'icon',
|
||||
inputMenuIdTip: 'input menuId',
|
||||
inputPermissionTip: 'input permission',
|
||||
inputPathTip: 'input path',
|
||||
inputParentIdTip: 'input parentId',
|
||||
inputIconTip: 'input icon',
|
||||
inputVisibleTip: 'input visible',
|
||||
inputSortOrderTip: 'input sortOrder',
|
||||
inputKeepAliveTip: 'input keepAlive',
|
||||
inputMenuTypeTip: 'input menuType',
|
||||
inputCreateByTip: 'input createBy',
|
||||
inputCreateTimeTip: 'input createTime',
|
||||
inputUpdateByTip: 'input updateBy',
|
||||
inputUpdateTimeTip: 'input updateTime',
|
||||
inputDelFlagTip: 'input delFlag',
|
||||
inputTenantIdTip: 'input tenantId',
|
||||
inputEmbeddedTip: 'input embedded',
|
||||
inputComponentTip: 'input component',
|
||||
deleteDisabledTip: 'menu inclusion subordinates cannot be deleted',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,31 +1,31 @@
|
||||
export default {
|
||||
sysmenu: {
|
||||
index: '#',
|
||||
name: '菜单名称',
|
||||
buttonName: '按钮名称',
|
||||
sortOrder: '排序',
|
||||
path: '路由',
|
||||
menuType: '类型',
|
||||
keepAlive: '缓冲',
|
||||
permission: '权限标识',
|
||||
inputNameTip: '请输入菜单名称',
|
||||
parentId: '上级菜单',
|
||||
embedded: '内嵌',
|
||||
param: '带参',
|
||||
component: '组件',
|
||||
visible: '显示',
|
||||
icon: '图标',
|
||||
inputMenuIdTip: '',
|
||||
inputPermissionTip: '请输入权限标识',
|
||||
inputPathTip: '请输入路由路径',
|
||||
inputParentIdTip: '请选择上级菜单',
|
||||
inputIconTip: '请选择图标',
|
||||
inputVisibleTip: '请选择是否显示',
|
||||
inputSortOrderTip: '请输入排序',
|
||||
inputKeepAliveTip: '请选择是否缓冲',
|
||||
inputMenuTypeTip: '请选择菜单类型',
|
||||
inputEmbeddedTip: '请选择是否内嵌',
|
||||
inputComponentTip: '请输入组件名称',
|
||||
deleteDisabledTip: '菜单包含下级不能删除',
|
||||
},
|
||||
sysmenu: {
|
||||
index: '#',
|
||||
name: '菜单名称',
|
||||
buttonName: '按钮名称',
|
||||
sortOrder: '排序',
|
||||
path: '路由',
|
||||
menuType: '类型',
|
||||
keepAlive: '缓冲',
|
||||
permission: '权限标识',
|
||||
inputNameTip: '请输入菜单名称',
|
||||
parentId: '上级菜单',
|
||||
embedded: '内嵌',
|
||||
param: '带参',
|
||||
component: '组件',
|
||||
visible: '显示',
|
||||
icon: '图标',
|
||||
inputMenuIdTip: '',
|
||||
inputPermissionTip: '请输入权限标识',
|
||||
inputPathTip: '请输入路由路径',
|
||||
inputParentIdTip: '请选择上级菜单',
|
||||
inputIconTip: '请选择图标',
|
||||
inputVisibleTip: '请选择是否显示',
|
||||
inputSortOrderTip: '请输入排序',
|
||||
inputKeepAliveTip: '请选择是否缓冲',
|
||||
inputMenuTypeTip: '请选择菜单类型',
|
||||
inputEmbeddedTip: '请选择是否内嵌',
|
||||
inputComponentTip: '请输入组件名称',
|
||||
deleteDisabledTip: '菜单包含下级不能删除',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -198,6 +198,6 @@ const handleDelete = async (row: any) => {
|
||||
|
||||
<style scoped>
|
||||
:deep(.el-table__body tr td) {
|
||||
text-align: left !important;
|
||||
text-align: left !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
import { useMessage } from '/@/hooks/message';
|
||||
import { getObj, addObj, putObj, validatePostCode, validatePostName } from '/@/api/admin/post';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import {rule} from "/@/utils/validate";
|
||||
import { rule } from '/@/utils/validate';
|
||||
|
||||
// 定义子组件向父组件传值/事件
|
||||
const emit = defineEmits(['refresh']);
|
||||
@@ -56,7 +56,7 @@ const form = reactive({
|
||||
// 定义校验规则
|
||||
const dataRules = ref({
|
||||
postCode: [
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{ required: true, message: '岗位编码不能为空', trigger: 'blur' },
|
||||
{
|
||||
validator: (rule: any, value: any, callback: any) => {
|
||||
@@ -66,7 +66,7 @@ const dataRules = ref({
|
||||
},
|
||||
],
|
||||
postName: [
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{ required: true, message: '岗位名称不能为空', trigger: 'blur' },
|
||||
{
|
||||
validator: (rule: any, value: any, callback: any) => {
|
||||
@@ -75,8 +75,14 @@ const dataRules = ref({
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
postSort: [{ validator: rule.overLength, trigger: 'blur' },{ required: true, message: '岗位排序不能为空', trigger: 'blur' }],
|
||||
remark: [{ validator: rule.overLength, trigger: 'blur' },{ required: true, message: '岗位描述不能为空', trigger: 'blur' }],
|
||||
postSort: [
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{ required: true, message: '岗位排序不能为空', trigger: 'blur' },
|
||||
],
|
||||
remark: [
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{ required: true, message: '岗位描述不能为空', trigger: 'blur' },
|
||||
],
|
||||
});
|
||||
|
||||
// 打开弹窗
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
@selection-change="handleSelectionChange"
|
||||
style="width: 100%"
|
||||
v-loading="state.loading"
|
||||
row-key="postId"
|
||||
row-key="postId"
|
||||
border
|
||||
:cell-style="tableStyle?.cellStyle"
|
||||
:header-cell-style="tableStyle?.headerCellStyle"
|
||||
@@ -115,7 +115,7 @@ const resetQuery = () => {
|
||||
|
||||
// 导出excel
|
||||
const exportExcel = () => {
|
||||
downBlobFile('/admin/post/export', Object.assign(state.queryForm,{ids:selectObjs}), 'post.xlsx');
|
||||
downBlobFile('/admin/post/export', Object.assign(state.queryForm, { ids: selectObjs }), 'post.xlsx');
|
||||
};
|
||||
|
||||
// 多选事件
|
||||
|
||||
@@ -1,178 +1,169 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="visible"
|
||||
:title="dialogTitle"
|
||||
width="80%"
|
||||
:show-close="false"
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
:before-close="handleBeforeClose"
|
||||
center
|
||||
>
|
||||
<el-form>
|
||||
<el-form-item class="role-form-item">
|
||||
<el-radio-group v-model="radio" class="role-radio-group" @change="handleChangeRole">
|
||||
<template v-for="(roles, groupName) in allRoleGroups" :key="groupName">
|
||||
<el-card class="role-group-card" shadow="hover">
|
||||
<template #header>
|
||||
<span class="group-name">{{ groupName }}</span>
|
||||
</template>
|
||||
<div class="role-group">
|
||||
<el-radio-button
|
||||
v-for="item in roles"
|
||||
:key="item.roleCode"
|
||||
:label="item.roleCode"
|
||||
size="small"
|
||||
>
|
||||
{{ item.roleName }}
|
||||
</el-radio-button>
|
||||
</div>
|
||||
</el-card>
|
||||
</template>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-dialog
|
||||
v-model="visible"
|
||||
:title="dialogTitle"
|
||||
width="80%"
|
||||
:show-close="false"
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
:before-close="handleBeforeClose"
|
||||
center
|
||||
>
|
||||
<el-form>
|
||||
<el-form-item class="role-form-item">
|
||||
<el-radio-group v-model="radio" class="role-radio-group" @change="handleChangeRole">
|
||||
<template v-for="(roles, groupName) in allRoleGroups" :key="groupName">
|
||||
<el-card class="role-group-card" shadow="hover">
|
||||
<template #header>
|
||||
<span class="group-name">{{ groupName }}</span>
|
||||
</template>
|
||||
<div class="role-group">
|
||||
<el-radio-button v-for="item in roles" :key="item.roleCode" :label="item.roleCode" size="small">
|
||||
{{ item.roleName }}
|
||||
</el-radio-button>
|
||||
</div>
|
||||
</el-card>
|
||||
</template>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template v-if="!requireSelectToClose" #footer>
|
||||
<el-button @click="handleFooterClose">关 闭</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<template v-if="!requireSelectToClose" #footer>
|
||||
<el-button @click="handleFooterClose">关 闭</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, toRef } from 'vue'
|
||||
import { listAllRole } from '/@/api/admin/role'
|
||||
import { Local, Session } from '/@/utils/storage'
|
||||
import { useMessage } from '/@/hooks/message'
|
||||
import { ref, computed, toRef } from 'vue';
|
||||
import { listAllRole } from '/@/api/admin/role';
|
||||
import { Local, Session } from '/@/utils/storage';
|
||||
import { useMessage } from '/@/hooks/message';
|
||||
|
||||
/** 弹框标题,如「角色切换」「登录角色选择」 */
|
||||
const props = withDefaults(
|
||||
defineProps<{ title?: string; requireSelectToClose?: boolean }>(),
|
||||
{ title: '角色切换', requireSelectToClose: false }
|
||||
)
|
||||
const dialogTitle = computed(() => props.title)
|
||||
const props = withDefaults(defineProps<{ title?: string; requireSelectToClose?: boolean }>(), { title: '角色切换', requireSelectToClose: false });
|
||||
const dialogTitle = computed(() => props.title);
|
||||
|
||||
const visible = ref(false)
|
||||
const radio = ref('')
|
||||
const visible = ref(false);
|
||||
const radio = ref('');
|
||||
/** 按分组名分组的角色列表:{ "未分组": [{ roleId, roleName, roleCode, ... }], ... } */
|
||||
const allRoleGroups = ref<Record<string, any[]>>({})
|
||||
const requireSelectToClose = toRef(props, 'requireSelectToClose')
|
||||
const allRoleGroups = ref<Record<string, any[]>>({});
|
||||
const requireSelectToClose = toRef(props, 'requireSelectToClose');
|
||||
|
||||
const open = () => {
|
||||
if (visible.value) return
|
||||
visible.value = true
|
||||
listAllRole().then((res) => {
|
||||
allRoleGroups.value = res.data && typeof res.data === 'object' && !Array.isArray(res.data)
|
||||
? res.data
|
||||
: { '未分组': Array.isArray(res.data) ? res.data : [] }
|
||||
radio.value = Local.get('roleCode')
|
||||
})
|
||||
}
|
||||
if (visible.value) return;
|
||||
visible.value = true;
|
||||
listAllRole().then((res) => {
|
||||
allRoleGroups.value =
|
||||
res.data && typeof res.data === 'object' && !Array.isArray(res.data) ? res.data : { 未分组: Array.isArray(res.data) ? res.data : [] };
|
||||
radio.value = Local.get('roleCode');
|
||||
});
|
||||
};
|
||||
|
||||
/** 根据 roleCode 从分组数据中查找角色 */
|
||||
const findRoleByCode = (code: string) => {
|
||||
for (const roles of Object.values(allRoleGroups.value)) {
|
||||
if (!Array.isArray(roles)) continue
|
||||
const found = roles.find((r: any) => r.roleCode === code)
|
||||
if (found) return found
|
||||
}
|
||||
return null
|
||||
}
|
||||
for (const roles of Object.values(allRoleGroups.value)) {
|
||||
if (!Array.isArray(roles)) continue;
|
||||
const found = roles.find((r: any) => r.roleCode === code);
|
||||
if (found) return found;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const canClose = () => {
|
||||
if (!radio.value) {
|
||||
useMessage().warning('请选择一个角色')
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
if (!radio.value) {
|
||||
useMessage().warning('请选择一个角色');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
const handleBeforeClose = (done: () => void) => {
|
||||
if (requireSelectToClose.value) {
|
||||
useMessage().warning('请先选择登录角色')
|
||||
return
|
||||
}
|
||||
if (!canClose()) return
|
||||
done()
|
||||
}
|
||||
if (requireSelectToClose.value) {
|
||||
useMessage().warning('请先选择登录角色');
|
||||
return;
|
||||
}
|
||||
if (!canClose()) return;
|
||||
done();
|
||||
};
|
||||
|
||||
const handleFooterClose = () => {
|
||||
if (!canClose()) return
|
||||
visible.value = false
|
||||
}
|
||||
if (!canClose()) return;
|
||||
visible.value = false;
|
||||
};
|
||||
|
||||
const handleChangeRole = (label: string) => {
|
||||
const obj = findRoleByCode(label)
|
||||
if (!obj) return
|
||||
Local.set('roleCode', obj.roleCode)
|
||||
Local.set('roleName', obj.roleName)
|
||||
Local.set('roleId', obj.roleId)
|
||||
useMessage().success('操作成功')
|
||||
// 清掉 tags 缓存,重载后只保留首页 tag
|
||||
Session.remove('tagsViewList')
|
||||
// 清除 pinia 持久化的 tagsView 路由,避免重载后先恢复旧角色菜单再被新路由覆盖前就初始化出“不存在的 tag”
|
||||
try {
|
||||
window.localStorage.removeItem('tagsViewRoutes')
|
||||
} catch (_) {}
|
||||
setTimeout(() => {
|
||||
window.location.hash = '#/home'
|
||||
window.location.reload()
|
||||
}, 500)
|
||||
}
|
||||
const obj = findRoleByCode(label);
|
||||
if (!obj) return;
|
||||
Local.set('roleCode', obj.roleCode);
|
||||
Local.set('roleName', obj.roleName);
|
||||
Local.set('roleId', obj.roleId);
|
||||
useMessage().success('操作成功');
|
||||
// 清掉 tags 缓存,重载后只保留首页 tag
|
||||
Session.remove('tagsViewList');
|
||||
// 清除 pinia 持久化的 tagsView 路由,避免重载后先恢复旧角色菜单再被新路由覆盖前就初始化出“不存在的 tag”
|
||||
try {
|
||||
window.localStorage.removeItem('tagsViewRoutes');
|
||||
} catch (_) {}
|
||||
setTimeout(() => {
|
||||
window.location.hash = '#/home';
|
||||
window.location.reload();
|
||||
}, 500);
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
open
|
||||
})
|
||||
open,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.role-form-item {
|
||||
:deep(.el-form-item__content) {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
:deep(.el-form-item__content) {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
.role-radio-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
width: 100%;
|
||||
|
||||
:deep(.el-radio-button) {
|
||||
margin: 0;
|
||||
}
|
||||
:deep(.el-radio-button__inner) {
|
||||
border-radius: 6px !important;
|
||||
border: 1px solid var(--el-border-color) !important;
|
||||
margin-left: 0 !important;
|
||||
line-height: 1.3;
|
||||
}
|
||||
:deep(.el-radio-button.is-active .el-radio-button__inner) {
|
||||
border-color: var(--el-color-primary) !important;
|
||||
}
|
||||
:deep(.el-radio-button) {
|
||||
margin: 0;
|
||||
}
|
||||
:deep(.el-radio-button__inner) {
|
||||
border-radius: 6px !important;
|
||||
border: 1px solid var(--el-border-color) !important;
|
||||
margin-left: 0 !important;
|
||||
line-height: 1.3;
|
||||
}
|
||||
:deep(.el-radio-button.is-active .el-radio-button__inner) {
|
||||
border-color: var(--el-color-primary) !important;
|
||||
}
|
||||
}
|
||||
.role-group-card {
|
||||
width: 100%;
|
||||
flex: 0 0 auto;
|
||||
width: 100%;
|
||||
flex: 0 0 auto;
|
||||
|
||||
:deep(.el-card__header) {
|
||||
padding: 6px 12px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: var(--el-text-color-primary);
|
||||
}
|
||||
:deep(.el-card__body) {
|
||||
padding: 6px 12px 8px;
|
||||
}
|
||||
:deep(.el-card__header) {
|
||||
padding: 6px 12px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: var(--el-text-color-primary);
|
||||
}
|
||||
:deep(.el-card__body) {
|
||||
padding: 6px 12px 8px;
|
||||
}
|
||||
}
|
||||
.role-group {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 4px 8px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 4px 8px;
|
||||
}
|
||||
.group-name {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: var(--el-text-color-primary);
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: var(--el-text-color-primary);
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -47,7 +47,6 @@
|
||||
style="width: 100%"
|
||||
row-key="roleId"
|
||||
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
|
||||
|
||||
@selection-change="handleSelectionChange"
|
||||
border
|
||||
:cell-style="tableStyle.cellStyle"
|
||||
@@ -67,7 +66,7 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="roleSort" label="排序" width="80" align="center">
|
||||
<template #default="scope">{{ scope.row._isGroup ? '—' : (scope.row.roleSort ?? 0) }}</template>
|
||||
<template #default="scope">{{ scope.row._isGroup ? '—' : scope.row.roleSort ?? 0 }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="roleCode" :label="$t('sysrole.roleCode')" show-overflow-tooltip min-width="120">
|
||||
<template #default="scope">{{ scope.row._isGroup ? '—' : scope.row.roleCode }}</template>
|
||||
@@ -133,9 +132,18 @@
|
||||
title="批量指定关联用户"
|
||||
width="720px"
|
||||
destroy-on-close
|
||||
@close="assignUserKeyword = ''; assignUserType = ''; assignUserList = []; assignUserTree = []; assignSelectedIds = []">
|
||||
@close="
|
||||
assignUserKeyword = '';
|
||||
assignUserType = '';
|
||||
assignUserList = [];
|
||||
assignUserTree = [];
|
||||
assignSelectedIds = [];
|
||||
"
|
||||
>
|
||||
<template v-if="assignCurrentRole">
|
||||
<div class="mb12"><el-text type="info">当前角色:{{ assignCurrentRole.roleName }}({{ assignCurrentRole.roleCode }})</el-text></div>
|
||||
<div class="mb12">
|
||||
<el-text type="info">当前角色:{{ assignCurrentRole.roleName }}({{ assignCurrentRole.roleCode }})</el-text>
|
||||
</div>
|
||||
<el-form :inline="true" class="mb12">
|
||||
<el-form-item label="用户类型" required>
|
||||
<el-radio-group v-model="assignUserType">
|
||||
@@ -161,7 +169,8 @@
|
||||
v-loading="assignUserLoading"
|
||||
max-height="360"
|
||||
border
|
||||
@selection-change="handleAssignUserSelectionChange">
|
||||
@selection-change="handleAssignUserSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="50" align="left" :selectable="(row: any) => !row._isDept" />
|
||||
<el-table-column prop="label" label="部门 / 姓名" min-width="200">
|
||||
<template #default="{ row }">
|
||||
@@ -173,20 +182,24 @@
|
||||
<template #default="{ row }">{{ row._isDept ? '—' : row.username }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="deptName" label="部门" width="140">
|
||||
<template #default="{ row }">{{ row._isDept ? '—' : (row.deptName || '—') }}</template>
|
||||
<template #default="{ row }">{{ row._isDept ? '—' : row.deptName || '—' }}</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</template>
|
||||
<template #footer>
|
||||
<el-button @click="showAssignUserDialog = false">取消</el-button>
|
||||
<el-button type="primary" :loading="assignSubmitLoading" :disabled="assignSelectedIds.length === 0" @click="handleAssignUsersSubmit">确定(已选 {{ assignSelectedIds.length }} 人)</el-button>
|
||||
<el-button type="primary" :loading="assignSubmitLoading" :disabled="assignSelectedIds.length === 0" @click="handleAssignUsersSubmit"
|
||||
>确定(已选 {{ assignSelectedIds.length }} 人)</el-button
|
||||
>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 查看角色关联用户弹窗 -->
|
||||
<el-dialog v-model="showRoleUsersDialog" title="关联用户" width="560px" destroy-on-close>
|
||||
<template v-if="currentRoleForUsers">
|
||||
<div class="mb12"><el-text type="info">角色:{{ currentRoleForUsers.roleName }}({{ currentRoleForUsers.roleCode }})</el-text></div>
|
||||
<div class="mb12">
|
||||
<el-text type="info">角色:{{ currentRoleForUsers.roleName }}({{ currentRoleForUsers.roleCode }})</el-text>
|
||||
</div>
|
||||
<el-table :data="roleUsersList" v-loading="roleUsersLoading" max-height="400" border>
|
||||
<el-table-column prop="deptName" label="部门" min-width="140" show-overflow-tooltip />
|
||||
<el-table-column prop="realName" label="姓名" width="100" show-overflow-tooltip />
|
||||
@@ -298,9 +311,7 @@ const state: BasicTableProps = reactive<BasicTableProps>({
|
||||
const kw = String(params.roleName).trim().toLowerCase();
|
||||
if (kw) {
|
||||
data = data.filter(
|
||||
(r: any) =>
|
||||
(r.roleName && r.roleName.toLowerCase().includes(kw)) ||
|
||||
(r.roleCode && r.roleCode.toLowerCase().includes(kw))
|
||||
(r: any) => (r.roleName && r.roleName.toLowerCase().includes(kw)) || (r.roleCode && r.roleCode.toLowerCase().includes(kw))
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -353,10 +364,10 @@ const dictType = ref([
|
||||
label: '本级',
|
||||
value: '3',
|
||||
},
|
||||
{
|
||||
label: '本人',
|
||||
value: '4',
|
||||
},
|
||||
{
|
||||
label: '本人',
|
||||
value: '4',
|
||||
},
|
||||
]);
|
||||
|
||||
// table hook(无分页,不暴露 currentChangeHandle/sizeChangeHandle)
|
||||
@@ -370,7 +381,7 @@ const resetQuery = () => {
|
||||
|
||||
// 导出excel
|
||||
const exportExcel = () => {
|
||||
downBlobFile('/admin/role/export',Object.assign(state.queryForm,{ids:selectObjs}), 'role.xlsx');
|
||||
downBlobFile('/admin/role/export', Object.assign(state.queryForm, { ids: selectObjs }), 'role.xlsx');
|
||||
};
|
||||
|
||||
// 是否可以多选(分组行不可选,管理员角色不可选)
|
||||
@@ -470,9 +481,7 @@ async function loadAssignUserList() {
|
||||
}
|
||||
|
||||
function handleAssignUserSelectionChange(rows: any[]) {
|
||||
assignSelectedIds.value = (rows || [])
|
||||
.filter((r: any) => !r._isDept && r.userId)
|
||||
.map((r: any) => r.userId);
|
||||
assignSelectedIds.value = (rows || []).filter((r: any) => !r._isDept && r.userId).map((r: any) => r.userId);
|
||||
}
|
||||
|
||||
async function handleAssignUsersSubmit() {
|
||||
@@ -520,9 +529,21 @@ const handleBatchGroup = async () => {
|
||||
font-weight: 600;
|
||||
color: var(--el-text-color-primary);
|
||||
}
|
||||
.mb12 { margin-bottom: 12px; }
|
||||
.mb8 { margin-bottom: 8px; }
|
||||
.mt8 { margin-top: 8px; }
|
||||
.assign-user-tip { color: var(--el-text-color-secondary); font-size: 13px; }
|
||||
.dept-row { font-weight: 600; color: var(--el-text-color-primary); }
|
||||
.mb12 {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.mb8 {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.mt8 {
|
||||
margin-top: 8px;
|
||||
}
|
||||
.assign-user-tip {
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 13px;
|
||||
}
|
||||
.dept-row {
|
||||
font-weight: 600;
|
||||
color: var(--el-text-color-primary);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -15,12 +15,24 @@
|
||||
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="t('tenant.startTime')" prop="startTime">
|
||||
<el-date-picker class="!w-full" v-model="form.startTime" type="date" :placeholder="t('tenant.inputstartTimeTip')" :value-format="dateTimeStr" />
|
||||
<el-date-picker
|
||||
class="!w-full"
|
||||
v-model="form.startTime"
|
||||
type="date"
|
||||
:placeholder="t('tenant.inputstartTimeTip')"
|
||||
:value-format="dateTimeStr"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="t('tenant.endTime')" prop="endTime">
|
||||
<el-date-picker class="!w-full" v-model="form.endTime" type="date" :placeholder="t('tenant.inputendTimeTip')" :value-format="dateTimeStr" />
|
||||
<el-date-picker
|
||||
class="!w-full"
|
||||
v-model="form.endTime"
|
||||
type="date"
|
||||
:placeholder="t('tenant.inputendTimeTip')"
|
||||
:value-format="dateTimeStr"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20">
|
||||
@@ -71,11 +83,11 @@
|
||||
import { validateTenantCode, validateTenantName } from '/@/api/admin/tenant';
|
||||
import { useDict } from '/@/hooks/dict';
|
||||
import { useMessage } from '/@/hooks/message';
|
||||
import { getObj, addObj, putObj, treemenu,fetchList } from '/@/api/admin/tenant';
|
||||
import { getObj, addObj, putObj, treemenu, fetchList } from '/@/api/admin/tenant';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import other from '/@/utils/other';
|
||||
import { CheckboxValueType } from 'element-plus';
|
||||
import {rule} from "/@/utils/validate";
|
||||
import { rule } from '/@/utils/validate';
|
||||
|
||||
// 定义子组件向父组件传值/事件
|
||||
const emit = defineEmits(['refresh']);
|
||||
@@ -120,7 +132,7 @@ const checkedMenu = ref<any[]>([]);
|
||||
// 定义校验规则
|
||||
const dataRules = ref({
|
||||
name: [
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{ required: true, message: '名称不能为空', trigger: 'blur' },
|
||||
{
|
||||
validator: (rule: any, value: any, callback: any) => {
|
||||
@@ -130,7 +142,7 @@ const dataRules = ref({
|
||||
},
|
||||
],
|
||||
code: [
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{ required: true, message: '编码不能为空', trigger: 'blur' },
|
||||
{
|
||||
validator: (rule: any, value: any, callback: any) => {
|
||||
@@ -183,7 +195,7 @@ const onSubmit = async () => {
|
||||
}
|
||||
|
||||
if (menuTreeRef.value?.getCheckedKeys()) {
|
||||
let checkMenu = [...menuTreeRef.value.getCheckedKeys(), ...menuTreeRef.value.getHalfCheckedKeys()]
|
||||
let checkMenu = [...menuTreeRef.value.getCheckedKeys(), ...menuTreeRef.value.getHalfCheckedKeys()];
|
||||
|
||||
if (!checkMenu.includes('1300')) {
|
||||
useMessage().error('必须分配角色管理功能');
|
||||
@@ -207,7 +219,7 @@ const onSubmit = async () => {
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg);
|
||||
} finally {
|
||||
await fetchList()
|
||||
await fetchList();
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,53 +1,53 @@
|
||||
export default {
|
||||
tenant: {
|
||||
index: '#',
|
||||
importTenantTip: '导入租户',
|
||||
id: '租户id',
|
||||
name: '租户名称',
|
||||
code: '编码',
|
||||
tenantDomain: '域名',
|
||||
startTime: '开始时间',
|
||||
endTime: '结束时间',
|
||||
status: '状态',
|
||||
delFlag: 'delFlag',
|
||||
createBy: '创建人',
|
||||
updateBy: '修改人',
|
||||
createTime: '创建',
|
||||
updateTime: '更新时间',
|
||||
menuId: '租户套餐',
|
||||
individuationBtn: '个性化',
|
||||
inputidTip: '请输入租户id',
|
||||
inputnameTip: '请输入名称',
|
||||
inputcodeTip: '请输入编码',
|
||||
inputtenantDomainTip: '请输入域名',
|
||||
inputstartTimeTip: '请输入开始时间',
|
||||
inputendTimeTip: '请输入结束时间',
|
||||
inputstatusTip: '请输入status',
|
||||
inputdelFlagTip: '请输入delFlag',
|
||||
inputcreateByTip: '请输入创建人',
|
||||
inputupdateByTip: '请输入修改人',
|
||||
inputcreateTimeTip: '请输入创建',
|
||||
inputupdateTimeTip: '请输入更新时间',
|
||||
inputmenuIdTip: '请选择租户套餐',
|
||||
deleteDisabledTip: '基础租户不允许删除',
|
||||
},
|
||||
tenantmenu: {
|
||||
name: '套餐',
|
||||
index: '#',
|
||||
status: '状态',
|
||||
createTime: '创建',
|
||||
},
|
||||
tenant: {
|
||||
index: '#',
|
||||
importTenantTip: '导入租户',
|
||||
id: '租户id',
|
||||
name: '租户名称',
|
||||
code: '编码',
|
||||
tenantDomain: '域名',
|
||||
startTime: '开始时间',
|
||||
endTime: '结束时间',
|
||||
status: '状态',
|
||||
delFlag: 'delFlag',
|
||||
createBy: '创建人',
|
||||
updateBy: '修改人',
|
||||
createTime: '创建',
|
||||
updateTime: '更新时间',
|
||||
menuId: '租户套餐',
|
||||
individuationBtn: '个性化',
|
||||
inputidTip: '请输入租户id',
|
||||
inputnameTip: '请输入名称',
|
||||
inputcodeTip: '请输入编码',
|
||||
inputtenantDomainTip: '请输入域名',
|
||||
inputstartTimeTip: '请输入开始时间',
|
||||
inputendTimeTip: '请输入结束时间',
|
||||
inputstatusTip: '请输入status',
|
||||
inputdelFlagTip: '请输入delFlag',
|
||||
inputcreateByTip: '请输入创建人',
|
||||
inputupdateByTip: '请输入修改人',
|
||||
inputcreateTimeTip: '请输入创建',
|
||||
inputupdateTimeTip: '请输入更新时间',
|
||||
inputmenuIdTip: '请选择租户套餐',
|
||||
deleteDisabledTip: '基础租户不允许删除',
|
||||
},
|
||||
tenantmenu: {
|
||||
name: '套餐',
|
||||
index: '#',
|
||||
status: '状态',
|
||||
createTime: '创建',
|
||||
},
|
||||
|
||||
individuation: {
|
||||
websiteName: '网站名称',
|
||||
miniQr: '移动端二维码',
|
||||
logo: '网站图标',
|
||||
footerAuthor: '页脚信息',
|
||||
background: '登录页背景图',
|
||||
inputIndividuationNameTip: '请输入网站名称',
|
||||
inputMiniQrTip: '请输入网站图标',
|
||||
inputLogoTip: '请输入网站Logo',
|
||||
inputFooterAuthorTip: '请输入页脚信息',
|
||||
inputBackgroundTip: '请输入登录页背景图',
|
||||
}
|
||||
individuation: {
|
||||
websiteName: '网站名称',
|
||||
miniQr: '移动端二维码',
|
||||
logo: '网站图标',
|
||||
footerAuthor: '页脚信息',
|
||||
background: '登录页背景图',
|
||||
inputIndividuationNameTip: '请输入网站名称',
|
||||
inputMiniQrTip: '请输入网站图标',
|
||||
inputLogoTip: '请输入网站Logo',
|
||||
inputFooterAuthorTip: '请输入页脚信息',
|
||||
inputBackgroundTip: '请输入登录页背景图',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,60 +1,60 @@
|
||||
<template>
|
||||
<el-drawer :title="$t('tenant.individuationBtn')" v-model="visible" :close-on-click-modal="false">
|
||||
<el-form ref="dataFormRef" :model="form" :rules="dataRules" v-loading="loading">
|
||||
<el-row>
|
||||
<el-col :span="24" class="mt-4">
|
||||
<el-form-item :label="t('individuation.websiteName')" prop="websiteName" label-width="120px" align="left">
|
||||
<el-input v-model="form.websiteName" :placeholder="t('individuation.inputIndividuationNameTip')"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" class="mt-4">
|
||||
<el-form-item prop="footerAuthor" label-width="120px" align="left">
|
||||
<template #label>
|
||||
{{ t('individuation.footerAuthor') }}
|
||||
<tip content="浏览器底部版权信息、备案信息"/>
|
||||
</template>
|
||||
<el-input v-model="form.footer" :placeholder="t('individuation.inputFooterAuthorTip')"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" class="mt-4">
|
||||
<el-form-item prop="icon" label-width="120px" align="left">
|
||||
<template #label>
|
||||
{{ t('individuation.miniQr') }}
|
||||
<tip content="登录页右下角显示的移动端二维码"/>
|
||||
</template>
|
||||
<upload-img v-model:image-url="form.miniQr"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" class="mt-4">
|
||||
<el-form-item :label="t('individuation.background')" prop="background" label-width="120px" align="left">
|
||||
<upload-img v-model:image-url="form.background"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-drawer :title="$t('tenant.individuationBtn')" v-model="visible" :close-on-click-modal="false">
|
||||
<el-form ref="dataFormRef" :model="form" :rules="dataRules" v-loading="loading">
|
||||
<el-row>
|
||||
<el-col :span="24" class="mt-4">
|
||||
<el-form-item :label="t('individuation.websiteName')" prop="websiteName" label-width="120px" align="left">
|
||||
<el-input v-model="form.websiteName" :placeholder="t('individuation.inputIndividuationNameTip')" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" class="mt-4">
|
||||
<el-form-item prop="footerAuthor" label-width="120px" align="left">
|
||||
<template #label>
|
||||
{{ t('individuation.footerAuthor') }}
|
||||
<tip content="浏览器底部版权信息、备案信息" />
|
||||
</template>
|
||||
<el-input v-model="form.footer" :placeholder="t('individuation.inputFooterAuthorTip')" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" class="mt-4">
|
||||
<el-form-item prop="icon" label-width="120px" align="left">
|
||||
<template #label>
|
||||
{{ t('individuation.miniQr') }}
|
||||
<tip content="登录页右下角显示的移动端二维码" />
|
||||
</template>
|
||||
<upload-img v-model:image-url="form.miniQr" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" class="mt-4">
|
||||
<el-form-item :label="t('individuation.background')" prop="background" label-width="120px" align="left">
|
||||
<upload-img v-model:image-url="form.background" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="visible = false">{{ $t('common.cancelButtonText') }}</el-button>
|
||||
<el-button type="primary" @click="onSubmit" :disabled="loading">{{ $t('common.confirmButtonText') }}</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-drawer>
|
||||
</template>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="systemTenantDialog">
|
||||
import {useDict} from '/@/hooks/dict';
|
||||
import {useMessage} from '/@/hooks/message';
|
||||
import {getObj, putObj} from '/@/api/admin/tenant';
|
||||
import {useI18n} from 'vue-i18n';
|
||||
import UploadImg from "/@/components/Upload/Image.vue";
|
||||
import {useThemeConfig} from "/@/stores/themeConfig";
|
||||
import pinia from "/@/stores";
|
||||
import {storeToRefs} from "pinia";
|
||||
import Tip from "/@/components/Tip/index.vue";
|
||||
import { useDict } from '/@/hooks/dict';
|
||||
import { useMessage } from '/@/hooks/message';
|
||||
import { getObj, putObj } from '/@/api/admin/tenant';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import UploadImg from '/@/components/Upload/Image.vue';
|
||||
import { useThemeConfig } from '/@/stores/themeConfig';
|
||||
import pinia from '/@/stores';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import Tip from '/@/components/Tip/index.vue';
|
||||
|
||||
// 定义子组件向父组件传值/事件
|
||||
const emit = defineEmits(['refresh']);
|
||||
const {t} = useI18n();
|
||||
const { t } = useI18n();
|
||||
|
||||
// 定义变量内容
|
||||
const dataFormRef = ref();
|
||||
@@ -62,42 +62,38 @@ const visible = ref(false);
|
||||
const loading = ref(false);
|
||||
|
||||
// 字典
|
||||
const {status_type} = useDict('status_type');
|
||||
const { status_type } = useDict('status_type');
|
||||
|
||||
// 导入配置文件
|
||||
const stores = useThemeConfig(pinia);
|
||||
const {themeConfig} = storeToRefs(stores);
|
||||
const { themeConfig } = storeToRefs(stores);
|
||||
|
||||
// 提交表单数据
|
||||
const form = reactive({
|
||||
id: '',
|
||||
websiteName: themeConfig.value.globalTitle,
|
||||
background: '',
|
||||
miniQr: '',
|
||||
footer: themeConfig.value.footerAuthor,
|
||||
id: '',
|
||||
websiteName: themeConfig.value.globalTitle,
|
||||
background: '',
|
||||
miniQr: '',
|
||||
footer: themeConfig.value.footerAuthor,
|
||||
});
|
||||
|
||||
|
||||
// 定义校验规则
|
||||
const dataRules = ref({
|
||||
});
|
||||
const dataRules = ref({});
|
||||
|
||||
// 打开弹窗
|
||||
const openDialog = (id: string): void => {
|
||||
visible.value = true;
|
||||
form.id = ''
|
||||
visible.value = true;
|
||||
form.id = '';
|
||||
|
||||
// 重置表单数据
|
||||
nextTick(() => {
|
||||
dataFormRef.value?.resetFields();
|
||||
});
|
||||
|
||||
// 重置表单数据
|
||||
nextTick(() => {
|
||||
dataFormRef.value?.resetFields();
|
||||
});
|
||||
|
||||
if (id) {
|
||||
form.id = id;
|
||||
getTenantData(id);
|
||||
}
|
||||
|
||||
if (id) {
|
||||
form.id = id;
|
||||
getTenantData(id);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -105,33 +101,30 @@ const openDialog = (id: string): void => {
|
||||
* @param {string} id - 部门 ID。
|
||||
*/
|
||||
const getTenantData = async (id: any) => {
|
||||
const res = await getObj(id);
|
||||
Object.assign(form, res.data);
|
||||
const res = await getObj(id);
|
||||
Object.assign(form, res.data);
|
||||
};
|
||||
|
||||
|
||||
// 提交
|
||||
const onSubmit = async () => {
|
||||
const valid = await dataFormRef.value.validate().catch(() => {
|
||||
});
|
||||
if (!valid) return false;
|
||||
const valid = await dataFormRef.value.validate().catch(() => {});
|
||||
if (!valid) return false;
|
||||
|
||||
try {
|
||||
loading.value = true;
|
||||
await putObj(form);
|
||||
useMessage().success(t('common.editSuccessText'));
|
||||
visible.value = false;
|
||||
emit('refresh');
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
try {
|
||||
loading.value = true;
|
||||
await putObj(form);
|
||||
useMessage().success(t('common.editSuccessText'));
|
||||
visible.value = false;
|
||||
emit('refresh');
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// 暴露变量
|
||||
defineExpose({
|
||||
openDialog,
|
||||
openDialog,
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -3,41 +3,41 @@
|
||||
<el-dialog :close-on-click-modal="false" :title="dataForm.userId ? $t('common.editBtn') : $t('common.addBtn')" draggable v-model="visible">
|
||||
<el-form :model="dataForm" :rules="dataRules" label-width="90px" ref="dataFormRef" v-loading="loading">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="$t('sysuser.username')" prop="username">
|
||||
<el-input
|
||||
:disabled="dataForm.userId !== ''"
|
||||
:placeholder="$t('sysuser.inputUsernameTip')"
|
||||
v-model="dataForm.username"
|
||||
autocomplete="off"
|
||||
:readonly="formState.usernameReadonly"
|
||||
@focus="formState.usernameReadonly = false"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="$t('sysuser.password')" prop="password">
|
||||
<el-input
|
||||
clearable
|
||||
:placeholder="$t('sysuser.inputPasswordTip')"
|
||||
type="password"
|
||||
v-model="dataForm.password"
|
||||
autocomplete="new-password"
|
||||
:readonly="formState.passwordReadonly"
|
||||
@focus="formState.passwordReadonly = false"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="$t('sysuser.username')" prop="username">
|
||||
<el-input
|
||||
:disabled="dataForm.userId !== ''"
|
||||
:placeholder="$t('sysuser.inputUsernameTip')"
|
||||
v-model="dataForm.username"
|
||||
autocomplete="off"
|
||||
:readonly="formState.usernameReadonly"
|
||||
@focus="formState.usernameReadonly = false"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="$t('sysuser.password')" prop="password">
|
||||
<el-input
|
||||
clearable
|
||||
:placeholder="$t('sysuser.inputPasswordTip')"
|
||||
type="password"
|
||||
v-model="dataForm.password"
|
||||
autocomplete="new-password"
|
||||
:readonly="formState.passwordReadonly"
|
||||
@focus="formState.passwordReadonly = false"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="$t('sysuser.name')" prop="realName">
|
||||
<el-input clearable :placeholder="$t('sysuser.inputNameTip')" v-model="dataForm.realName"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<!-- <el-col :span="12" class="mb20">-->
|
||||
<!-- <el-form-item :label="$t('sysuser.phone')" prop="phone">-->
|
||||
<!-- <el-input clearable :placeholder="$t('sysuser.inputPhoneTip')" v-model="dataForm.phone"></el-input>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- </el-col>-->
|
||||
<!-- <el-col :span="12" class="mb20">-->
|
||||
<!-- <el-form-item :label="$t('sysuser.phone')" prop="phone">-->
|
||||
<!-- <el-input clearable :placeholder="$t('sysuser.inputPhoneTip')" v-model="dataForm.phone"></el-input>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- </el-col>-->
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="$t('sysuser.role')" prop="role">
|
||||
<el-select clearable multiple :placeholder="$t('sysuser.selectRole')" v-model="dataForm.role" filterable>
|
||||
@@ -45,13 +45,13 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<!-- <el-col :span="12" class="mb20">-->
|
||||
<!-- <el-form-item :label="$t('sysuser.post')" prop="post">-->
|
||||
<!-- <el-select clearable multiple :placeholder="$t('sysuser.selectPost')" v-model="dataForm.post">-->
|
||||
<!-- <el-option :key="item.postId" :label="item.postName" :value="item.postId" v-for="item in postData" />-->
|
||||
<!-- </el-select>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- </el-col>-->
|
||||
<!-- <el-col :span="12" class="mb20">-->
|
||||
<!-- <el-form-item :label="$t('sysuser.post')" prop="post">-->
|
||||
<!-- <el-select clearable multiple :placeholder="$t('sysuser.selectPost')" v-model="dataForm.post">-->
|
||||
<!-- <el-option :key="item.postId" :label="item.postName" :value="item.postId" v-for="item in postData" />-->
|
||||
<!-- </el-select>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- </el-col>-->
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="$t('sysuser.dept')" prop="deptId">
|
||||
<el-tree-select
|
||||
@@ -67,11 +67,11 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<!-- <el-col :span="12" class="mb20">-->
|
||||
<!-- <el-form-item :label="$t('sysuser.email')" prop="email">-->
|
||||
<!-- <el-input clearable :placeholder="$t('sysuser.inputEmailTip')" v-model="dataForm.email"></el-input>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- </el-col>-->
|
||||
<!-- <el-col :span="12" class="mb20">-->
|
||||
<!-- <el-form-item :label="$t('sysuser.email')" prop="email">-->
|
||||
<!-- <el-input clearable :placeholder="$t('sysuser.inputEmailTip')" v-model="dataForm.email"></el-input>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- </el-col>-->
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="$t('sysuser.lockFlag')" prop="lockFlag">
|
||||
<el-radio-group v-model="dataForm.lockFlag">
|
||||
@@ -135,7 +135,7 @@ const dataForm = reactive({
|
||||
deptId: '',
|
||||
roleList: [],
|
||||
postList: [],
|
||||
realName: '',
|
||||
realName: '',
|
||||
email: '',
|
||||
post: [] as string[],
|
||||
role: [] as string[],
|
||||
@@ -227,7 +227,7 @@ const onSubmit = async () => {
|
||||
// 立即设置 loading,防止重复点击
|
||||
if (loading.value) return;
|
||||
loading.value = true;
|
||||
|
||||
|
||||
try {
|
||||
const valid = await dataFormRef.value.validate().catch(() => {});
|
||||
if (!valid) {
|
||||
|
||||
@@ -79,12 +79,12 @@
|
||||
<el-table-column :label="$t('sysuser.index')" type="index" width="60" fixed="left" />
|
||||
<el-table-column :label="$t('sysuser.username')" prop="username" fixed="left" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column :label="$t('sysuser.name')" prop="realName" show-overflow-tooltip></el-table-column>
|
||||
<!-- <el-table-column :label="$t('sysuser.phone')" prop="phone" show-overflow-tooltip></el-table-column>-->
|
||||
<!-- <el-table-column :label="$t('sysuser.post')" show-overflow-tooltip>-->
|
||||
<!-- <template #default="scope">-->
|
||||
<!-- <el-tag v-for="(item, index) in scope.row.postList" :key="index">{{ item.postName }}</el-tag>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<!-- <el-table-column :label="$t('sysuser.phone')" prop="phone" show-overflow-tooltip></el-table-column>-->
|
||||
<!-- <el-table-column :label="$t('sysuser.post')" show-overflow-tooltip>-->
|
||||
<!-- <template #default="scope">-->
|
||||
<!-- <el-tag v-for="(item, index) in scope.row.postList" :key="index">{{ item.postName }}</el-tag>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<el-table-column :label="$t('sysuser.role')" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
<el-tag v-for="(item, index) in scope.row.roleList" :key="index">{{ item.roleName }}</el-tag>
|
||||
|
||||
@@ -2,12 +2,16 @@
|
||||
<el-drawer v-model="visible" :title="$t('personal.name')" size="40%">
|
||||
<el-tabs style="height: 200px" class="demo-tabs">
|
||||
<el-tab-pane label="基本信息" v-loading="loading">
|
||||
<template #label>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M17.982 18.725A7.488 7.488 0 0 0 12 15.75a7.488 7.488 0 0 0-5.982 2.975m11.963 0a9 9 0 1 0-11.963 0m11.963 0A8.966 8.966 0 0 1 12 21a8.966 8.966 0 0 1-5.982-2.275M15 9.75a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" />
|
||||
</svg>
|
||||
基本信息
|
||||
</template>
|
||||
<template #label>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M17.982 18.725A7.488 7.488 0 0 0 12 15.75a7.488 7.488 0 0 0-5.982 2.975m11.963 0a9 9 0 1 0-11.963 0m11.963 0A8.966 8.966 0 0 1 12 21a8.966 8.966 0 0 1-5.982-2.275M15 9.75a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"
|
||||
/>
|
||||
</svg>
|
||||
基本信息
|
||||
</template>
|
||||
<el-form :model="formData" :rules="ruleForm" label-width="100px" class="mt30" ref="formdataRef">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24" class="mb20">
|
||||
@@ -55,26 +59,36 @@
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="安全信息">
|
||||
<template #label>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75 11.25 15 15 9.75m-3-7.036A11.959 11.959 0 0 1 3.598 6 11.99 11.99 0 0 0 3 9.749c0 5.592 3.824 10.29 9 11.623 5.176-1.332 9-6.03 9-11.622 0-1.31-.21-2.571-.598-3.751h-.152c-3.196 0-6.1-1.248-8.25-3.285Z" />
|
||||
</svg>
|
||||
安全信息
|
||||
</template>
|
||||
<template #label>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M9 12.75 11.25 15 15 9.75m-3-7.036A11.959 11.959 0 0 1 3.598 6 11.99 11.99 0 0 0 3 9.749c0 5.592 3.824 10.29 9 11.623 5.176-1.332 9-6.03 9-11.622 0-1.31-.21-2.571-.598-3.751h-.152c-3.196 0-6.1-1.248-8.25-3.285Z"
|
||||
/>
|
||||
</svg>
|
||||
安全信息
|
||||
</template>
|
||||
<el-form :model="passwordFormData" :rules="passwordRuleForm" label-width="100px" class="mt30" ref="passwordFormdataRef">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24" class="mb20">
|
||||
<el-form-item label="原密码" prop="password">
|
||||
<el-input v-model="passwordFormData.password" :type="showPassword ? 'text' : 'password'" placeholder="请输入密码" clearable type="password">
|
||||
<template #suffix>
|
||||
<i
|
||||
class="iconfont el-input__icon login-content-password"
|
||||
:class="showPassword ? 'icon-yincangmima' : 'icon-xianshimima'"
|
||||
@click="showPassword = !showPassword"
|
||||
>
|
||||
</i>
|
||||
</template>
|
||||
</el-input>
|
||||
<el-input
|
||||
v-model="passwordFormData.password"
|
||||
:type="showPassword ? 'text' : 'password'"
|
||||
placeholder="请输入密码"
|
||||
clearable
|
||||
type="password"
|
||||
>
|
||||
<template #suffix>
|
||||
<i
|
||||
class="iconfont el-input__icon login-content-password"
|
||||
:class="showPassword ? 'icon-yincangmima' : 'icon-xianshimima'"
|
||||
@click="showPassword = !showPassword"
|
||||
>
|
||||
</i>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" class="mb20">
|
||||
@@ -103,13 +117,17 @@
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="第三方账号">
|
||||
<template #label>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M7.864 4.243A7.5 7.5 0 0 1 19.5 10.5c0 2.92-.556 5.709-1.568 8.268M5.742 6.364A7.465 7.465 0 0 0 4.5 10.5a7.464 7.464 0 0 1-1.15 3.993m1.989 3.559A11.209 11.209 0 0 0 8.25 10.5a3.75 3.75 0 1 1 7.5 0c0 .527-.021 1.049-.064 1.565M12 10.5a14.94 14.94 0 0 1-3.6 9.75m6.633-4.596a18.666 18.666 0 0 1-2.485 5.33" />
|
||||
</svg>
|
||||
<template #label>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M7.864 4.243A7.5 7.5 0 0 1 19.5 10.5c0 2.92-.556 5.709-1.568 8.268M5.742 6.364A7.465 7.465 0 0 0 4.5 10.5a7.464 7.464 0 0 1-1.15 3.993m1.989 3.559A11.209 11.209 0 0 0 8.25 10.5a3.75 3.75 0 1 1 7.5 0c0 .527-.021 1.049-.064 1.565M12 10.5a14.94 14.94 0 0 1-3.6 9.75m6.633-4.596a18.666 18.666 0 0 1-2.485 5.33"
|
||||
/>
|
||||
</svg>
|
||||
|
||||
社交登录
|
||||
</template>
|
||||
社交登录
|
||||
</template>
|
||||
<el-table :data="socialList" class="mt10">
|
||||
<el-table-column type="index" label="序号" width="80"></el-table-column>
|
||||
<el-table-column prop="name" label="平台"></el-table-column>
|
||||
@@ -132,14 +150,14 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="personal">
|
||||
import {useUserInfo} from '/@/stores/userInfo';
|
||||
import {editInfo, getObj, password, unbindingUser} from '/@/api/admin/user';
|
||||
import {useMessage} from '/@/hooks/message';
|
||||
import {rule, validateNull} from '/@/utils/validate';
|
||||
import { useUserInfo } from '/@/stores/userInfo';
|
||||
import { editInfo, getObj, password, unbindingUser } from '/@/api/admin/user';
|
||||
import { useMessage } from '/@/hooks/message';
|
||||
import { rule, validateNull } from '/@/utils/validate';
|
||||
import other from '/@/utils/other';
|
||||
import {Session} from '/@/utils/storage';
|
||||
import {useI18n} from 'vue-i18n';
|
||||
import {getLoginAppList} from "/@/api/admin/social";
|
||||
import { Session } from '/@/utils/storage';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { getLoginAppList } from '/@/api/admin/social';
|
||||
import { SocialLoginEnum } from '/@/api/login';
|
||||
|
||||
const { t } = useI18n();
|
||||
@@ -177,9 +195,18 @@ const ruleForm = reactive({
|
||||
{ required: true, message: '手机号不能为空', trigger: 'blur' },
|
||||
{ validator: rule.validatePhone, trigger: 'blur' },
|
||||
],
|
||||
nickname: [{ validator: rule.overLength, trigger: 'blur' },{ required: true, message: '昵称不能为空', trigger: 'blur' }],
|
||||
email: [{ validator: rule.overLength, trigger: 'blur' },{ required: true, message: '邮箱不能为空', trigger: 'blur' }],
|
||||
name: [{ validator: rule.overLength, trigger: 'blur' },{ required: true, message: '姓名不能为空', trigger: 'blur' }],
|
||||
nickname: [
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{ required: true, message: '昵称不能为空', trigger: 'blur' },
|
||||
],
|
||||
email: [
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{ required: true, message: '邮箱不能为空', trigger: 'blur' },
|
||||
],
|
||||
name: [
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{ required: true, message: '姓名不能为空', trigger: 'blur' },
|
||||
],
|
||||
});
|
||||
const validatorPassword2 = (rule: any, value: any, callback: any) => {
|
||||
if (value !== passwordFormData.newpassword1) {
|
||||
|
||||
Reference in New Issue
Block a user