更新短信功能
This commit is contained in:
parent
b870115444
commit
e927026495
@ -12,13 +12,15 @@ export function getUserCate() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有文件
|
||||
* 获取所有文件(支持分页、分类、关键词,与后端 query 一致)
|
||||
* @param {Object} [params] page, pageSize, cate, keyword
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export function getAllFiles() {
|
||||
export function getAllFiles(params = {}) {
|
||||
return request({
|
||||
url: "/platform/allfiles",
|
||||
method: "get",
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
@ -85,13 +87,13 @@ export function getCateFiles(id, page = 1, pageSize = 24, keyword = "") {
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取文件
|
||||
* @param {number|string} id 文件ID
|
||||
* 根据文件 ID 获取单条文件信息(非分类列表)
|
||||
* @param {number|string} id 文件主键 ID
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export function getFileById(id) {
|
||||
return request({
|
||||
url: `/platform/catefiles`,
|
||||
url: `/platform/file/${id}`,
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
@ -100,12 +102,17 @@ export function getFileById(id) {
|
||||
* 上传文件
|
||||
* @param {FormData} formData 文件数据
|
||||
* @param {Object} options 额外选项
|
||||
* @param {string} [options.cate]
|
||||
* @param {string|number} [options.cate] 文件分组,0 为未分类
|
||||
* @param {string|number} [options.tuid] 租户用户 yz_tenant_user.id;租户侧上传时传,平台管理员不传
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export function uploadFile(formData, options = {}) {
|
||||
if (options.cate) {
|
||||
formData.append('cate', options.cate);
|
||||
// 0 表示「未分类」,不能用 truthy 判断
|
||||
if (options.cate !== undefined && options.cate !== null && options.cate !== "") {
|
||||
formData.append("cate", String(options.cate));
|
||||
}
|
||||
if (options.tuid !== undefined && options.tuid !== null && options.tuid !== "") {
|
||||
formData.append("tuid", String(options.tuid));
|
||||
}
|
||||
|
||||
return request({
|
||||
@ -151,7 +158,7 @@ export function deleteFile(id) {
|
||||
*/
|
||||
export function deleteFilePermanently(id) {
|
||||
return request({
|
||||
url: `/platform/deleteFilePermanently/${id}`,
|
||||
url: `/platform/deletefilepermanently/${id}`,
|
||||
method: "delete",
|
||||
});
|
||||
}
|
||||
@ -177,7 +184,7 @@ export function moveFile(id, cate) {
|
||||
*/
|
||||
export function batchDeleteFiles(ids) {
|
||||
return request({
|
||||
url: "/platform/batchDeleteFiles",
|
||||
url: "/platform/batchdeletefiles",
|
||||
method: "post",
|
||||
data: { ids },
|
||||
});
|
||||
|
||||
@ -9,6 +9,14 @@ export function login(data) {
|
||||
});
|
||||
}
|
||||
|
||||
/** 当前登录用户信息(含角色名称),需携带 token */
|
||||
export function getCurrentUser() {
|
||||
return request({
|
||||
url: `/platform/currentUser`,
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
// 发送登录验证码(手机号)
|
||||
export function sendLoginCode(data) {
|
||||
return request({
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import request from "@/utils/request";
|
||||
|
||||
// 获取租户用户列表
|
||||
/** 获取租户用户列表;params 可含 tid、uid、keyword(模糊匹配姓名/手机/邮箱/账号) */
|
||||
export function getTenantUserList(params) {
|
||||
return request({
|
||||
url: "/platform/tenantUser/list",
|
||||
@ -9,7 +9,7 @@ export function getTenantUserList(params) {
|
||||
});
|
||||
}
|
||||
|
||||
// 创建租户用户绑定关系
|
||||
/** 创建租户用户绑定(后端写入 yz_tenant_user) */
|
||||
export function createTenantUser(data) {
|
||||
return request({
|
||||
url: "/platform/tenantUser/create",
|
||||
|
||||
@ -43,6 +43,7 @@
|
||||
<span class="el-dropdown-link" style="cursor: pointer;">
|
||||
<img :src="getImageUrl('user')" class="user" />
|
||||
<span class="user-name">{{ displayName }}</span>
|
||||
<el-tag v-if="roleLabel" size="small" effect="plain" class="user-role-tag">{{ roleLabel }}</el-tag>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
@ -73,7 +74,7 @@ import { ref, computed, onMounted, onUnmounted } from "vue";
|
||||
import { useRouter, useRoute } from "vue-router";
|
||||
import { useAllDataStore, useMenuStore, useTabsStore } from "@/stores";
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
import { logout } from "@/api/login";
|
||||
import { logout, getCurrentUser } from "@/api/login";
|
||||
import { User, SwitchButton, Sunny, Moon, Refresh, Bell, HomeFilled } from '@element-plus/icons-vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
|
||||
@ -130,7 +131,18 @@ async function refreshCache() {
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(loadMenu);
|
||||
onMounted(async () => {
|
||||
await loadMenu();
|
||||
if (!authStore.token) return;
|
||||
try {
|
||||
const res = await getCurrentUser();
|
||||
if (res && res.code === 200 && res.data) {
|
||||
authStore.updateUserInfo({ ...authStore.user, ...res.data });
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("getCurrentUser failed", e);
|
||||
}
|
||||
});
|
||||
|
||||
// 根据菜单列表和当前路径计算出的面包屑导航
|
||||
const breadcrumbs = computed(() => {
|
||||
@ -179,6 +191,12 @@ const displayName = computed(() => {
|
||||
return user.account || '';
|
||||
});
|
||||
|
||||
/** 角色展示名(来自 yz_admin_role.name) */
|
||||
const roleLabel = computed(() => {
|
||||
const n = authStore.user?.role_name;
|
||||
return typeof n === "string" && n.trim() ? n.trim() : "";
|
||||
});
|
||||
|
||||
const handleCollapse = () => {
|
||||
store.state.isCollapse = !store.state.isCollapse;
|
||||
};
|
||||
@ -424,6 +442,18 @@ onUnmounted(() => {
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
|
||||
.user-role-tag {
|
||||
flex-shrink: 0;
|
||||
margin-left: 4px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
html:not(.dark) & .user-role-tag {
|
||||
color: #ffffff;
|
||||
border-color: rgba(255, 255, 255, 0.85);
|
||||
background: rgba(255, 255, 255, 0.12);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -15,6 +15,12 @@ const staticMainChildren = [
|
||||
component: () => import("@/views/user/userProfile.vue"),
|
||||
meta: { requiresAuth: true, title: "用户中心" }
|
||||
},
|
||||
{
|
||||
path: "/system/email",
|
||||
name: "SystemEmail",
|
||||
component: () => import("@/views/system/email/index.vue"),
|
||||
meta: { requiresAuth: true, title: "邮箱管理" }
|
||||
},
|
||||
// 兼容拼写错误的路径重定向
|
||||
{
|
||||
path: "/apps/erp/dashborad",
|
||||
|
||||
@ -1,13 +1,39 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref, reactive } from 'vue'
|
||||
|
||||
// 用户信息类型
|
||||
// 平台登录缓存仅保留以下字段(不含 tid、group_id)
|
||||
const defaultUser = {
|
||||
id: '',
|
||||
account: '',
|
||||
name: '',
|
||||
rid: '',
|
||||
avatar: ''
|
||||
avatar: '',
|
||||
role_name: ''
|
||||
}
|
||||
|
||||
/**
|
||||
* 规范化写入 localStorage 的用户信息,去掉 tid、group_id 等废弃字段;
|
||||
* 若仅有历史 group_id,则迁移到 rid。
|
||||
*/
|
||||
function normalizePlatformUser(raw) {
|
||||
if (!raw || typeof raw !== 'object') {
|
||||
return { ...defaultUser }
|
||||
}
|
||||
let rid = raw.rid
|
||||
if (rid === undefined || rid === null || rid === '') {
|
||||
const legacy = raw.group_id
|
||||
if (legacy !== undefined && legacy !== null && legacy !== '') {
|
||||
rid = legacy
|
||||
}
|
||||
}
|
||||
return {
|
||||
id: parseInt(raw.id, 10) || null,
|
||||
account: raw.account || '',
|
||||
name: raw.name || '',
|
||||
rid: rid !== undefined && rid !== null && rid !== '' ? rid : '',
|
||||
avatar: raw.avatar || '',
|
||||
role_name: raw.role_name || ''
|
||||
}
|
||||
}
|
||||
|
||||
export const useAuthStore = defineStore('auth', () => {
|
||||
@ -20,8 +46,10 @@ export const useAuthStore = defineStore('auth', () => {
|
||||
const cachedUser = localStorage.getItem('userInfo')
|
||||
if (cachedUser) {
|
||||
try {
|
||||
const userInfo = JSON.parse(cachedUser)
|
||||
Object.assign(user, userInfo)
|
||||
const parsed = JSON.parse(cachedUser)
|
||||
const normalized = normalizePlatformUser(parsed)
|
||||
Object.assign(user, normalized)
|
||||
localStorage.setItem('userInfo', JSON.stringify(normalized))
|
||||
} catch (e) {
|
||||
console.error('Failed to parse user info from cache:', e)
|
||||
}
|
||||
@ -34,16 +62,8 @@ export const useAuthStore = defineStore('auth', () => {
|
||||
// 保存登录信息(token 和用户信息)
|
||||
function setLoginInfo(loginData) {
|
||||
const userInfo = loginData.user || loginData
|
||||
const normalizedUser = normalizePlatformUser(userInfo)
|
||||
|
||||
const normalizedUser = {
|
||||
id: parseInt(userInfo.id) || null,
|
||||
account: userInfo.account || '',
|
||||
name: userInfo.name || '',
|
||||
rid: userInfo.rid || userInfo.group_id || '',
|
||||
avatar: userInfo.avatar || ''
|
||||
}
|
||||
|
||||
// 使用后端返回的真实 JWT token
|
||||
const accessToken = loginData.token || ''
|
||||
|
||||
token.value = accessToken
|
||||
@ -65,7 +85,7 @@ export const useAuthStore = defineStore('auth', () => {
|
||||
function clearToken() {
|
||||
token.value = ''
|
||||
isLoggedIn.value = false
|
||||
Object.assign(user, defaultUser)
|
||||
Object.assign(user, { ...defaultUser })
|
||||
localStorage.removeItem('token')
|
||||
localStorage.removeItem('userInfo')
|
||||
}
|
||||
@ -80,14 +100,15 @@ export const useAuthStore = defineStore('auth', () => {
|
||||
} else {
|
||||
token.value = ''
|
||||
isLoggedIn.value = false
|
||||
Object.assign(user, defaultUser)
|
||||
Object.assign(user, { ...defaultUser })
|
||||
}
|
||||
}
|
||||
|
||||
// 更新用户信息
|
||||
function updateUserInfo(userInfo) {
|
||||
Object.assign(user, userInfo)
|
||||
localStorage.setItem('userInfo', JSON.stringify(userInfo))
|
||||
// 更新用户信息(合并后仍只持久化平台字段)
|
||||
function updateUserInfo(partial) {
|
||||
const merged = normalizePlatformUser({ ...user, ...partial })
|
||||
Object.assign(user, merged)
|
||||
localStorage.setItem('userInfo', JSON.stringify(merged))
|
||||
}
|
||||
|
||||
return {
|
||||
@ -101,4 +122,3 @@ export const useAuthStore = defineStore('auth', () => {
|
||||
updateUserInfo
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@ -21,7 +21,7 @@ export const useMenuStore = defineStore('menu', () => {
|
||||
try {
|
||||
const userInfo = JSON.parse(localStorage.getItem('userInfo') || '{}');
|
||||
const loginType = userInfo.type || 'user';
|
||||
const roleId = userInfo.group_id || 0;
|
||||
const roleId = userInfo.rid || 0;
|
||||
return `menu_cache_${loginType}_${roleId}`;
|
||||
} catch (e) {
|
||||
return 'menu_cache_default';
|
||||
@ -109,7 +109,7 @@ export const useMenuStore = defineStore('menu', () => {
|
||||
try {
|
||||
const userInfo = getUserInfo();
|
||||
const loginType = userInfo.type || 'user';
|
||||
const roleId = userInfo.group_id || 0;
|
||||
const roleId = userInfo.rid || 0;
|
||||
|
||||
let res;
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import axios from 'axios';
|
||||
|
||||
// 获取API基础URL,添加调试信息
|
||||
const apiBaseURL = import.meta.env.VITE_API_BASE_URL;
|
||||
// 获取API基础URL;开发环境可在 .env.development 留空,配合 Vite 代理访问 /platform
|
||||
const apiBaseURL = import.meta.env.VITE_API_BASE_URL ?? "";
|
||||
|
||||
// 创建axios实例
|
||||
const service = axios.create({
|
||||
|
||||
@ -37,8 +37,8 @@
|
||||
{{ user.email || "未设置" }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="角色">
|
||||
<el-tag :type="getRoleTagType(user.group_id)" size="small">
|
||||
{{ getRoleName(user.group_id) }}
|
||||
<el-tag :type="getRoleTagType(user.rid)" size="small">
|
||||
{{ getRoleName(user.rid) }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="最后登录IP">
|
||||
@ -75,7 +75,7 @@ interface User {
|
||||
qq: string;
|
||||
email: string;
|
||||
sex: number;
|
||||
group_id: number;
|
||||
rid: number;
|
||||
status: number;
|
||||
last_login_ip: string;
|
||||
login_count: number;
|
||||
@ -128,19 +128,19 @@ const fetchRoles = async () => {
|
||||
};
|
||||
|
||||
// 获取角色tag状态
|
||||
function getRoleTagType(group_id: number): string {
|
||||
function getRoleTagType(rid: number): string {
|
||||
const typeMap: Record<number, string> = {
|
||||
1: "primary",
|
||||
2: "success",
|
||||
3: "warning",
|
||||
4: "danger",
|
||||
};
|
||||
return typeMap[group_id] || "primary";
|
||||
return typeMap[rid] || "primary";
|
||||
}
|
||||
|
||||
// 获取角色名称
|
||||
function getRoleName(group_id: number): string {
|
||||
const role = roles.value.find((r) => r.id === group_id);
|
||||
function getRoleName(rid: number): string {
|
||||
const role = roles.value.find((r) => r.id === rid);
|
||||
return role?.name || "未知";
|
||||
}
|
||||
|
||||
|
||||
@ -141,7 +141,7 @@ interface User {
|
||||
phone: string;
|
||||
qq: string;
|
||||
sex: number;
|
||||
group_id: number;
|
||||
rid: number;
|
||||
status: number;
|
||||
last_login_ip: string;
|
||||
email: number;
|
||||
|
||||
@ -26,7 +26,7 @@
|
||||
/>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="登录验证" v-if="groupId === 1" name="loginVerification">
|
||||
<el-tab-pane label="登录验证" v-if="roleId === 1" name="loginVerification">
|
||||
<loginVerificationSettings
|
||||
ref="loginVerificationSettingsRef"
|
||||
v-if="activeTab === 'loginVerification'"
|
||||
@ -49,7 +49,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from "vue";
|
||||
import { ref, computed } from "vue";
|
||||
import normalSettings from "./components/normalSettings.vue";
|
||||
import seoSettings from "./components/seoSettings.vue";
|
||||
import contactSettings from "./components/contactSettings.vue";
|
||||
@ -59,7 +59,7 @@ import loginVerificationSettings from "./components/loginVerification.vue";
|
||||
import { useAuthStore } from '@/stores/auth';
|
||||
|
||||
const authStore = useAuthStore();
|
||||
const groupId = (authStore.userInfo as any)?.group_id;
|
||||
const roleId = computed(() => Number(authStore.user?.rid) || 0);
|
||||
const activeTab = ref("basic");
|
||||
</script>
|
||||
|
||||
|
||||
244
src/views/basicSettings/tenants/components/TenantUsersTab.vue
Normal file
244
src/views/basicSettings/tenants/components/TenantUsersTab.vue
Normal file
@ -0,0 +1,244 @@
|
||||
<template>
|
||||
<div class="tenant-users-tab">
|
||||
<div class="section-header">
|
||||
<div class="section-title">用户列表</div>
|
||||
<el-button type="primary" size="small" :disabled="!tid" @click="handleAddUser">
|
||||
<el-icon><Plus /></el-icon>
|
||||
添加用户
|
||||
</el-button>
|
||||
</div>
|
||||
<el-form :inline="true" class="user-search-form" @submit.prevent>
|
||||
<el-form-item label="关键词">
|
||||
<el-input
|
||||
v-model="userSearchKeyword"
|
||||
clearable
|
||||
placeholder="姓名 / 手机 / 邮箱 / 账号"
|
||||
style="width: 260px"
|
||||
:disabled="!tid"
|
||||
@keyup.enter="handleSearchUsers"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" :disabled="!tid" @click="handleSearchUsers">查询</el-button>
|
||||
<el-button :disabled="!tid" @click="resetUserSearch">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-table :data="tenantUsers" v-loading="usersLoading" border style="width: 100%">
|
||||
<el-table-column prop="account" label="用户名" min-width="140" align="center" />
|
||||
<el-table-column prop="name" label="姓名" min-width="120" align="center" />
|
||||
<el-table-column prop="phone" label="手机号" min-width="140" align="center" />
|
||||
<el-table-column prop="email" label="邮箱" min-width="180" align="center" />
|
||||
<el-table-column prop="status" label="状态" width="90" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="Number(row.status) === 1 ? 'success' : 'danger'">
|
||||
{{ Number(row.status) === 1 ? "启用" : "禁用" }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="140" align="center" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button text type="primary" @click="openPasswordDialog(row)">
|
||||
修改密码
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<AddUser ref="addUserRef" @success="refreshTenantUsers" />
|
||||
|
||||
<el-dialog v-model="passwordDialogVisible" title="修改密码" width="420px" destroy-on-close>
|
||||
<el-form ref="passwordFormRef" :model="passwordForm" :rules="passwordRules" label-width="90px">
|
||||
<el-form-item label="新密码" prop="password">
|
||||
<el-input v-model="passwordForm.password" type="password" show-password placeholder="请输入新密码" />
|
||||
</el-form-item>
|
||||
<el-form-item label="确认密码" prop="password2">
|
||||
<el-input v-model="passwordForm.password2" type="password" show-password placeholder="请再次输入新密码" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="passwordDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="passwordSubmitting" @click="submitPassword">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, watch } from "vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { Plus } from "@element-plus/icons-vue";
|
||||
import { getTenantUserList, editTenantUser } from "@/api/tenantUser";
|
||||
import AddUser from "./adduser.vue";
|
||||
|
||||
const props = defineProps<{
|
||||
/** 当前租户 ID,为空时不请求 */
|
||||
tid: number | null;
|
||||
}>();
|
||||
|
||||
const tenantUsers = ref<any[]>([]);
|
||||
const usersLoading = ref(false);
|
||||
const userSearchKeyword = ref("");
|
||||
const addUserRef = ref<{ open: (tenantId: number) => void } | null>(null);
|
||||
|
||||
const passwordDialogVisible = ref(false);
|
||||
const passwordSubmitting = ref(false);
|
||||
const currentTenantUserId = ref<number | null>(null);
|
||||
const passwordFormRef = ref();
|
||||
const passwordForm = reactive({
|
||||
password: "",
|
||||
password2: "",
|
||||
});
|
||||
|
||||
const passwordRules = {
|
||||
password: [
|
||||
{ required: true, message: "请输入新密码", trigger: "blur" },
|
||||
{ min: 6, message: "密码至少 6 位", trigger: "blur" },
|
||||
],
|
||||
password2: [
|
||||
{ required: true, message: "请再次输入新密码", trigger: "blur" },
|
||||
{
|
||||
validator: (_rule: any, value: string, callback: any) => {
|
||||
if (!value) return callback(new Error("请再次输入新密码"));
|
||||
if (value !== passwordForm.password) return callback(new Error("两次密码不一致"));
|
||||
callback();
|
||||
},
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const buildTenantUserQuery = (tid: number) => {
|
||||
const params: Record<string, string | number> = { tid };
|
||||
const kw = userSearchKeyword.value.trim();
|
||||
if (kw) params.keyword = kw;
|
||||
return params;
|
||||
};
|
||||
|
||||
const refreshTenantUsers = async () => {
|
||||
const id = props.tid;
|
||||
if (id == null) return;
|
||||
usersLoading.value = true;
|
||||
try {
|
||||
const usersRes = await getTenantUserList(buildTenantUserQuery(id));
|
||||
if (usersRes?.code === 200) {
|
||||
tenantUsers.value = usersRes?.data?.list || usersRes?.data || [];
|
||||
} else {
|
||||
tenantUsers.value = [];
|
||||
}
|
||||
} catch {
|
||||
tenantUsers.value = [];
|
||||
} finally {
|
||||
usersLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const loadUsersForTid = async (tid: number) => {
|
||||
usersLoading.value = true;
|
||||
try {
|
||||
const usersRes = await getTenantUserList({ tid });
|
||||
if (usersRes?.code === 200) {
|
||||
tenantUsers.value = usersRes?.data?.list || usersRes?.data || [];
|
||||
} else {
|
||||
tenantUsers.value = [];
|
||||
}
|
||||
} catch {
|
||||
tenantUsers.value = [];
|
||||
} finally {
|
||||
usersLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.tid,
|
||||
(id) => {
|
||||
userSearchKeyword.value = "";
|
||||
if (id == null) {
|
||||
tenantUsers.value = [];
|
||||
return;
|
||||
}
|
||||
loadUsersForTid(id);
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
const handleSearchUsers = () => {
|
||||
refreshTenantUsers();
|
||||
};
|
||||
|
||||
const resetUserSearch = () => {
|
||||
userSearchKeyword.value = "";
|
||||
refreshTenantUsers();
|
||||
};
|
||||
|
||||
const handleAddUser = () => {
|
||||
if (props.tid != null) {
|
||||
addUserRef.value?.open(props.tid);
|
||||
}
|
||||
};
|
||||
|
||||
const openPasswordDialog = (row: any) => {
|
||||
currentTenantUserId.value = Number(row?.id || 0) || null;
|
||||
passwordForm.password = "";
|
||||
passwordForm.password2 = "";
|
||||
passwordDialogVisible.value = true;
|
||||
};
|
||||
|
||||
const submitPassword = async () => {
|
||||
if (!passwordFormRef.value || !currentTenantUserId.value) return;
|
||||
|
||||
try {
|
||||
await passwordFormRef.value.validate();
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
|
||||
passwordSubmitting.value = true;
|
||||
try {
|
||||
const res = await editTenantUser(currentTenantUserId.value, { password: passwordForm.password });
|
||||
if (res.code === 200) {
|
||||
ElMessage.success("密码修改成功");
|
||||
passwordDialogVisible.value = false;
|
||||
} else {
|
||||
ElMessage.error(res.msg || "密码修改失败");
|
||||
}
|
||||
} finally {
|
||||
passwordSubmitting.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
refreshTenantUsers,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tenant-users-tab {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.user-search-form {
|
||||
margin-bottom: 8px;
|
||||
padding: 8px 12px;
|
||||
background: var(--el-fill-color-light);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.user-search-form :deep(.el-form-item) {
|
||||
margin-bottom: 8px;
|
||||
margin-top: 0;
|
||||
}
|
||||
</style>
|
||||
@ -37,6 +37,10 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
/**
|
||||
* 租户详情内「添加用户」:提交到 /platform/tenantUser/create,
|
||||
* 后端写入 yz_tenant_user(租户-用户绑定,含冗余账号/密码等字段)。
|
||||
*/
|
||||
import { ref, reactive } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { createTenantUser } from '@/api/tenantUser';
|
||||
@ -60,7 +64,7 @@ const formData = reactive({
|
||||
|
||||
const rules = {
|
||||
account: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
|
||||
password: [{ required: true, message: '请输入密码', trigger: 'blur', min: 6 }],
|
||||
password: [{ required: true, message: '请输入密码', trigger: 'blur', min: 5 }],
|
||||
password2: [
|
||||
{ required: true, message: '请再次输入密码', trigger: 'blur' },
|
||||
{
|
||||
@ -103,7 +107,12 @@ const open = (tenantId: number) => {
|
||||
const submitForm = async () => {
|
||||
if (!formRef.value) return;
|
||||
|
||||
try {
|
||||
await formRef.value.validate();
|
||||
} catch {
|
||||
// 校验失败时 validate 会 reject,避免 Uncaught (in promise)
|
||||
return;
|
||||
}
|
||||
|
||||
submitting.value = true;
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<el-drawer
|
||||
v-model="visible"
|
||||
title="租户详细信息"
|
||||
size="1000px"
|
||||
size="1200px"
|
||||
@closed="handleClosed"
|
||||
>
|
||||
<div v-loading="loading" class="detail-container">
|
||||
@ -40,150 +40,64 @@
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<div class="users-section">
|
||||
<div class="section-title">租户用户列表</div>
|
||||
<el-table :data="tenantUsers" v-loading="usersLoading" border style="width: 100%">
|
||||
<el-table-column prop="account" label="用户名" min-width="140" align="center" />
|
||||
<el-table-column prop="name" label="姓名" min-width="120" align="center" />
|
||||
<el-table-column prop="phone" label="手机号" min-width="140" align="center" />
|
||||
<el-table-column prop="email" label="邮箱" min-width="180" align="center" />
|
||||
<el-table-column prop="status" label="状态" width="90" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="Number(row.status) === 1 ? 'success' : 'danger'">
|
||||
{{ Number(row.status) === 1 ? "启用" : "禁用" }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="140" align="center" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button text type="primary" @click="openPasswordDialog(row)">
|
||||
修改密码
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-divider />
|
||||
|
||||
<div class="tenant-detail-body">
|
||||
<el-tabs
|
||||
v-model="activeTab"
|
||||
tab-position="left"
|
||||
class="tenant-detail-tabs"
|
||||
>
|
||||
<el-tab-pane label="租户用户" name="users">
|
||||
<div class="tab-pane-inner">
|
||||
<TenantUsersTab :tid="detailTenantId" />
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<!-- 后续功能:在此继续增加 <el-tab-pane label="..." name="...">...</el-tab-pane> -->
|
||||
</el-tabs>
|
||||
</div>
|
||||
|
||||
<div class="footer-actions">
|
||||
<!-- <div class="footer-actions">
|
||||
<el-button @click="visible = false">关闭</el-button>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
</el-drawer>
|
||||
|
||||
<el-dialog v-model="passwordDialogVisible" title="修改密码" width="420px" destroy-on-close>
|
||||
<el-form ref="passwordFormRef" :model="passwordForm" :rules="passwordRules" label-width="90px">
|
||||
<el-form-item label="新密码" prop="password">
|
||||
<el-input v-model="passwordForm.password" type="password" show-password placeholder="请输入新密码" />
|
||||
</el-form-item>
|
||||
<el-form-item label="确认密码" prop="password2">
|
||||
<el-input v-model="passwordForm.password2" type="password" show-password placeholder="请再次输入新密码" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="passwordDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="passwordSubmitting" @click="submitPassword">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive } from 'vue';
|
||||
import { ElMessage } from "element-plus";
|
||||
import { getTenantDetail } from '@/api/tenant';
|
||||
import { getTenantUserList, editTenantUser } from "@/api/tenantUser";
|
||||
import { ref } from "vue";
|
||||
import { getTenantDetail } from "@/api/tenant";
|
||||
import TenantUsersTab from "./TenantUsersTab.vue";
|
||||
|
||||
const visible = ref(false);
|
||||
const loading = ref(false);
|
||||
const usersLoading = ref(false);
|
||||
const detailData = ref<any>({});
|
||||
const tenantUsers = ref<any[]>([]);
|
||||
const detailTenantId = ref<number | null>(null);
|
||||
const activeTab = ref("users");
|
||||
|
||||
const passwordDialogVisible = ref(false);
|
||||
const passwordSubmitting = ref(false);
|
||||
const currentTenantUserId = ref<number | null>(null);
|
||||
const passwordFormRef = ref();
|
||||
const passwordForm = reactive({
|
||||
password: "",
|
||||
password2: "",
|
||||
});
|
||||
|
||||
const passwordRules = {
|
||||
password: [
|
||||
{ required: true, message: "请输入新密码", trigger: "blur" },
|
||||
{ min: 6, message: "密码至少 6 位", trigger: "blur" },
|
||||
],
|
||||
password2: [
|
||||
{ required: true, message: "请再次输入新密码", trigger: "blur" },
|
||||
{
|
||||
validator: (_rule: any, value: string, callback: any) => {
|
||||
if (!value) return callback(new Error("请再次输入新密码"));
|
||||
if (value !== passwordForm.password) return callback(new Error("两次密码不一致"));
|
||||
callback();
|
||||
},
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const openPasswordDialog = (row: any) => {
|
||||
currentTenantUserId.value = Number(row?.id || 0) || null;
|
||||
passwordForm.password = "";
|
||||
passwordForm.password2 = "";
|
||||
passwordDialogVisible.value = true;
|
||||
};
|
||||
|
||||
const submitPassword = async () => {
|
||||
if (!passwordFormRef.value || !currentTenantUserId.value) return;
|
||||
await passwordFormRef.value.validate();
|
||||
|
||||
passwordSubmitting.value = true;
|
||||
try {
|
||||
const res = await editTenantUser(currentTenantUserId.value, { password: passwordForm.password });
|
||||
if (res.code === 200) {
|
||||
ElMessage.success("密码修改成功");
|
||||
passwordDialogVisible.value = false;
|
||||
} else {
|
||||
ElMessage.error(res.msg || "密码修改失败");
|
||||
}
|
||||
} finally {
|
||||
passwordSubmitting.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 暴露给父组件的方法
|
||||
const open = async (id: number) => {
|
||||
detailTenantId.value = id;
|
||||
activeTab.value = "users";
|
||||
visible.value = true;
|
||||
loading.value = true;
|
||||
usersLoading.value = true;
|
||||
try {
|
||||
const [detailRes, usersRes] = await Promise.all([
|
||||
getTenantDetail(id),
|
||||
getTenantUserList({ tid: id }),
|
||||
]);
|
||||
|
||||
const detailRes = await getTenantDetail(id);
|
||||
if (detailRes.code === 200) {
|
||||
detailData.value = detailRes.data;
|
||||
}
|
||||
if (usersRes?.code === 200) {
|
||||
tenantUsers.value = usersRes?.data?.list || usersRes?.data || [];
|
||||
} else {
|
||||
tenantUsers.value = [];
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取详情失败', error);
|
||||
tenantUsers.value = [];
|
||||
console.error("获取详情失败", error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
usersLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleClosed = () => {
|
||||
detailData.value = {};
|
||||
tenantUsers.value = [];
|
||||
detailTenantId.value = null;
|
||||
activeTab.value = "users";
|
||||
};
|
||||
|
||||
// 必须暴露给外部
|
||||
defineExpose({ open });
|
||||
</script>
|
||||
|
||||
@ -196,19 +110,44 @@
|
||||
color: #333;
|
||||
}
|
||||
.footer-actions {
|
||||
margin-top: 30px;
|
||||
margin-top: 24px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.users-section {
|
||||
|
||||
.tenant-detail-body {
|
||||
margin-top: 20px;
|
||||
min-height: 0;
|
||||
}
|
||||
.section-title {
|
||||
margin-bottom: 12px;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
|
||||
.tenant-detail-tabs {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.tenant-detail-tabs :deep(.el-tabs__header) {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.tenant-detail-tabs :deep(.el-tabs__nav-wrap.is-left) {
|
||||
min-width: 112px;
|
||||
}
|
||||
|
||||
.tenant-detail-tabs :deep(.el-tabs__item) {
|
||||
justify-content: flex-start;
|
||||
padding: 0 16px;
|
||||
height: 44px;
|
||||
line-height: 44px;
|
||||
}
|
||||
|
||||
.tenant-detail-tabs :deep(.el-tabs__content) {
|
||||
padding: 0 0 0 16px;
|
||||
min-height: 320px;
|
||||
}
|
||||
|
||||
.tab-pane-inner {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
:deep(.el-descriptions__label) {
|
||||
width: 120px;
|
||||
background-color: #f5f7fa;
|
||||
|
||||
@ -111,14 +111,9 @@
|
||||
}}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="200" align="center" fixed="right">
|
||||
<el-table-column label="操作" width="160" align="center" fixed="right">
|
||||
<template #default="scope">
|
||||
<div class="action-icons">
|
||||
<el-tooltip content="增加用户" placement="top">
|
||||
<el-button text size="small" @click="handleAddUser(scope.row)">
|
||||
<el-icon><UserFilled /></el-icon>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="编辑" placement="top">
|
||||
<el-button text size="small" @click="editRef.open(scope.row.id)">
|
||||
<el-icon><Edit /></el-icon>
|
||||
@ -142,7 +137,6 @@
|
||||
<EditModal ref="editRef" @success="refresh" />
|
||||
<DetailDrawer ref="detailRef" />
|
||||
<Qualification ref="qualificationRef" />
|
||||
<AddUser ref="addUserRef" />
|
||||
|
||||
<!-- 分页 -->
|
||||
<div class="pagination-bar">
|
||||
@ -165,8 +159,7 @@ import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import EditModal from "./components/edit.vue";
|
||||
import DetailDrawer from "./components/detail.vue";
|
||||
import Qualification from "./components/qualification.vue";
|
||||
import AddUser from "./components/adduser.vue";
|
||||
import { UserFilled, Edit, Delete } from "@element-plus/icons-vue";
|
||||
import { Edit, Delete } from "@element-plus/icons-vue";
|
||||
|
||||
const total = ref(0);
|
||||
const page = ref(1);
|
||||
@ -177,7 +170,6 @@ const tenants = ref([]);
|
||||
|
||||
const editRef = ref();
|
||||
const detailRef = ref();
|
||||
const addUserRef = ref();
|
||||
const qualificationRef = ref();
|
||||
|
||||
// 删除
|
||||
@ -201,11 +193,6 @@ const handlePreview = (row: any) => {
|
||||
detailRef.value.open(row.id);
|
||||
};
|
||||
|
||||
//增加用户
|
||||
const handleAddUser = (row: any) => {
|
||||
addUserRef.value.open(row.id);
|
||||
};
|
||||
|
||||
// 资质
|
||||
const handleQualification = (row: any) => {
|
||||
qualificationRef.value.open(row.id);
|
||||
|
||||
@ -37,8 +37,8 @@
|
||||
{{ user.email || "未设置" }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="角色">
|
||||
<el-tag :type="getRoleTagType(user.group_id)" size="small">
|
||||
{{ getRoleName(user.group_id) }}
|
||||
<el-tag :type="getRoleTagType(user.rid)" size="small">
|
||||
{{ getRoleName(user.rid) }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="最后登录IP">
|
||||
@ -75,7 +75,7 @@ interface User {
|
||||
qq: string;
|
||||
email: string;
|
||||
sex: number;
|
||||
group_id: number;
|
||||
rid: number;
|
||||
status: number;
|
||||
last_login_ip: string;
|
||||
login_count: number;
|
||||
@ -128,19 +128,19 @@ const fetchRoles = async () => {
|
||||
};
|
||||
|
||||
// 获取角色tag状态
|
||||
function getRoleTagType(group_id: number): string {
|
||||
function getRoleTagType(rid: number): string {
|
||||
const typeMap: Record<number, string> = {
|
||||
1: "primary",
|
||||
2: "success",
|
||||
3: "warning",
|
||||
4: "danger",
|
||||
};
|
||||
return typeMap[group_id] || "primary";
|
||||
return typeMap[rid] || "primary";
|
||||
}
|
||||
|
||||
// 获取角色名称
|
||||
function getRoleName(group_id: number): string {
|
||||
const role = roles.value.find((r) => r.id === group_id);
|
||||
function getRoleName(rid: number): string {
|
||||
const role = roles.value.find((r) => r.id === rid);
|
||||
return role?.name || "未知";
|
||||
}
|
||||
|
||||
|
||||
@ -52,8 +52,8 @@
|
||||
</el-form-item>
|
||||
|
||||
<!-- 角色 -->
|
||||
<el-form-item label="角色" prop="group_id">
|
||||
<el-select v-model="form.group_id" placeholder="请选择角色" style="width: 100%">
|
||||
<el-form-item label="角色" prop="rid">
|
||||
<el-select v-model="form.rid" placeholder="请选择角色" style="width: 100%">
|
||||
<el-option
|
||||
v-for="role in roleOptions"
|
||||
:key="role.id"
|
||||
@ -125,7 +125,7 @@ const form = ref<any>({
|
||||
name: "",
|
||||
phone: "",
|
||||
qq: "",
|
||||
group_id: undefined,
|
||||
rid: undefined,
|
||||
sex: 1,
|
||||
password: "",
|
||||
confirmPassword: "",
|
||||
@ -194,7 +194,7 @@ const rules = {
|
||||
{ min: 3, max: 20, message: "账号长度在 3 到 20 个字符", trigger: "blur" },
|
||||
],
|
||||
name: [{ required: true, message: "请输入姓名", trigger: "blur" }],
|
||||
group_id: [{ required: true, message: "请选择角色", trigger: "change" }],
|
||||
rid: [{ required: true, message: "请选择角色", trigger: "change" }],
|
||||
password: [{ validator: validatePassword, trigger: "blur" }],
|
||||
confirmPassword: [{ validator: validateConfirmPassword, trigger: "blur" }],
|
||||
email: [{ type: "email", message: "请输入正确的邮箱地址", trigger: "blur" }],
|
||||
@ -251,7 +251,7 @@ const loadUserData = async (user: any) => {
|
||||
name: data.name,
|
||||
phone: data.phone,
|
||||
qq: data.qq,
|
||||
group_id: data.group_id,
|
||||
rid: data.rid,
|
||||
sex: sexValue,
|
||||
password: "",
|
||||
confirmPassword: "",
|
||||
@ -323,7 +323,7 @@ const handleSubmit = async () => {
|
||||
name: form.value.name,
|
||||
phone: form.value.phone,
|
||||
qq: form.value.qq,
|
||||
group_id: form.value.group_id,
|
||||
rid: form.value.rid,
|
||||
sex: form.value.sex,
|
||||
email: form.value.email,
|
||||
status: form.value.status,
|
||||
@ -345,7 +345,7 @@ const handleSubmit = async () => {
|
||||
name: form.value.name,
|
||||
phone: form.value.phone,
|
||||
qq: form.value.qq,
|
||||
group_id: form.value.group_id,
|
||||
rid: form.value.rid,
|
||||
sex: form.value.sex,
|
||||
email: form.value.email,
|
||||
status: form.value.status,
|
||||
@ -380,7 +380,7 @@ defineExpose({
|
||||
name: "",
|
||||
phone: "",
|
||||
qq: "",
|
||||
group_id: undefined,
|
||||
rid: undefined,
|
||||
sex: 1,
|
||||
password: "",
|
||||
confirmPassword: "",
|
||||
@ -417,7 +417,7 @@ defineExpose({
|
||||
name: "",
|
||||
phone: "",
|
||||
qq: "",
|
||||
group_id: undefined,
|
||||
rid: undefined,
|
||||
sex: 1,
|
||||
password: "",
|
||||
confirmPassword: "",
|
||||
|
||||
@ -28,10 +28,10 @@
|
||||
<span class="name-link" @click="handlePreview(scope.row)">{{ scope.row.name }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="group_id" label="角色" align="center">
|
||||
<el-table-column prop="rid" label="角色" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getRoleTagType(scope.row.group_id)" size="small">
|
||||
{{ getRoleTagText(roles, scope.row.group_id) }}
|
||||
<el-tag :type="getRoleTagType(scope.row.rid)" size="small">
|
||||
{{ getRoleTagText(roles, scope.row.rid) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@ -98,7 +98,7 @@ interface User {
|
||||
phone: string;
|
||||
qq: string;
|
||||
sex: number;
|
||||
group_id: number;
|
||||
rid: number;
|
||||
status: number;
|
||||
last_login_ip: string;
|
||||
login_count: number;
|
||||
@ -153,22 +153,22 @@ const fetchRoles = async () => {
|
||||
};
|
||||
|
||||
// 获取角色tag状态
|
||||
function getRoleTagType(group_id: number): string {
|
||||
function getRoleTagType(rid: number): string {
|
||||
const typeMap: Record<number, string> = {
|
||||
1: "primary",
|
||||
2: "success",
|
||||
3: "warning",
|
||||
4: "danger",
|
||||
};
|
||||
return typeMap[group_id] || "primary";
|
||||
return typeMap[rid] || "primary";
|
||||
}
|
||||
|
||||
// 获取角色tag文本
|
||||
function getRoleTagText(roles: Role[] | undefined, group_id: number): string {
|
||||
function getRoleTagText(roles: Role[] | undefined, rid: number): string {
|
||||
if (!roles || !Array.isArray(roles)) {
|
||||
return "未知";
|
||||
}
|
||||
return roles.find((role) => role.id === group_id)?.name || "未知";
|
||||
return roles.find((role) => role.id === rid)?.name || "未知";
|
||||
}
|
||||
|
||||
// 获取用户列表
|
||||
|
||||
@ -105,6 +105,9 @@ import { getEmailInfo, editEmailInfo, sendTestEmail } from "@/api/email";
|
||||
|
||||
const emailFormRef = ref<FormInstance>();
|
||||
|
||||
/** 是否已有数据库中的配置(允许保存时不重复填写密码) */
|
||||
const hasSavedConfig = ref(false);
|
||||
|
||||
const emailForm = reactive({
|
||||
fromAddress: "",
|
||||
fromName: "",
|
||||
@ -115,6 +118,18 @@ const emailForm = reactive({
|
||||
timeout: 30
|
||||
});
|
||||
|
||||
const validatePassword = (
|
||||
_rule: unknown,
|
||||
value: string,
|
||||
callback: (e?: Error) => void
|
||||
) => {
|
||||
if (!hasSavedConfig.value && !String(value || "").trim()) {
|
||||
callback(new Error("请输入授权码/密码"));
|
||||
return;
|
||||
}
|
||||
callback();
|
||||
};
|
||||
|
||||
const emailRules: FormRules = {
|
||||
fromAddress: [
|
||||
{ required: true, message: "请输入发件人邮箱", trigger: "blur" },
|
||||
@ -122,7 +137,7 @@ const emailRules: FormRules = {
|
||||
],
|
||||
host: [{ required: true, message: "请输入 SMTP 主机", trigger: "blur" }],
|
||||
port: [{ required: true, message: "请输入 SMTP 端口", trigger: "blur" }],
|
||||
password: [{ required: true, message: "请输入授权码/密码", trigger: "blur" }]
|
||||
password: [{ validator: validatePassword, trigger: "blur" }]
|
||||
};
|
||||
|
||||
const testEmail = ref("");
|
||||
@ -130,6 +145,7 @@ const testEmail = ref("");
|
||||
const loadEmailConfig = async () => {
|
||||
const res = await getEmailInfo();
|
||||
if (res.code === 200 && Array.isArray(res.data) && res.data.length > 0) {
|
||||
hasSavedConfig.value = true;
|
||||
const item = res.data[0];
|
||||
emailForm.fromAddress = item.from_address || "";
|
||||
emailForm.fromName = item.from_name || "";
|
||||
@ -138,6 +154,8 @@ const loadEmailConfig = async () => {
|
||||
emailForm.password = item.password || "";
|
||||
emailForm.encryption = item.encryption || "ssl";
|
||||
emailForm.timeout = item.timeout != null ? item.timeout : 30;
|
||||
} else {
|
||||
hasSavedConfig.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -177,8 +177,8 @@ const submitUpload = async () => {
|
||||
try {
|
||||
const formData = new FormData();
|
||||
formData.append("file", file.raw as File);
|
||||
if (props.categoryId) {
|
||||
formData.append("cate", props.categoryId.toString());
|
||||
if (props.categoryId !== undefined && props.categoryId !== null) {
|
||||
formData.append("cate", String(props.categoryId));
|
||||
}
|
||||
|
||||
// 模拟上传进度
|
||||
|
||||
@ -59,7 +59,6 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="create_time" label="创建时间" width="180" />
|
||||
<el-table-column prop="update_time" label="更新时间" width="180" />
|
||||
</el-table>
|
||||
|
||||
<div class="pagination" v-if="total > 0">
|
||||
|
||||
@ -23,5 +23,12 @@ export default defineConfig({
|
||||
},
|
||||
server: {
|
||||
port: 5000,
|
||||
// 开发时前端在 5000,接口走相对路径 /platform/*,由这里转发到 Go(go/conf/app.conf 默认 httpport=8080)
|
||||
proxy: {
|
||||
"/platform": {
|
||||
target: "http://127.0.0.1:8080",
|
||||
changeOrigin: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user