重构组合式api

This commit is contained in:
WUSIJIAN
2025-11-25 17:02:31 +08:00
parent 428989c993
commit 4d6f49598e
16 changed files with 2213 additions and 1631 deletions

View File

@@ -1,6 +1,65 @@
import request from '/@/utils/request';
/**
* 登录api接口集合
* @method signIn 用户登录
*/
export function getRoleList(query: Object) {
return request({
url: '/api/v1/system/role/list',
method: 'get',
params: query,
});
}
export function getRoleParams() {
return request({
url: '/api/v1/system/role/getParams',
method: 'get',
});
}
export function addRole(data: object) {
return request({
url: '/api/v1/system/role/add',
method: 'post',
data: data,
});
}
export function getRole(id: number) {
return request({
url: '/api/v1/system/role/get',
method: 'get',
params: { id },
});
}
export function editRole(data: object) {
return request({
url: '/api/v1/system/role/edit',
method: 'put',
data: data,
});
}
export function deleteRole(id: number) {
return request({
url: '/api/v1/system/role/delete',
method: 'delete',
data: { ids: [id] },
});
}
export function dataScope(data: any) {
return request({
url: '/api/v1/system/role/dataScope',
method: 'put',
data: data,
});
}
// 根据角色ID查询部门树结构
export function roleDeptTreeSelect(roleId: number) {
return request({
url: '/api/v1/system/role/deptTreeSelect',
method: 'get',
params: { roleId },
});
}

View File

@@ -0,0 +1,65 @@
import request from '/@/utils/request';
export function getRoleList(query: Object) {
return request({
url: '/api/v1/system/role/list',
method: 'get',
params: query,
});
}
export function getRoleParams() {
return request({
url: '/api/v1/system/role/getParams',
method: 'get',
});
}
export function addRole(data: object) {
return request({
url: '/api/v1/system/role/add',
method: 'post',
data: data,
});
}
export function getRole(id: number) {
return request({
url: '/api/v1/system/role/get',
method: 'get',
params: { id },
});
}
export function editRole(data: object) {
return request({
url: '/api/v1/system/role/edit',
method: 'put',
data: data,
});
}
export function deleteRole(id: number) {
return request({
url: '/api/v1/system/role/delete',
method: 'delete',
data: { ids: [id] },
});
}
export function dataScope(data: any) {
return request({
url: '/api/v1/system/role/dataScope',
method: 'put',
data: data,
});
}
// 根据角色ID查询部门树结构
export function roleDeptTreeSelect(roleId: number) {
return request({
url: '/api/v1/system/role/deptTreeSelect',
method: 'get',
params: { roleId },
});
}

View File

@@ -0,0 +1,24 @@
import request from '/@/utils/request';
export function logList(query: object) {
return request({
url: '/api/v1/system/loginLog/list',
method: 'get',
params: query,
});
}
export function deleteLog(ids: number[]) {
return request({
url: '/api/v1/system/loginLog/delete',
method: 'delete',
params: { ids },
});
}
export function clearLog() {
return request({
url: '/api/v1/system/loginLog/clear',
method: 'delete',
});
}

View File

@@ -0,0 +1,65 @@
import request from '/@/utils/request';
export function getRoleList(query: Object) {
return request({
url: '/api/v1/system/role/list',
method: 'get',
params: query,
});
}
export function getRoleParams() {
return request({
url: '/api/v1/system/role/getParams',
method: 'get',
});
}
export function addRole(data: object) {
return request({
url: '/api/v1/system/role/add',
method: 'post',
data: data,
});
}
export function getRole(id: number) {
return request({
url: '/api/v1/system/role/get',
method: 'get',
params: { id },
});
}
export function editRole(data: object) {
return request({
url: '/api/v1/system/role/edit',
method: 'put',
data: data,
});
}
export function deleteRole(id: number) {
return request({
url: '/api/v1/system/role/delete',
method: 'delete',
data: { ids: [id] },
});
}
export function dataScope(data: any) {
return request({
url: '/api/v1/system/role/dataScope',
method: 'put',
data: data,
});
}
// 根据角色ID查询部门树结构
export function roleDeptTreeSelect(roleId: number) {
return request({
url: '/api/v1/system/role/deptTreeSelect',
method: 'get',
params: { roleId },
});
}

View File

@@ -1,6 +1,5 @@
import { defineStore } from 'pinia';
import { ThemeConfigStates, ThemeConfigState } from './interface';
import { tr } from 'element-plus/es/locale';
/**
* 布局配置
@@ -20,7 +19,7 @@ export const useThemeConfig = defineStore('themeConfig', {
* 全局主题
*/
// 默认 primary 主题颜色
primary: '#409eff',
primary: '#FF2442',
// 是否开启深色模式
isIsDark: false,
@@ -33,21 +32,22 @@ export const useThemeConfig = defineStore('themeConfig', {
// 默认顶栏导航背景颜色
topBar: '#ffffff',
// 默认顶栏导航字体颜色
topBarColor: '#606266',
topBarColor: '#1A1A1A',
// 是否开启顶栏背景颜色渐变
isTopBarColorGradual: false,
isTopBarColorGradual: true,
// 默认菜单导航背景颜色
menuBar: '#354E67',
menuBar: '#F0F8FF',
// 默认菜单导航字体颜色
menuBarColor: '#eaeaea',
menuBarColor: '#2D3748',
// 是否开启菜单背景颜色渐变
isMenuBarColorGradual: false,
isMenuBarColorGradual: true,
// 默认分栏菜单背景颜色
columnsMenuBar: '#545c64',
columnsMenuBar: '#F8FAFF',
// 默认分栏菜单字体颜色
columnsMenuBarColor: '#e6e6e6',
columnsMenuBarColor: '#4A5568',
// 是否开启分栏菜单背景颜色渐变
isColumnsMenuBarColorGradual: false,
isColumnsMenuBarColorGradual: true,
/**
* 界面设置
*/

View File

@@ -1,268 +1,319 @@
<template>
<div class="system-edit-role-container">
<el-dialog :title="(formData.id === 0 ? '添加' : '修改') + '角色'" v-model="isShowDialog" width="769px">
<el-dialog :title="(formData.id === 0 ? '添加' : '修改') + '客服账号'" v-model="isShowDialog" width="769px">
<el-form ref="formRef" :model="formData" :rules="rules" size="default" label-width="90px">
<el-row :gutter="35">
<!-- 客服ID输入框 -->
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="客服id" prop="name">
<el-input placeholder="987698789" clearable></el-input>
<el-form-item label="客服ID" prop="name">
<el-input v-model="formData.name" placeholder="请输入客服ID987698789" clearable />
</el-form-item>
</el-col>
<!-- 客服平台输入框 -->
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="客服平台" prop="name">
<el-input placeholder="小红书" clearable></el-input>
<el-form-item label="客服平台" prop="platform">
<el-input v-model="formData.platform" placeholder="请输入客服平台,如:小红书" clearable />
</el-form-item>
</el-col>
<!-- <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="排序">
<el-input-number v-model="formData.listOrder" :min="0" controls-position="right" placeholder="请输入排序"
class="w100" />
</el-form-item>
</el-col> -->
<!-- 客服状态开关 -->
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="客服状态">
<el-switch v-model="formData.status" :active-value="1" :inactive-value="0" inline-prompt active-text="启"
inactive-text=""></el-switch>
<el-switch v-model="formData.status" :active-value="1" :inactive-value="0" inline-prompt active-text="启用" inactive-text="禁用" />
</el-form-item>
</el-col>
<!-- <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
<el-form-item label="角色描述">
<el-input v-model="formData.remark" type="textarea" placeholder="请输入角色描述" maxlength="150"></el-input>
</el-form-item>
</el-col> -->
<!-- <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
<el-form-item label="菜单权限">
<el-row :gutter="35">
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-checkbox v-model="menuExpand" @change="handleCheckedTreeExpand($event)">展开/折叠</el-checkbox>
<el-checkbox v-model="menuNodeAll" @change="handleCheckedTreeNodeAll($event)">全选/全不选</el-checkbox>
<el-checkbox v-model="menuCheckStrictly" @change="handleCheckedTreeConnect($event)">父子联动</el-checkbox>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
<el-tree :data="menuData" ref="menuRef" :props="menuProps" :default-checked-keys="formData.menuIds"
node-key="id" show-checkbox class="menu-data-tree tree-border"
:check-strictly="!menuCheckStrictly" />
</el-col>
</el-row>
</el-form-item>
</el-col> -->
</el-row>
</el-form>
<!-- 对话框底部操作按钮 -->
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel" size="default"> </el-button>
<el-button type="primary" @click="onSubmit" size="default" :loading="loading">{{ formData.id === 0 ? '新 增' :
'修改' }}</el-button>
<el-button type="primary" @click="onSubmit" size="default" :loading="loading">
{{ formData.id === 0 ? '新 增' : '修 改' }}
</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts">
import { reactive, toRefs, defineComponent, ref, getCurrentInstance, unref } from 'vue';
import { addRole, editRole, getRole, getRoleParams } from "/@/api/system/role";
import { ElMessage } from "element-plus";
import { refreshBackEndControlRoutes } from "/@/router/backEnd";
<script lang="ts" setup>
import { ref, reactive, toRefs, nextTick, defineEmits, defineExpose } from 'vue';
import { ElMessage, type FormInstance, type FormRules } from 'element-plus';
import { addRole, editRole, getRole } from '/@/api/system/role';
// 定义接口来定义对象的类型
/**
* 菜单数据结构接口
*/
interface MenuDataTree {
id: number;
pid: number;
title: string;
children?: MenuDataTree[];
}
interface DialogRow {
/**
* 表单数据接口
*/
interface DialogFormData {
id: number;
name: string;
status: number;
listOrder: number;
remark: string;
menuIds: Array<number>
}
interface RoleState {
loading: boolean;
isShowDialog: boolean;
formData: DialogRow;
menuData: Array<MenuDataTree>;
menuExpand: boolean;
menuNodeAll: boolean;
menuCheckStrictly: boolean;
menuProps: {
children: string;
label: string;
};
rules: object;
name: string; // 客服ID
platform: string; // 客服平台
status: number; // 状态0-禁用1-启用
listOrder: number; // 排序
remark: string; // 备注
menuIds: number[]; // 菜单ID数组
}
export default defineComponent({
name: 'systemEditRole',
setup(props, { emit }) {
const { proxy } = getCurrentInstance() as any;
const formRef = ref<HTMLElement | null>(null);
const menuRef = ref();
const state = reactive<RoleState>({
/**
* 组件状态接口
*/
interface ComponentState {
loading: boolean; // 提交加载状态
isShowDialog: boolean; // 对话框显示状态
formData: DialogFormData; // 表单数据
menuData: MenuDataTree[]; // 菜单数据
menuExpand: boolean; // 菜单展开状态
menuNodeAll: boolean; // 菜单全选状态
menuCheckStrictly: boolean; // 菜单严格选择模式
}
// 定义组件事件
const emit = defineEmits<{
/**
* 获取角色列表事件
*/
(e: 'getRoleList'): void;
}>();
// 响应式状态管理
const state = reactive<ComponentState>({
loading: false,
isShowDialog: false,
formData: {
id: 0,
name: '',
platform: '',
status: 1,
listOrder: 0,
remark: '',
menuIds: []
},
// 表单校验
rules: {
name: [
{ required: true, message: "角色名称不能为空", trigger: "blur" },
]
menuIds: [],
},
menuData: [],
menuExpand: false,
menuNodeAll: false,
menuCheckStrictly: false,
menuProps: {
});
// 表单验证规则
const rules: FormRules = {
name: [
{ required: true, message: '客服ID不能为空', trigger: 'blur' },
{ min: 3, max: 20, message: '客服ID长度在 3 到 20 个字符', trigger: 'blur' },
],
platform: [{ required: true, message: '客服平台不能为空', trigger: 'blur' }],
};
// 模板引用
const formRef = ref<FormInstance>();
// 解构状态数据,便于模板使用
const { loading, isShowDialog, formData, menuData, menuExpand, menuNodeAll, menuCheckStrictly } = toRefs(state);
// 菜单树配置
const menuProps = {
children: 'children',
label: 'title',
},
});
// 打开弹窗
const openDialog = (row?: DialogRow) => {
resetForm();
getMenuData();
if (row) {
getRole(row.id).then((res: any) => {
if (res.data.role) {
state.formData = res.data.role;
state.formData.menuIds = res.data.menuIds ?? []
}
})
}
state.isShowDialog = true;
};
// 关闭弹窗
/**
* 打开对话框
* @param row - 可选的编辑数据,如果传入则为编辑模式,否则为新增模式
*/
const openDialog = async (row?: DialogFormData) => {
// 重置表单状态
resetForm();
try {
if (row && row.id) {
// 编辑模式:获取角色详情
await handleEditMode(row.id);
}
// 显示对话框
state.isShowDialog = true;
} catch (error) {
console.error('打开对话框失败:', error);
ElMessage.error('初始化数据失败');
}
};
/**
* 处理编辑模式的数据获取
* @param id - 角色ID
*/
const handleEditMode = async (id: number) => {
try {
const res = await getRole(id);
if (res.data?.role) {
// 填充表单数据
state.formData = {
...res.data.role,
platform: res.data.role.platform || '',
menuIds: res.data.menuIds ?? [],
};
}
} catch (error) {
console.error('获取角色详情失败:', error);
throw new Error('获取角色详情失败');
}
};
/**
* 关闭对话框
*/
const closeDialog = () => {
state.isShowDialog = false;
};
// 取消
/**
* 取消操作
*/
const onCancel = () => {
closeDialog();
};
// 新增
const onSubmit = () => {
const formWrap = unref(formRef) as any;
if (!formWrap) return;
formWrap.validate((valid: boolean) => {
if (valid) {
/**
* 提交表单
*/
const onSubmit = async () => {
// 验证表单引用是否存在
if (!formRef.value) {
ElMessage.error('表单引用不存在');
return;
}
try {
// 表单验证
const valid = await formRef.value.validate();
if (!valid) {
ElMessage.warning('请完善表单信息');
return;
}
// 设置加载状态
state.loading = true;
state.formData.menuIds = getMenuAllCheckedKeys();
// 根据ID判断是新增还是修改
if (state.formData.id === 0) {
//添加
addRole(state.formData).then(() => {
ElMessage.success('角色添加成功');
closeDialog(); // 关闭弹窗
resetMenuSession()
emit('getRoleList')
}).finally(() => {
state.loading = false;
})
// 新增操作
await handleAddRole();
} else {
//修改
editRole(state.formData).then(() => {
ElMessage.success('角色修改成功');
closeDialog(); // 关闭弹窗
resetMenuSession()
emit('getRoleList')
}).finally(() => {
// 修改操作
await handleEditRole();
}
// 关闭对话框并刷新列表
closeDialog();
emit('getRoleList');
ElMessage.success(state.formData.id === 0 ? '添加成功' : '修改成功');
} catch (error) {
console.error('提交失败:', error);
ElMessage.error(state.formData.id === 0 ? '添加失败' : '修改失败');
} finally {
// 无论成功失败,都取消加载状态
state.loading = false;
})
}
}
});
};
// 获取菜单结构数据
const getMenuData = () => {
getRoleParams().then((res: any) => {
state.menuData = proxy.handleTree(res.data.menu, "id", "pid");
})
/**
* 处理新增角色
*/
const handleAddRole = async () => {
await addRole(state.formData);
};
/**
* 处理编辑角色
*/
const handleEditRole = async () => {
await editRole(state.formData);
};
/**
* 重置表单
*/
const resetForm = () => {
// 重置菜单选择状态
state.menuCheckStrictly = false;
state.menuExpand = false;
state.menuNodeAll = false;
// 重置表单数据
state.formData = {
id: 0,
name: '',
platform: '',
status: 1,
listOrder: 0,
remark: '',
menuIds: []
}
menuIds: [],
};
/** 树权限(展开/折叠)*/
const handleCheckedTreeExpand = (value: any) => {
let treeList = state.menuData;
for (let i = 0; i < treeList.length; i++) {
menuRef.value.store.nodesMap[treeList[i].id].expanded = value;
}
}
/** 树权限(全选/全不选) */
const handleCheckedTreeNodeAll = (value: any) => {
menuRef.value.setCheckedNodes(value ? state.menuData : []);
}
/** 树权限(父子联动) */
const handleCheckedTreeConnect = (value: any) => {
state.menuCheckStrictly = value ? true : false;
}
/** 所有菜单节点数据 */
function getMenuAllCheckedKeys() {
// 目前被选中的菜单节点
let checkedKeys = menuRef.value.getCheckedKeys();
// 半选中的菜单节点
let halfCheckedKeys = menuRef.value.getHalfCheckedKeys();
checkedKeys.unshift.apply(checkedKeys, halfCheckedKeys);
return checkedKeys;
}
// 重置菜单session
const resetMenuSession = () => {
refreshBackEndControlRoutes();
// 在下一个DOM更新周期后清除表单验证
nextTick(() => {
formRef.value?.clearValidate();
});
};
return {
// 暴露方法给父组件使用
defineExpose({
/**
* 打开对话框方法
*/
openDialog,
/**
* 关闭对话框方法
*/
closeDialog,
onCancel,
onSubmit,
menuRef,
formRef,
handleCheckedTreeExpand,
handleCheckedTreeNodeAll,
handleCheckedTreeConnect,
resetMenuSession,
...toRefs(state),
};
},
});
</script>
<style scoped lang="scss">
.system-edit-role-container {
// 对话框内容区域样式
:deep(.el-dialog__body) {
padding: 20px;
}
// 底部按钮区域样式
.dialog-footer {
display: flex;
justify-content: flex-end;
align-items: center;
gap: 10px;
}
}
// 间距工具类
.mb20 {
margin-bottom: 20px;
}
// 树形组件边框样式
.tree-border {
margin-top: 5px;
border: 1px solid #e5e6e7 !important;
border: 1px solid #e5e6e7;
border-radius: 4px;
}
.system-edit-role-container {
// 菜单数据树样式
.menu-data-tree {
border: var(--el-input-border, var(--el-border-base));
border-radius: var(--el-input-border-radius, var(--el-border-radius-base));
padding: 5px;
}
}
</style>

View File

@@ -7,14 +7,14 @@
<el-input size="default" v-model="tableData.param.roleName" placeholder="请输入客服ID" class="w-50 m-2" clearable />
</el-form-item>
<el-form-item label="客服平台" prop="status" style="width: 200px">
<el-select placeholder="小红书" clearable size="default" style="width: 240px">
<el-option :label="'小红书'" :value="''" />
<el-option :label="'抖音'" :value="''" />
<el-option :label="'快手'" :value="''" />
<el-select v-model="tableData.param.platform" placeholder="请选择客服平台" clearable size="default" style="width: 240px">
<el-option label="小红书" value="xiaohongshu" />
<el-option label="抖音" value="douyin" />
<el-option label="快手" value="kuaishou" />
</el-select>
</el-form-item>
<el-form-item>
<el-button size="default" type="primary" class="ml10" @click="roleList">
<el-button size="default" type="primary" class="ml10" @click="handleSearch">
<el-icon>
<ele-Search />
</el-icon>
@@ -29,9 +29,9 @@
</el-form-item>
</el-form>
</div>
<el-table :data="tableData.data" style="width: 100%">
<el-table :data="tableData.data" style="width: 100%" v-loading="tableData.loading">
<el-table-column type="index" label="序号" width="60" />
<el-table-column prop="name" label="客服id" show-overflow-tooltip></el-table-column>
<el-table-column prop="name" label="客服ID" show-overflow-tooltip></el-table-column>
<el-table-column prop="status" label="客服状态" show-overflow-tooltip>
<template #default="scope">
<el-tag type="success" v-if="scope.row.status === 1">启用</el-tag>
@@ -39,18 +39,18 @@
</template>
</el-table-column>
<el-table-column prop="remark" label="客服平台" show-overflow-tooltip></el-table-column>
<el-table-column prop="key" label="创建人" show-overflow-tooltip></el-table-column>
<el-table-column prop="key" label="修改人" show-overflow-tooltip></el-table-column>
<el-table-column prop="creator" label="创建人" show-overflow-tooltip></el-table-column>
<el-table-column prop="updater" label="修改人" show-overflow-tooltip></el-table-column>
<el-table-column prop="createdAt" label="创建时间" show-overflow-tooltip></el-table-column>
<el-table-column prop="createdAt" label="修改时间" show-overflow-tooltip></el-table-column>
<el-table-column prop="updatedAt" label="修改时间" show-overflow-tooltip></el-table-column>
<el-table-column label="操作" width="220">
<template #default="scope">
<el-button size="small" text type="primary" @click="onOpenEditRole(scope.row)"
><el-icon><ele-EditPen /></el-icon>修改</el-button
>
<el-button size="small" text type="primary" @click="onRowDel(scope.row)"
><el-icon><ele-DeleteFilled /></el-icon>删除</el-button
>
<el-button size="small" text type="primary" @click="onOpenEditRole(scope.row)">
<el-icon><ele-EditPen /></el-icon>修改
</el-button>
<el-button size="small" text type="primary" @click="onRowDel(scope.row)">
<el-icon><ele-DeleteFilled /></el-icon>删除
</el-button>
</template>
</el-table-column>
</el-table>
@@ -59,20 +59,21 @@
:total="tableData.total"
v-model:page="tableData.param.pageNum"
v-model:limit="tableData.param.pageSize"
@pagination="roleList"
@pagination="handlePaginationChange"
/>
</el-card>
<EditRole ref="editRoleRef" @getRoleList="roleList" />
</div>
</template>
<script lang="ts">
import { toRefs, reactive, onMounted, ref, defineComponent, toRaw, getCurrentInstance } from 'vue';
<script lang="ts" setup>
import { ref, reactive, onMounted, nextTick } from 'vue';
import { ElMessageBox, ElMessage } from 'element-plus';
import EditRole from '/@/views/customerService/account/component/editRole.vue';
import { deleteRole, getRoleList } from '/@/api/system/role';
// 定义接口来定义对象的类型
interface TableData {
import { deleteRole, getRoleList } from '/@/api/customerService/account';
// 定义类型接口
interface TableDataItem {
id: number;
status: number;
listOrder: number;
@@ -80,22 +81,29 @@ interface TableData {
remark: string;
dataScope: number;
createdAt: string;
updatedAt: string;
key: string;
creator?: string;
updater?: string;
}
interface TableDataState {
tableData: {
data: Array<TableData>;
total: number;
loading: boolean;
param: {
interface TableParam {
roleName: string;
platform: string;
roleStatus: string;
pageNum: number;
pageSize: number;
};
};
}
const dateList = ref([
interface TableState {
data: TableDataItem[];
total: number;
loading: boolean;
param: TableParam;
}
// 模拟数据
const mockData: TableDataItem[] = [
{
id: 1,
status: 1,
@@ -106,6 +114,8 @@ const dateList = ref([
createdAt: '2022-04-01 11:38:39',
updatedAt: '2022-04-28 10:00:15',
key: 'list',
creator: '张三',
updater: '李四',
},
{
id: 2,
@@ -117,6 +127,8 @@ const dateList = ref([
createdAt: '2022-04-01 11:38:39',
updatedAt: '2022-04-28 10:01:34',
key: 'list',
creator: '王五',
updater: '赵六',
},
{
id: 3,
@@ -128,6 +140,8 @@ const dateList = ref([
createdAt: '2022-04-01 11:38:39',
updatedAt: '2022-04-01 11:38:39',
key: '李四',
creator: '孙七',
updater: '周八',
},
{
id: 4,
@@ -139,6 +153,8 @@ const dateList = ref([
createdAt: '2022-04-01 11:38:39',
updatedAt: '2022-04-01 11:38:39',
key: '张三',
creator: '吴九',
updater: '郑十',
},
{
id: 5,
@@ -150,6 +166,8 @@ const dateList = ref([
createdAt: '2022-04-01 11:38:39',
updatedAt: '2022-04-01 11:38:39',
key: 'zhang',
creator: '钱一',
updater: '孙二',
},
{
id: 8,
@@ -161,102 +179,150 @@ const dateList = ref([
createdAt: '2022-04-01 11:38:39',
updatedAt: '2022-04-06 09:53:40',
key: 'list',
creator: '李三',
updater: '王四',
},
]);
];
export default defineComponent({
name: 'apiV1SystemRoleList',
components: { EditRole },
setup() {
const { proxy } = getCurrentInstance() as any;
const addRoleRef = ref();
const editRoleRef = ref();
const dataScopeRef = ref();
const state = reactive<TableDataState>({
tableData: {
// 响应式数据
const tableData = reactive<TableState>({
data: [],
total: 0,
loading: false,
param: {
roleName: '',
platform: '',
roleStatus: '',
pageNum: 1,
pageSize: 10,
},
},
});
// 初始化表格数据
const initTableData = () => {
roleList();
};
const roleList = () => {
const data: Array<TableData> = [];
getRoleList(state.tableData.param).then((res) => {
const list = res.data.list ?? [];
list.map((item: TableData) => {
data.push({
id: item.id,
status: item.status,
listOrder: item.listOrder,
name: item.name,
remark: item.remark,
dataScope: item.dataScope,
createdAt: item.createdAt,
});
});
state.tableData.data = dateList.value;
state.tableData.total = dateList.value.length;
});
};
// 打开新增角色弹窗
const onOpenAddRole = () => {
editRoleRef.value.openDialog();
};
// 打开修改角色弹窗
const onOpenEditRole = (row: Object) => {
editRoleRef.value.openDialog(toRaw(row));
// 模板引用
const editRoleRef = ref<InstanceType<typeof EditRole>>();
/**
* 获取角色列表
*/
const roleList = async () => {
try {
tableData.loading = true;
// 实际API调用
// const res = await getRoleList(tableData.param);
// tableData.data = res.data.list || [];
// tableData.total = res.data.total || 0;
// 模拟数据
await new Promise((resolve) => setTimeout(resolve, 500)); // 模拟网络延迟
tableData.data = mockData;
tableData.total = mockData.length;
} catch (error) {
console.error('获取角色列表失败:', error);
ElMessage.error('获取数据失败');
} finally {
tableData.loading = false;
}
};
// 删除角色
const onRowDel = (row: any) => {
ElMessageBox.confirm(`此操作将永久删除角色:“${row.name}”,是否继续?`, '提示', {
/**
* 处理搜索
*/
const handleSearch = () => {
tableData.param.pageNum = 1; // 重置到第一页
roleList();
};
/**
* 处理分页变化
* @param pagination - 分页参数
*/
const handlePaginationChange = (pagination: { page: number; limit: number }) => {
tableData.param.pageNum = pagination.page;
tableData.param.pageSize = pagination.limit;
roleList();
};
/**
* 打开新增角色对话框
*/
const onOpenAddRole = () => {
if (editRoleRef.value) {
editRoleRef.value.openDialog();
}
};
/**
* 打开编辑角色对话框
* @param row - 角色数据
*/
const onOpenEditRole = (row: TableDataItem) => {
if (editRoleRef.value) {
editRoleRef.value.openDialog(row);
}
};
/**
* 删除角色
* @param row - 角色数据
*/
const onRowDel = async (row: TableDataItem) => {
try {
await ElMessageBox.confirm(`此操作将永久删除客服账号:"${row.name}",是否继续?`, '提示', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
deleteRole(row.id).then(() => {
ElMessage.success('删除成功');
proxy.$refs['editRoleRef'].resetMenuSession();
roleList();
});
})
.catch(() => {});
// 实际删除操作
// await deleteRole(row.id);
// 模拟删除成功
ElMessage.success('删除成功');
// 刷新列表
await roleList();
} catch (error) {
// 用户取消操作,不处理
console.log('用户取消删除操作');
}
};
// 分页改变
const onHandleSizeChange = (val: number) => {
state.tableData.param.pageSize = val;
/**
* 初始化表格数据
*/
const initTableData = () => {
roleList();
};
// 分页改变
const onHandleCurrentChange = (val: number) => {
state.tableData.param.pageNum = val;
};
// 页面加载时
// 生命周期
onMounted(() => {
initTableData();
});
return {
addRoleRef,
editRoleRef,
dataScopeRef,
onOpenAddRole,
onOpenEditRole,
onRowDel,
onHandleSizeChange,
onHandleCurrentChange,
roleList,
...toRefs(state),
};
},
});
</script>
<style scoped>
.system-user-search {
margin-bottom: 15px;
}
.mb15 {
margin-bottom: 15px;
}
.ml10 {
margin-left: 10px;
}
.w-50 {
width: 240px;
}
.system-role-container {
padding: 20px;
}
:deep(.el-table) {
margin-top: 10px;
}
</style>

View File

@@ -1,85 +1,74 @@
<template>
<div class="system-edit-role-container">
<el-dialog title="" v-model="isShowDialog" width="1000px">
<el-dialog :title="(formData.id === 0 ? '添加' : '修改') + '产品'" v-model="isShowDialog" width="1000px">
<el-form ref="formRef" :model="formData" :rules="rules" size="default" label-width="90px">
<el-row :gutter="35">
<!-- 产品名称输入框 -->
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="产品名称" prop="name">
<el-input placeholder="xxx" clearable></el-input>
<el-input v-model="formData.name" placeholder="请输入产品名称" clearable />
</el-form-item>
</el-col>
<!-- 新增富文本编辑器 -->
<!-- 富文本编辑器 -->
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
<el-form-item label="产品详情" prop="content">
<Editor height="400px" placeholder="请输入产品详细描述..." />
<Editor v-model="formData.content" height="400px" placeholder="请输入产品详细描述..." />
</el-form-item>
</el-col>
</el-row>
</el-form>
<!-- 对话框底部按钮 -->
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel" size="default"> </el-button>
<el-button type="primary" @click="onSubmit" size="default" :loading="loading">{{ formData.id === 0 ? '新 增' :
'修改' }}</el-button>
<el-button type="primary" @click="onSubmit" size="default" :loading="loading">
{{ formData.id === 0 ? '新 增' : '修 改' }}
</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts">
import { reactive, toRefs, defineComponent, ref, getCurrentInstance, unref, } from 'vue';
import { addRole, editRole, getRole, getRoleParams } from "/@/api/system/role";
import { ElMessage } from "element-plus";
import { refreshBackEndControlRoutes } from "/@/router/backEnd";
// 引入富文本编辑器组件
<script lang="ts" setup>
import { ref, reactive, toRefs, nextTick } from 'vue';
import { ElMessage, type FormInstance, type FormRules } from 'element-plus';
import { addRole, editRole, getRole } from '/@/api/system/role';
import Editor from '/@/components/editor/index.vue';
// 定义接口来定义对象的类型
interface MenuDataTree {
id: number;
pid: number;
title: string;
children?: MenuDataTree[];
}
interface DialogRow {
id: number;
name: string;
status: number;
listOrder: number;
remark: string;
menuIds: Array<number>
}
interface RoleState {
loading: boolean;
isShowDialog: boolean;
formData: DialogRow;
menuData: Array<MenuDataTree>;
menuExpand: boolean;
menuNodeAll: boolean;
menuCheckStrictly: boolean;
menuProps: {
children: string;
label: string;
};
rules: object;
/**
* 产品表单数据接口
*/
interface ProductFormData {
id: number; // 产品ID
name: string; // 产品名称
status: number; // 状态1-启用0-禁用
listOrder: number; // 排序
remark: string; // 备注
content?: string; // 产品详情(富文本内容)
}
export default defineComponent({
name: 'systemEditRole',
components: {
Editor // 注册富文本组件
},
setup(props, { emit }) {
const { proxy } = getCurrentInstance() as any;
const formRef = ref<HTMLElement | null>(null);
const menuRef = ref();
const state = reactive<RoleState>({
/**
* 组件状态接口
*/
interface ComponentState {
loading: boolean; // 提交加载状态
isShowDialog: boolean; // 对话框显示状态
formData: ProductFormData; // 表单数据
}
// 定义组件事件
const emit = defineEmits<{
/**
* 获取产品列表事件 - 用于刷新父组件列表
*/
(e: 'getRoleList'): void;
}>();
// 响应式状态
const state = reactive<ComponentState>({
loading: false,
isShowDialog: false,
formData: {
@@ -88,157 +77,175 @@ export default defineComponent({
status: 1,
listOrder: 0,
remark: '',
menuIds: []
},
// 表单校验
rules: {
name: [
{ required: true, message: "角色名称不能为空", trigger: "blur" },
]
},
menuData: [],
menuExpand: false,
menuNodeAll: false,
menuCheckStrictly: false,
menuProps: {
children: 'children',
label: 'title',
content: '',
},
});
// 打开弹窗
const openDialog = (row?: DialogRow) => {
resetForm();
getMenuData();
if (row) {
getRole(row.id).then((res: any) => {
if (res.data.role) {
state.formData = res.data.role;
state.formData.menuIds = res.data.menuIds ?? []
}
})
}
state.isShowDialog = true;
// 表单验证规则
const rules: FormRules = {
name: [
{ required: true, message: '产品名称不能为空', trigger: 'blur' },
{ min: 2, max: 50, message: '产品名称长度在 2 到 50 个字符', trigger: 'blur' },
],
content: [{ required: true, message: '产品详情不能为空', trigger: 'blur' }],
};
// 关闭弹窗
// 模板引用
const formRef = ref<FormInstance>();
// 解构状态数据,便于模板使用
const { loading, isShowDialog, formData } = toRefs(state);
/**
* 打开对话框
* @param row - 可选的编辑数据,如果传入则为编辑模式,否则为新增模式
*/
const openDialog = async (row?: ProductFormData) => {
// 重置表单
resetForm();
try {
if (row && row.id) {
// 编辑模式:获取产品详情
await handleEditMode(row.id);
}
// 显示对话框
state.isShowDialog = true;
} catch (error) {
console.error('打开对话框失败:', error);
ElMessage.error('初始化数据失败');
}
};
/**
* 处理编辑模式的数据获取
* @param id - 产品ID
*/
const handleEditMode = async (id: number) => {
try {
const res = await getRole(id);
if (res.data?.role) {
// 填充表单数据
state.formData = {
...res.data.role,
content: res.data.role.content || '',
};
}
} catch (error) {
console.error('获取产品详情失败:', error);
throw new Error('获取产品详情失败');
}
};
/**
* 关闭对话框
*/
const closeDialog = () => {
state.isShowDialog = false;
};
// 取消
/**
* 取消操作
*/
const onCancel = () => {
closeDialog();
};
// 新增
const onSubmit = () => {
const formWrap = unref(formRef) as any;
if (!formWrap) return;
formWrap.validate((valid: boolean) => {
if (valid) {
/**
* 提交表单
*/
const onSubmit = async () => {
if (!formRef.value) {
ElMessage.error('表单引用不存在');
return;
}
try {
// 表单验证
const valid = await formRef.value.validate();
if (!valid) {
ElMessage.warning('请完善表单信息');
return;
}
// 设置加载状态
state.loading = true;
state.formData.menuIds = getMenuAllCheckedKeys();
// 根据ID判断是新增还是修改
if (state.formData.id === 0) {
//添加
addRole(state.formData).then(() => {
ElMessage.success('角色添加成功');
closeDialog(); // 关闭弹窗
resetMenuSession()
emit('getRoleList')
}).finally(() => {
state.loading = false;
})
// 新增产品
await addRole(state.formData);
ElMessage.success('产品添加成功');
} else {
//修改
editRole(state.formData).then(() => {
ElMessage.success('角色修改成功');
closeDialog(); // 关闭弹窗
resetMenuSession()
emit('getRoleList')
}).finally(() => {
// 修改产品
await editRole(state.formData);
ElMessage.success('产品修改成功');
}
// 关闭对话框并刷新列表
closeDialog();
emit('getRoleList');
} catch (error) {
console.error('提交失败:', error);
ElMessage.error(state.formData.id === 0 ? '添加失败' : '修改失败');
} finally {
state.loading = false;
})
}
}
});
};
// 获取菜单结构数据
const getMenuData = () => {
getRoleParams().then((res: any) => {
state.menuData = proxy.handleTree(res.data.menu, "id", "pid");
})
};
/**
* 重置表单
*/
const resetForm = () => {
state.menuCheckStrictly = false;
state.menuExpand = false;
state.menuNodeAll = false;
// 重置表单数据
state.formData = {
id: 0,
name: '',
status: 1,
listOrder: 0,
remark: '',
menuIds: []
}
content: '',
};
/** 树权限(展开/折叠)*/
const handleCheckedTreeExpand = (value: any) => {
let treeList = state.menuData;
for (let i = 0; i < treeList.length; i++) {
menuRef.value.store.nodesMap[treeList[i].id].expanded = value;
}
}
/** 树权限(全选/全不选) */
const handleCheckedTreeNodeAll = (value: any) => {
menuRef.value.setCheckedNodes(value ? state.menuData : []);
}
/** 树权限(父子联动) */
const handleCheckedTreeConnect = (value: any) => {
state.menuCheckStrictly = value ? true : false;
}
/** 所有菜单节点数据 */
function getMenuAllCheckedKeys() {
// 目前被选中的菜单节点
let checkedKeys = menuRef.value.getCheckedKeys();
// 半选中的菜单节点
let halfCheckedKeys = menuRef.value.getHalfCheckedKeys();
checkedKeys.unshift.apply(checkedKeys, halfCheckedKeys);
return checkedKeys;
}
// 重置菜单session
const resetMenuSession = () => {
refreshBackEndControlRoutes();
// 在下一个DOM更新周期后清除表单验证
nextTick(() => {
formRef.value?.clearValidate();
});
};
return {
// 暴露方法给父组件使用
defineExpose({
/**
* 打开对话框方法
*/
openDialog,
/**
* 关闭对话框方法
*/
closeDialog,
onCancel,
onSubmit,
menuRef,
formRef,
handleCheckedTreeExpand,
handleCheckedTreeNodeAll,
handleCheckedTreeConnect,
resetMenuSession,
...toRefs(state),
};
},
});
</script>
<style scoped lang="scss">
.tree-border {
margin-top: 5px;
border: 1px solid #e5e6e7 !important;
border-radius: 4px;
.system-edit-role-container {
// 对话框内容区域样式
:deep(.el-dialog__body) {
padding: 20px;
}
.system-edit-role-container {
.menu-data-tree {
border: var(--el-input-border, var(--el-border-base));
border-radius: var(--el-input-border-radius, var(--el-border-radius-base));
padding: 5px;
// 底部按钮区域样式
.dialog-footer {
display: flex;
justify-content: flex-end;
align-items: center;
gap: 10px;
}
}
// 间距工具类
.mb20 {
margin-bottom: 20px;
}
</style>

View File

@@ -9,13 +9,10 @@
</el-dialog>
</template>
<script lang="ts">
import { defineComponent, ref, reactive } from 'vue';
<script lang="ts" setup>
import { ref, reactive } from 'vue';
import { ElMessage } from 'element-plus';
export default defineComponent({
name: 'ExportDialog',
setup() {
const isShowDialog = ref(false);
const tableData = ref([]);
@@ -37,12 +34,11 @@ export default defineComponent({
isShowDialog.value = false;
};
return {
isShowDialog,
form,
// 暴露方法给父组件使用
defineExpose({
/**
* 打开对话框方法
*/
openDialog,
handleExport,
};
},
});
</script>

View File

@@ -1,21 +1,7 @@
<template>
<el-dialog title="导入产品" v-model="isShowDialog" width="500px" destroy-on-close>
<el-dialog title="导入产品" v-model="isShowDialog" width="500px" destroy-on-close @close="handleDialogClose">
<div class="import-container">
<!-- 导入说明 -->
<div class="import-instructions mt20" style="margin-bottom: 40px">
<el-card shadow="never" class="box-card">
<el-text type="info" size="small">
<ul class="instruction-list">
<li>1. 请先下载模板ZIP包查看Word文档格式要求</li>
<li>2. 按照模板中的Word文档格式准备您的产品文档</li>
<li>3. 将您的Word文档打包成ZIP格式上传</li>
<li>4. 支持 .docx 格式的Word文档</li>
</ul>
</el-text>
</el-card>
</div>
<!-- 上传区域 -->
<!-- 文件上传区域 -->
<el-upload
ref="uploadRef"
class="upload-demo"
@@ -25,38 +11,49 @@
:data="uploadData"
:accept="acceptFileTypes"
:limit="1"
:on-success="handleSuccess"
:on-error="handleError"
:on-success="handleUploadSuccess"
:on-error="handleUploadError"
:before-upload="beforeUpload"
:on-exceed="handleExceed"
:on-remove="handleRemove"
>
<div class="el-upload-area">
<el-icon class="el-icon--upload"><ele-UploadFilled /></el-icon>
<el-icon class="el-icon--upload">
<UploadFilled />
</el-icon>
<div class="el-upload__text">将ZIP文件拖到此处<em>点击上传</em></div>
</div>
<template #tip>
<div class="el-upload__tip">
<el-text type="info" size="small"> 请上传 {{ acceptFileTypes }} 格式文件且不超过 {{ fileSizeLimit }}MB </el-text>
</div>
</template>
</el-upload>
<!-- 下载模板按钮 -->
<div class="download-section" style="margin-top: 20px; text-align: center">
<el-button type="primary" size="default" :loading="downloadLoading" @click="downloadTemplate" style="width: 200px">
<template #icon>
<el-icon><ele-Download /></el-icon>
</template>
{{ downloadLoading ? '正在生成模板...' : '下载导入模板' }}
</el-button>
<!-- 导入说明 -->
<div class="import-instructions mt20">
<el-card shadow="never" class="box-card">
<el-text type="info" size="small">
<ul class="instruction-list">
<li>1. 请先下载模板ZIP包查看Word文档格式要求</li>
<li>2. 按照模板中的Word文档格式准备您的产品文档</li>
<li>3. 将您的Word文档打包成ZIP格式上传</li>
<li>4. 支持 .docx 格式的Word文档</li>
<li>5. 文件大小不能超过 {{ fileSizeLimit }}MB</li>
</ul>
</el-text>
</el-card>
</div>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="isShowDialog = false">取消</el-button>
<el-button type="primary" :loading="loading" @click="submitUpload">
<!-- 下载模板按钮 -->
<el-button type="primary" size="default" :loading="downloadLoading" @click="handleDownloadTemplate" class="download-btn">
<template #icon>
<el-icon><Download /></el-icon>
</template>
{{ downloadLoading ? '生成中...' : '下载模板' }}
</el-button>
<el-button @click="handleCancel">取消</el-button>
<el-button type="primary" :loading="loading" @click="handleSubmitUpload">
{{ loading ? '导入中...' : '开始导入' }}
</el-button>
</span>
@@ -64,27 +61,31 @@
</el-dialog>
</template>
<script lang="ts">
import { defineComponent, ref, reactive } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
<script lang="ts" setup>
import { ref, reactive, nextTick } from 'vue';
import { ElMessage, ElMessageBox, type UploadInstance } from 'element-plus';
import { UploadFilled, Download } from '@element-plus/icons-vue';
import JSZip from 'jszip';
import { saveAs } from 'file-saver';
export default defineComponent({
name: 'ImportDialog',
components: { UploadFilled, Download },
emits: ['getRoleList'],
setup(props, { emit }) {
// 定义组件事件
const emit = defineEmits<{
/**
* 获取产品列表事件 - 导入成功后刷新列表
*/
(e: 'getRoleList'): void;
}>();
// 响应式状态
const isShowDialog = ref(false);
const uploadRef = ref();
const loading = ref(false);
const downloadLoading = ref(false);
const uploadRef = ref<UploadInstance>();
// 上传配置
const uploadAction = ref('/api/product/import-word-zip');
const uploadAction = '/api/product/import-word-zip';
const uploadHeaders = reactive({
Authorization: 'Bearer ' + localStorage.getItem('token'),
Authorization: `Bearer ${localStorage.getItem('token') || ''}`,
});
const uploadData = reactive({
type: 'word-zip',
@@ -92,52 +93,10 @@ export default defineComponent({
const acceptFileTypes = '.zip,.ZIP';
const fileSizeLimit = 50;
// 打开对话框
const openDialog = () => {
isShowDialog.value = true;
loading.value = false;
downloadLoading.value = false;
if (uploadRef.value) {
uploadRef.value.clearFiles();
}
};
// 创建Word文档的二进制内容模拟真实的.docx格式
const createWordDocumentContent = (content: string) => {
// Word文档的基本结构简化版
// 实际Word文档是复杂的XML结构这里创建一个简单的文本文件
// 在实际项目中应该使用后端生成真正的Word文档
// 创建一个包含基本Word文档结构的文本
const wordContent = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<?mso-application progid="Word.Document"?>
<w:wordDocument xmlns:w="http://schemas.microsoft.com/office/word/2003/wordml">
<w:body>
<w:p>
<w:r>
<w:t>${content.replace(/\n/g, '</w:t></w:r></w:p><w:p><w:r><w:t>')}</w:t>
</w:r>
</w:p>
</w:body>
</w:wordDocument>`;
return new Blob([wordContent], { type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' });
};
// 创建文本文件内容
const createTextFile = (content: string) => {
return new Blob([content], { type: 'text/plain;charset=utf-8' });
};
// 下载模板 - 修复版本生成包含真实文件的ZIP包
const downloadTemplate = async () => {
downloadLoading.value = true;
try {
const zip = new JSZip();
// 1. 创建产品说明书模板内容
const manualContent = `产品说明书模板
// 模板内容定义
const templateContents = {
/** 产品说明书模板内容 */
manual: `产品说明书模板
产品名称_________________________
产品型号_________________________
@@ -166,10 +125,10 @@ ___________________________________
注意事项:
• ________________________________
• ________________________________
• ________________________________`;
• ________________________________`,
// 2. 创建技术规格书模板内容
const specContent = `技术规格书模板
/** 技术规格书模板内容 */
spec: `技术规格书模板
产品名称_________________________
产品型号_________________________
@@ -193,10 +152,10 @@ ___________________________________
4. 安全标准
- 认证_______________________
- 防护等级___________________`;
- 防护等级___________________`,
// 3. 创建使用说明文档
const readmeContent = `产品文档导入模板使用说明
/** 使用说明文档内容 */
readme: `产品文档导入模板使用说明
================================
欢迎使用产品文档导入模板!
@@ -227,167 +186,207 @@ ___________________________________
-----------
如有问题请联系系统管理员。
祝您使用愉快!`;
祝您使用愉快!`,
};
// 创建真实的文件Blob对象
const manualBlob = createWordDocumentContent(manualContent);
const specBlob = createWordDocumentContent(specContent);
const readmeBlob = createTextFile(readmeContent);
/**
* 创建Word文档的二进制内容
* @param content - 文档内容
* @returns Blob对象
*/
const createWordDocumentContent = (content: string): Blob => {
// 简化的Word文档XML结构
const wordContent = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<?mso-application progid="Word.Document"?>
<w:wordDocument xmlns:w="http://schemas.microsoft.com/office/word/2003/wordml">
<w:body>
<w:p>
<w:r>
<w:t>${content.replace(/\n/g, '</w:t></w:r></w:p><w:p><w:r><w:t>')}</w:t>
</w:r>
</w:p>
</w:body>
</w:wordDocument>`;
// 将文件添加到ZIP包
zip.file('产品说明书模板.docx', manualBlob);
zip.file('技术规格书模板.docx', specBlob);
zip.file('使用说明.txt', readmeBlob);
return new Blob([wordContent], {
type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
});
};
/**
* 创建文本文件内容
* @param content - 文本内容
* @returns Blob对象
*/
const createTextFile = (content: string): Blob => {
return new Blob([content], { type: 'text/plain;charset=utf-8' });
};
/**
* 打开对话框
*/
const openDialog = () => {
isShowDialog.value = true;
loading.value = false;
downloadLoading.value = false;
// 清空已上传的文件
nextTick(() => {
uploadRef.value?.clearFiles();
});
};
/**
* 处理对话框关闭
*/
const handleDialogClose = () => {
// 清空上传文件
uploadRef.value?.clearFiles();
};
/**
* 取消操作
*/
const handleCancel = () => {
isShowDialog.value = false;
};
/**
* 下载模板文件
*/
const handleDownloadTemplate = async () => {
downloadLoading.value = true;
try {
const zip = new JSZip();
// 添加模板文件到ZIP包
zip.file('产品说明书模板.docx', createWordDocumentContent(templateContents.manual));
zip.file('技术规格书模板.docx', createWordDocumentContent(templateContents.spec));
zip.file('使用说明.txt', createTextFile(templateContents.readme));
// 生成ZIP包
const zipBlob = await zip.generateAsync({
type: 'blob',
compression: 'DEFLATE',
compressionOptions: {
level: 6,
},
compressionOptions: { level: 6 },
});
// 重要:检查文件大小,确保不是空文件
// 检查文件大小
if (zipBlob.size === 0) {
throw new Error('生成的ZIP包为空,请检查文件内容');
throw new Error('生成的ZIP包为空');
}
// 下载文件
saveAs(zipBlob, `产品文档导入模板_${new Date().toISOString().split('T')[0]}.zip`);
const dateString = new Date().toISOString().split('T')[0];
saveAs(zipBlob, `产品文档导入模板_${dateString}.zip`);
ElMessage.success({
message: `模板下载成功!文件大小:${(zipBlob.size / 1024).toFixed(2)}KB`,
duration: 3000,
});
// 显示使用提示
setTimeout(() => {
ElMessage.info('下载完成后请解压ZIP包查看Word文档模板');
}, 1000);
ElMessage.success(`模板下载成功!文件大小:${(zipBlob.size / 1024).toFixed(2)}KB`);
} catch (error) {
console.error('模板下载失败:', error);
ElMessage.error({
message: '模板生成失败:' + (error instanceof Error ? error.message : '未知错误'),
duration: 5000,
});
// 提供备用方案
setTimeout(() => {
ElMessageBox.alert('如果模板下载仍有问题,请联系管理员获取手动模板文件。', '下载失败', {
confirmButtonText: '确定',
type: 'warning',
});
}, 1000);
ElMessage.error(`模板生成失败:${error instanceof Error ? error.message : '未知错误'}`);
} finally {
downloadLoading.value = false;
}
};
// 上传前校验
const beforeUpload = (file: File) => {
/**
* 上传前校验
* @param file - 上传的文件
* @returns 是否允许上传
*/
const beforeUpload = (file: File): boolean => {
const isZip = file.type === 'application/zip' || file.name.toLowerCase().endsWith('.zip');
const isLt50M = file.size / 1024 / 1024 < fileSizeLimit;
const isLtLimit = file.size / 1024 / 1024 < fileSizeLimit;
if (!isZip) {
ElMessage.error('请上传ZIP格式的压缩包文件!');
return false;
}
if (!isLt50M) {
ElMessage.error(`ZIP文件大小不能超过${fileSizeLimit}MB!`);
if (!isLtLimit) {
ElMessage.error(`文件大小不能超过${fileSizeLimit}MB!`);
return false;
}
ElMessage.info(`开始上传ZIP包: ${file.name} (${(file.size / 1024 / 1024).toFixed(2)}MB)`);
ElMessage.info(`开始上传: ${file.name} (${(file.size / 1024 / 1024).toFixed(2)}MB)`);
return true;
};
// 文件超出限制
/**
* 处理文件超出限制
*/
const handleExceed = () => {
ElMessage.warning('只能上传一个ZIP包文件,请先删除当前文件');
ElMessage.warning('只能上传一个文件,请先删除当前文件');
};
// 移除文件
/**
* 处理文件移除
*/
const handleRemove = () => {
// 文件移除时的处理
// 文件移除后的清理操作
};
// 上传成功
const handleSuccess = (response: any) => {
/**
* 处理上传成功
* @param response - 服务器响应
*/
const handleUploadSuccess = (response: any) => {
loading.value = false;
if (response.code === 200) {
ElMessage.success('Word文档导入成功');
ElMessage.success('文件导入成功');
emit('getRoleList');
isShowDialog.value = false;
} else {
ElMessage.error(response.msg || 'Word文档ZIP包导入失败');
ElMessage.error(response.msg || '文件导入失败');
}
};
// 上传失败
const handleError = (error: any) => {
/**
* 处理上传失败
* @param error - 错误信息
*/
const handleUploadError = (error: any) => {
loading.value = false;
ElMessage.error('ZIP包上传失败,请重试');
console.error('ZIP upload error:', error);
ElMessage.error('文件上传失败,请重试');
console.error('Upload error:', error);
};
// 手动提交上传
const submitUpload = () => {
if (!uploadRef.value || !uploadRef.value.uploadFiles.length) {
ElMessage.warning('请先选择要上传的ZIP包文件');
/**
* 手动提交上传
*/
const handleSubmitUpload = async () => {
if (!uploadRef.value?.uploadFiles?.length) {
ElMessage.warning('请先选择要上传的文件');
return;
}
ElMessageBox.confirm('确认导入选中的ZIP包吗', '导入确认', {
try {
await ElMessageBox.confirm('确认导入选中的文件吗?', '导入确认', {
confirmButtonText: '确认导入',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
});
loading.value = true;
uploadRef.value.submit();
})
.catch(() => {
// 用户取消
});
} catch {
// 用户取消操作
}
};
return {
isShowDialog,
uploadRef,
loading,
downloadLoading,
uploadAction,
uploadHeaders,
uploadData,
acceptFileTypes,
fileSizeLimit,
// 暴露方法给父组件
defineExpose({
openDialog,
downloadTemplate,
beforeUpload,
handleExceed,
handleRemove,
handleSuccess,
handleError,
submitUpload,
};
},
});
</script>
<style scoped>
<style scoped lang="scss">
.import-container {
padding: 10px 0;
}
.download-section {
text-align: center;
}
.template-tips {
margin-top: 10px;
}
.upload-demo {
width: 100%;
}
@@ -403,18 +402,37 @@ ___________________________________
margin-bottom: 16px;
}
.import-instructions .card-header {
font-weight: 600;
.import-instructions {
.mt20 {
margin-top: 20px;
}
}
.instruction-list {
margin: 0;
padding-left: 16px;
line-height: 1.8;
li {
margin-bottom: 4px;
}
}
.instruction-list li {
margin-bottom: 4px;
.dialog-footer {
display: flex;
justify-content: right;
align-items: center;
.download-btn {
background-color: coral;
border-color: coral;
width: 120px;
&:hover {
background-color: #ff7f50;
border-color: #ff7f50;
}
}
}
:deep(.el-upload-dragger) {

View File

@@ -1,13 +1,14 @@
<template>
<div class="system-role-container">
<el-card shadow="hover">
<!-- 搜索区域 -->
<div class="system-user-search mb15">
<el-form :inline="true">
<el-form-item label="产品名称">
<el-input size="default" v-model="tableData.param.roleName" placeholder="请输入产品名称" class="w-50 m-2" clearable />
<el-input size="default" v-model="tableData.param.productName" placeholder="请输入产品名称" class="w-50 m-2" clearable />
</el-form-item>
<el-form-item>
<el-button size="default" type="primary" class="ml10" @click="roleList">
<el-button size="default" type="primary" class="ml10" @click="handleSearch">
<el-icon>
<ele-Search />
</el-icon>
@@ -19,7 +20,7 @@
</el-icon>
新增产品
</el-button>
<!-- 添加导入导出按钮 -->
<!-- 导入导出按钮 -->
<el-button size="default" type="warning" class="ml10" @click="onOpenImport">
<el-icon>
<ele-Upload />
@@ -35,253 +36,349 @@
</el-form-item>
</el-form>
</div>
<el-table :data="tableData.data" style="width: 100%">
<!-- 产品表格 -->
<el-table :data="tableData.data" style="width: 100%" v-loading="tableData.loading">
<el-table-column type="index" label="序号" width="60" />
<el-table-column prop="name" label="产品名称" show-overflow-tooltip></el-table-column>
<el-table-column prop="remark" label="创建人" show-overflow-tooltip></el-table-column>
<el-table-column prop="status" label="修改人" show-overflow-tooltip></el-table-column>
<el-table-column prop="createdAt" label="创建时间" show-overflow-tooltip></el-table-column>
<el-table-column prop="createdAt" label="修改时间" show-overflow-tooltip></el-table-column>
<el-table-column prop="name" label="产品名称" show-overflow-tooltip />
<el-table-column prop="creator" label="创建人" show-overflow-tooltip />
<el-table-column prop="updater" label="修改人" show-overflow-tooltip />
<el-table-column prop="createdAt" label="创建时间" show-overflow-tooltip />
<el-table-column prop="updatedAt" label="修改时间" show-overflow-tooltip />
<el-table-column label="操作" width="220">
<template #default="scope">
<el-button size="small" text type="primary" @click="onOpenEditRole(scope.row)"
><el-icon><ele-EditPen /></el-icon>修改</el-button
>
<el-button size="small" text type="primary" @click="onRowDel(scope.row)"
><el-icon><ele-DeleteFilled /></el-icon>删除</el-button
>
<el-button size="small" text type="primary" @click="onOpenEditRole(scope.row)">
<el-icon><ele-EditPen /></el-icon>修改
</el-button>
<el-button size="small" text type="primary" @click="onRowDel(scope.row)">
<el-icon><ele-DeleteFilled /></el-icon>删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination
v-show="tableData.total > 0"
:total="tableData.total"
v-model:page="tableData.param.pageNum"
v-model:limit="tableData.param.pageSize"
@pagination="roleList"
@pagination="handlePaginationChange"
/>
</el-card>
<EditRole ref="editRoleRef" @getRoleList="roleList" />
<!-- 编辑对话框组件 -->
<EditRole ref="editRoleRef" @getRoleList="getProductList" />
<!-- 添加导入导出对话框 -->
<ImportDialog ref="importDialogRef" @getRoleList="roleList" />
<!-- 导入导出对话框组件 -->
<ImportDialog ref="importDialogRef" @getRoleList="getProductList" />
<ExportDialog ref="exportDialogRef" />
</div>
</template>
<script lang="ts">
import { toRefs, reactive, onMounted, ref, defineComponent, toRaw, getCurrentInstance } from 'vue';
<script lang="ts" setup>
import { ref, reactive, onMounted, nextTick } from 'vue';
import { ElMessageBox, ElMessage } from 'element-plus';
import EditRole from '/@/views/customerService/product/component/editRole.vue';
import { deleteRole, getRoleList } from '/@/api/system/role';
import ImportDialog from '/@/views/customerService/product/component/importDialog.vue';
import ExportDialog from '/@/views/customerService/product/component/exportDialog.vue';
import { deleteRole, getRoleList } from '/@/api/customerService/product';
// 定义接口来定义对象的类型
interface TableData {
/**
* 产品数据接口
*/
interface ProductData {
id: number;
status: number;
listOrder: number;
name: string;
creator: string; // 创建人
updater: string; // 修改人
remark: string;
dataScope: number;
createdAt: string;
}
interface TableDataState {
tableData: {
data: Array<TableData>;
total: number;
loading: boolean;
param: {
roleName: string;
roleStatus: string;
pageNum: number;
pageSize: number;
};
};
updatedAt: string;
}
const dateList = ref([
/**
* 查询参数接口
*/
interface QueryParam {
productName: string; // 产品名称
roleStatus: string; // 状态
pageNum: number; // 页码
pageSize: number; // 每页大小
}
/**
* 表格状态接口
*/
interface TableState {
data: ProductData[]; // 表格数据
total: number; // 总条数
loading: boolean; // 加载状态
param: QueryParam; // 查询参数
}
/**
* 模拟产品数据
*/
const mockProductData: ProductData[] = [
{
id: 1,
status: '李四',
status: 1,
listOrder: 0,
name: 'xxx药品',
remark: '张三',
creator: '张三',
updater: '李四',
remark: '药品描述',
dataScope: 3,
createdAt: '2022-04-01 11:38:39',
updatedAt: '2022-04-28 10:00:15',
},
{
id: 2,
status: '李四',
status: 1,
listOrder: 0,
name: 'xxx药品',
remark: '张三',
name: 'yyy医疗器械',
creator: '王五',
updater: '赵六',
remark: '医疗器械描述',
dataScope: 3,
createdAt: '2022-04-01 11:38:39',
updatedAt: '2022-04-28 10:01:34',
},
{
id: 3,
status: '李四',
status: 1,
listOrder: 0,
name: 'xxx药品',
remark: '张三',
name: 'zzz保健品',
creator: '孙七',
updater: '周八',
remark: '保健品描述',
dataScope: 3,
createdAt: '2022-04-01 11:38:39',
updatedAt: '2022-04-01 11:38:39',
},
{
id: 4,
status: '李四',
status: 1,
listOrder: 0,
name: 'xxx药品',
remark: '张三',
name: 'aaa化妆品',
creator: '吴九',
updater: '郑十',
remark: '化妆品描述',
dataScope: 3,
createdAt: '2022-04-01 11:38:39',
updatedAt: '2022-04-01 11:38:39',
},
{
id: 5,
status: '李四',
status: 1,
listOrder: 0,
name: 'xxx药品',
remark: '张三',
name: 'bbb药品',
creator: '钱一',
updater: '孙二',
remark: '药品描述',
dataScope: 2,
createdAt: '2022-04-01 11:38:39',
updatedAt: '2022-04-01 11:38:39',
},
{
id: 8,
status: '李四',
status: 1,
listOrder: 0,
name: 'xxx药品',
remark: '三',
name: 'ccc医疗器械',
creator: '三',
updater: '王四',
remark: '医疗器械描述',
dataScope: 2,
createdAt: '2022-04-01 11:38:39',
updatedAt: '2022-04-06 09:53:40',
},
]);
];
export default defineComponent({
name: 'apiV1SystemRoleList',
components: { EditRole, ImportDialog, ExportDialog },
setup() {
const { proxy } = getCurrentInstance() as any;
const addRoleRef = ref();
const editRoleRef = ref();
const importDialogRef = ref();
const exportDialogRef = ref();
const dataScopeRef = ref();
const state = reactive<TableDataState>({
tableData: {
// 响应式状态
const tableData = reactive<TableState>({
data: [],
total: 0,
loading: false,
param: {
roleName: '',
productName: '',
roleStatus: '',
pageNum: 1,
pageSize: 10,
},
},
});
// 初始化表格数据
const initTableData = () => {
roleList();
// 组件引用
const editRoleRef = ref<InstanceType<typeof EditRole>>();
const importDialogRef = ref<InstanceType<typeof ImportDialog>>();
const exportDialogRef = ref<InstanceType<typeof ExportDialog>>();
/**
* 获取产品列表
*/
const getProductList = async () => {
try {
tableData.loading = true;
// 实际API调用 - 注释掉模拟数据后取消注释
// const res = await getRoleList(tableData.param);
// if (res.data?.list) {
// tableData.data = res.data.list;
// tableData.total = res.data.total;
// }
// 模拟数据 - 添加延迟模拟网络请求
await new Promise((resolve) => setTimeout(resolve, 500));
// 根据产品名称过滤数据
const filteredData = mockProductData.filter((item) => !tableData.param.productName || item.name.includes(tableData.param.productName));
tableData.data = filteredData;
tableData.total = filteredData.length;
} catch (error) {
console.error('获取产品列表失败:', error);
ElMessage.error('获取产品列表失败');
} finally {
tableData.loading = false;
}
};
const roleList = () => {
const data: Array<TableData> = [];
getRoleList(state.tableData.param).then((res) => {
const list = res.data.list ?? [];
list.map((item: TableData) => {
data.push({
id: item.id,
status: item.status,
listOrder: item.listOrder,
name: item.name,
remark: item.remark,
dataScope: item.dataScope,
createdAt: item.createdAt,
});
});
state.tableData.data = dateList.value;
state.tableData.total = dateList.value.length;
});
/**
* 处理搜索
*/
const handleSearch = () => {
tableData.param.pageNum = 1; // 重置到第一页
getProductList();
};
// 打开新增角色弹窗
/**
* 处理分页变化
* @param pagination - 分页参数
*/
const handlePaginationChange = (pagination: { page: number; limit: number }) => {
tableData.param.pageNum = pagination.page;
tableData.param.pageSize = pagination.limit;
getProductList();
};
/**
* 打开新增产品对话框
*/
const onOpenAddRole = () => {
if (editRoleRef.value) {
editRoleRef.value.openDialog();
}
};
// 打开修改角色弹窗
const onOpenEditRole = (row: Object) => {
editRoleRef.value.openDialog(toRaw(row));
/**
* 打开编辑产品对话框
* @param row - 产品数据
*/
const onOpenEditRole = (row: ProductData) => {
if (editRoleRef.value) {
editRoleRef.value.openDialog(row);
}
};
// 打开导入弹窗
/**
* 打开导入对话框
*/
const onOpenImport = () => {
if (importDialogRef.value) {
importDialogRef.value.openDialog();
}
};
// 打开导出弹窗
/**
* 打开导出对话框
*/
const onOpenExport = () => {
exportDialogRef.value.openDialog(state.tableData.data);
if (exportDialogRef.value) {
exportDialogRef.value.openDialog(tableData.data);
}
};
// 删除角色
const onRowDel = (row: any) => {
ElMessageBox.confirm(`此操作将永久删除角色:"${row.name}",是否继续?`, '提示', {
/**
* 删除产品
* @param row - 产品数据
*/
const onRowDel = async (row: ProductData) => {
try {
await ElMessageBox.confirm(`此操作将永久删除产品:"${row.name}",是否继续?`, '提示', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
deleteRole(row.id).then(() => {
ElMessage.success('删除成功');
proxy.$refs['editRoleRef']?.resetMenuSession?.();
roleList();
});
})
.catch(() => {});
// 实际删除操作
// await deleteRole(row.id);
// 模拟删除成功
ElMessage.success('删除成功');
// 刷新列表
await getProductList();
} catch (error) {
// 用户取消操作,不处理
console.log('用户取消删除操作');
}
};
// 分页改变
const onHandleSizeChange = (val: number) => {
state.tableData.param.pageSize = val;
/**
* 初始化表格数据
*/
const initTableData = () => {
getProductList();
};
// 分页改变
const onHandleCurrentChange = (val: number) => {
state.tableData.param.pageNum = val;
};
// 页面加载时
// 生命周期 - 页面加载时初始化数据
onMounted(() => {
initTableData();
});
return {
addRoleRef,
editRoleRef,
importDialogRef,
exportDialogRef,
dataScopeRef,
onOpenAddRole,
onOpenEditRole,
onOpenImport,
onOpenExport,
onRowDel,
onHandleSizeChange,
onHandleCurrentChange,
roleList,
...toRefs(state),
};
},
});
</script>
<style scoped lang="scss">
.system-role-container {
padding: 20px;
}
.system-user-search {
margin-bottom: 15px;
:deep(.el-form-item) {
margin-bottom: 0;
}
}
.mb15 {
margin-bottom: 15px;
}
.ml10 {
margin-left: 10px;
}
.w-50 {
width: 240px;
}
// 表格操作按钮样式
:deep(.el-table) {
.el-button {
margin: 0 2px;
&.el-button--small {
padding: 6px 8px;
}
}
}
// 分页样式
:deep(.pagination-container) {
margin-top: 15px;
padding: 10px 0;
}
</style>

View File

@@ -5,9 +5,9 @@
<el-form :model="tableData.param" ref="queryRef" :inline="true" label-width="68px">
<el-form-item label="客服平台" prop="status" style="width: 200px">
<el-select v-model="tableData.param.status" placeholder="小红书" clearable size="default" style="width: 240px">
<el-option :label="'小红书'" :value="''" />
<el-option :label="'抖音'" :value="''" />
<el-option :label="'快手'" :value="''" />
<el-option label="小红书" value="xiaohongshu" />
<el-option label="抖音" value="douyin" />
<el-option label="快手" value="kuaishou" />
</el-select>
</el-form-item>
@@ -36,25 +36,30 @@
</el-icon>
重置
</el-button>
<el-button size="default" type="info" class="ml10" @click="handleExport">
<el-icon>
<ele-Download />
</el-icon>
导出
</el-button>
</el-form-item>
</el-form>
</div>
<el-table :data="tableData.data" style="width: 100%" @selection-change="handleSelectionChange">
<el-table :data="tableData.data" style="width: 100%">
<!-- <el-table-column type="selection" width="55" align="center" /> -->
<el-table-column label="日期" align="center" prop="loginTime" width="180" />
<el-table-column label="客服平台" align="center" prop="platform" />
<el-table-column label="客服ID" align="center" prop="infoId" />
<el-table-column label="客服姓名" align="center" prop="loginName" />
<el-table-column label="进线人数" align="center" prop="ipaddr" width="130" :show-overflow-tooltip="true" />
<el-table-column label="开口人数" align="center" prop="loginLocation" :s0ow-overflow-tooltip="true" />
<el-table-column label="开口人数" align="center" prop="loginLocation" :show-overflow-tooltip="true" />
<el-table-column label="留资卡发送数量" align="center" prop="browser" />
<el-table-column label="名片发送数" align="center" prop="os" />
<!-- <el-table-column label="留资人数" align="center" prop="status" :formatter="statusFormat" /> -->
<el-table-column label="留资人数" align="center" prop="status" />
<el-table-column label="接待人数" align="center" prop="msg" />
<el-table-column label="30秒回复率" alian="center" prop="module" />
<el-table-column label="60秒回复率" alian="center" prop="module" />
<el-table-column label="3分钟回复率" alian="center" prop="module" />
<el-table-column label="30秒回复率" align="center" prop="module" />
<el-table-column label="60秒回复率" align="center" prop="module" />
<el-table-column label="3分钟回复率" align="center" prop="module" />
</el-table>
<pagination
v-show="tableData.total > 0"
@@ -67,13 +72,11 @@
</div>
</template>
<script lang="ts">
import { toRefs, reactive, onMounted, ref, defineComponent, getCurrentInstance, unref } from 'vue';
<script lang="ts" setup>
import { ref, reactive, onMounted, nextTick } from 'vue';
import { ElMessageBox, ElMessage, FormInstance } from 'element-plus';
import { logList, deleteLog, clearLog } from '/@/api/system/monitor/loginLog';
import { log } from 'console';
// 定义接口来定义对象的类型
// 定义接口
interface TableDataRow {
infoId: number;
loginName: string;
@@ -85,14 +88,9 @@ interface TableDataRow {
msg: string;
loginTime: string;
module: string;
platform: string;
}
interface TableDataState {
ids: number[];
tableData: {
data: Array<TableDataRow>;
total: number;
loading: boolean;
param: {
interface TableDataParam {
pageNum: number;
pageSize: number;
dateRange: string[];
@@ -100,153 +98,62 @@ interface TableDataState {
ipaddr: string;
loginLocation: any;
userName: string;
};
};
}
export default defineComponent({
name: 'apiV1SystemLoginLogList',
setup() {
interface TableData {
data: Array<TableDataRow>;
total: number;
loading: boolean;
param: TableDataParam;
}
// 模拟数据
const dateList = ref([
const dateList: TableDataRow[] = [
{
infoId: 434569527,
loginName: 'demo',
ipaddr: '0',
loginLocation: '0',
browser: '0',
os: '0',
status: 0,
msg: '0',
ipaddr: '10',
loginLocation: '8',
browser: '5',
os: '3',
status: 2,
msg: '15',
loginTime: '2025-11-22',
module: '0',
module: '95%',
platform: '抖音',
},
{
infoId: 424569527,
loginName: 'demo',
ipaddr: '0',
loginLocation: '0',
browser: '0',
os: '0',
status: 0,
msg: '0',
ipaddr: '12',
loginLocation: '9',
browser: '6',
os: '4',
status: 3,
msg: '18',
loginTime: '2025-11-22',
module: '0',
module: '92%',
platform: '小红书',
},
{
infoId: 414569527,
loginName: 'demo',
ipaddr: '0',
loginLocation: '0',
browser: '0',
os: '0',
status: 0,
msg: '0',
ipaddr: '8',
loginLocation: '6',
browser: '4',
os: '2',
status: 1,
msg: '12',
loginTime: '2025-11-22',
module: '0',
module: '98%',
platform: '快手',
},
{
infoId: 404569527,
loginName: 'demo',
ipaddr: '0',
loginLocation: '0',
browser: '0',
os: '0',
status: 0,
msg: '0',
loginTime: '2025-11-22',
module: '0',
platform: '小红书',
},
{
infoId: 394569527,
loginName: 'demo',
ipaddr: '0',
loginLocation: '0',
browser: '0',
os: '0',
status: 0,
msg: '0',
loginTime: '2025-11-22',
module: '0',
platform: '小红书',
},
{
infoId: 384569527,
loginName: 'demo',
ipaddr: '0',
loginLocation: '0',
browser: '0',
os: '0',
status: 0,
msg: '0',
loginTime: '2025-11-22',
module: '0',
platform: '快手',
},
{
infoId: 374569527,
loginName: 'demo',
ipaddr: '0',
loginLocation: '0',
browser: '0',
os: '0',
status: 0,
msg: '0',
loginTime: '2025-11-22',
module: '0',
platform: '小红书',
},
{
infoId: 364569527,
loginName: 'demo',
ipaddr: '0',
loginLocation: '0',
browser: '0',
os: '0',
status: 0,
msg: '0',
loginTime: '2025-11-22',
module: '0',
platform: '抖音',
},
{
infoId: 354569527,
loginName: 'demo',
ipaddr: '0',
loginLocation: '0',
browser: '0',
os: '0',
status: 0,
msg: '0',
loginTime: '2025-11-22',
module: '0',
platform: '小红书',
},
{
infoId: 344569527,
loginName: 'demo',
ipaddr: '0',
loginLocation: '0',
browser: '0',
os: '0',
status: 0,
msg: '0',
loginTime: '2025-11-22',
module: '0',
platform: '小红书',
},
]);
];
const { proxy } = getCurrentInstance() as any;
const queryRef = ref();
const { admin_login_status } = proxy.useDict('admin_login_status');
const state = reactive<TableDataState>({
ids: [],
tableData: {
// 响应式数据
const queryRef = ref<FormInstance>();
const ids = ref<number[]>([]);
const tableData = reactive<TableData>({
data: [],
total: 0,
loading: false,
@@ -259,98 +166,84 @@ export default defineComponent({
loginLocation: '',
userName: '',
},
},
});
// 方法定义
// 获取数据列表
const dataList = async () => {
try {
tableData.loading = true;
// 实际API调用
// const res = await logList(tableData.param);
// tableData.data = res.data.list;
// tableData.total = res.data.total;
// 模拟数据 - 添加延迟避免DOM操作错误
await new Promise((resolve) => setTimeout(resolve, 100));
tableData.data = dateList;
tableData.total = dateList.length;
} catch (error) {
console.error('获取数据失败:', error);
ElMessage.error('获取数据失败');
} finally {
tableData.loading = false;
}
};
// 重置查询
const resetQuery = (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.resetFields();
tableData.param = {
pageNum: 1,
pageSize: 10,
dateRange: [],
status: '',
ipaddr: '',
loginLocation: '',
userName: '',
};
nextTick(() => {
dataList();
});
};
// 导出数据
const handleExport = () => {
ElMessage.info('导出功能开发中');
};
// 初始化表格数据
const initTableData = () => {
dataList();
};
const dataList = () => {
// logList(state.tableData.param).then((res: any) => {
// state.tableData.data = res.data.list;
// console.log(state.tableData.data, '11111');
// state.tableData.total = res.data.total;
// });
state.tableData.data = dateList.value;
console.log(state.tableData.data, '11111');
state.tableData.total = dateList.value.length;
};
// 删除日志
const onRowDel = (row: TableDataRow) => {
let msg = '你确定要删除所选数据?';
let ids: number[] = [];
if (row) {
msg = `此操作将永久删除:“${row.loginName}”,是否继续?`;
ids = [row.infoId];
4569527;
} else {
ids = state.ids;
}
if (ids.length === 0) {
ElMessage.error('请选择要删除的数据。');
return;
}
ElMessageBox.confirm(msg, '提示', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
deleteLog(ids).then(() => {
ElMessage.success('删除成功');
dataList();
});
})
.catch(() => {});
};
// 清空日志
const onRowClear = () => {
ElMessageBox.confirm('你确定要删除所选数据?', '提示', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
clearLog().then(() => {
ElMessage.success('清除成功');
dataList();
});
})
.catch(() => {});
};
// 页面加载时
// 生命周期
onMounted(() => {
// 使用 nextTick 确保DOM已渲染完成
nextTick(() => {
initTableData();
});
/** 重置按钮操作 */
const resetQuery = (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.resetFields();
dataList();
};
// 多选框选中数据
const handleSelectionChange = (selection: TableDataRow[]) => {
state.ids = selection.map((item) => item.infoId);
4569527;
};
// 登录状态字典翻译
const statusFormat = (row: TableDataRow) => {
return proxy.selectDictLabel(unref(admin_login_status), row.status);
};
return {
queryRef,
onRowDel,
dataList,
resetQuery,
handleSelectionChange,
statusFormat,
onRowClear,
admin_login_status,
...toRefs(state),
};
},
});
</script>
<style scoped>
.system-dic-container {
padding: 20px;
}
.system-user-search {
margin-bottom: 15px;
}
.mb15 {
margin-bottom: 15px;
}
.ml10 {
margin-left: 10px;
}
</style>

View File

@@ -5,103 +5,60 @@
<el-row :gutter="35">
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="标签" prop="name">
<el-input placeholder="小红书" clearable></el-input>
<el-input v-model="formData.name" placeholder="请输入标签名称" clearable />
</el-form-item>
</el-col>
<!-- <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="排序">
<el-input-number v-model="formData.listOrder" :min="0" controls-position="right" placeholder="请输入排序"
class="w100" />
</el-form-item>
</el-col> -->
<!-- <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="客服状态">
<el-switch v-model="formData.status" :active-value="1" :inactive-value="0" inline-prompt active-text="启"
inactive-text=""></el-switch>
</el-form-item>
</el-col> -->
<!-- 新增富文本编辑器 -->
<!-- 富文本编辑器 -->
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
<el-form-item label="产品详情" prop="content">
<Editor height="400px" placeholder="请输入产品详细描述..." />
<Editor v-model="formData.content" height="400px" placeholder="请输入产品详细描述..." />
</el-form-item>
</el-col>
<!-- <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
<el-form-item label="菜单权限">
<el-row :gutter="35">
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-checkbox v-model="menuExpand" @change="handleCheckedTreeExpand($event)">展开/折叠</el-checkbox>
<el-checkbox v-model="menuNodeAll" @change="handleCheckedTreeNodeAll($event)">全选/全不选</el-checkbox>
<el-checkbox v-model="menuCheckStrictly" @change="handleCheckedTreeConnect($event)">父子联动</el-checkbox>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
<el-tree :data="menuData" ref="menuRef" :props="menuProps" :default-checked-keys="formData.menuIds"
node-key="id" show-checkbox class="menu-data-tree tree-border"
:check-strictly="!menuCheckStrictly" />
</el-col>
</el-row>
</el-form-item>
</el-col> -->
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel" size="default"> </el-button>
<el-button type="primary" @click="onSubmit" size="default" :loading="loading">{{ formData.id === 0 ? '新 增' : '修改' }}</el-button>
<el-button type="primary" @click="onSubmit" size="default" :loading="loading">
{{ formData.id === 0 ? '新 增' : '修 改' }}
</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts">
import { reactive, toRefs, defineComponent, ref, getCurrentInstance, unref } from 'vue';
import { addRole, editRole, getRole, getRoleParams } from '/@/api/system/role';
import { ElMessage } from 'element-plus';
import { refreshBackEndControlRoutes } from '/@/router/backEnd';
// 引入富文本编辑器组件
<script lang="ts" setup>
import { ref, reactive, toRefs, nextTick } from 'vue';
import { ElMessage, type FormInstance, type FormRules } from 'element-plus';
import { addRole, editRole, getRole } from '/@/api/customerService/script';
import Editor from '/@/components/editor/index.vue';
// 定义接口来定义对象的类型
// 定义类型接口
interface MenuDataTree {
id: number;
pid: number;
title: string;
children?: MenuDataTree[];
}
interface DialogRow {
id: number;
name: string;
status: number;
listOrder: number;
remark: string;
menuIds: Array<number>;
}
interface RoleState {
loading: boolean;
isShowDialog: boolean;
formData: DialogRow;
menuData: Array<MenuDataTree>;
menuExpand: boolean;
menuNodeAll: boolean;
menuCheckStrictly: boolean;
menuProps: {
children: string;
label: string;
};
rules: object;
menuIds: number[];
content?: string;
}
export default defineComponent({
name: 'systemEditRole',
components: {
Editor, // 注册富文本组件
},
setup(props, { emit }) {
const { proxy } = getCurrentInstance() as any;
const formRef = ref<HTMLElement | null>(null);
const menuRef = ref();
const state = reactive<RoleState>({
// 定义事件
const emit = defineEmits<{
(e: 'getRoleList'): void;
}>();
// 响应式数据
const state = reactive({
loading: false,
isShowDialog: false,
formData: {
@@ -111,84 +68,112 @@ export default defineComponent({
listOrder: 0,
remark: '',
menuIds: [],
},
// 表单校验
rules: {
name: [{ required: true, message: '角色名称不能为空', trigger: 'blur' }],
},
menuData: [],
content: '',
} as DialogRow,
menuData: [] as MenuDataTree[],
menuExpand: false,
menuNodeAll: false,
menuCheckStrictly: false,
menuProps: {
});
// 表单验证规则
const rules: FormRules = {
name: [{ required: true, message: '标签名称不能为空', trigger: 'blur' }],
content: [{ required: true, message: '产品详情不能为空', trigger: 'blur' }],
};
// 模板引用
const formRef = ref<FormInstance>();
const menuRef = ref();
// 解构状态数据
const { loading, isShowDialog, formData, menuData, menuExpand, menuNodeAll, menuCheckStrictly } = toRefs(state);
// 菜单配置
const menuProps = {
children: 'children',
label: 'title',
},
});
// 打开弹窗
};
/**
* 打开对话框
* @param row - 可选的编辑数据
*/
const openDialog = (row?: DialogRow) => {
resetForm();
getMenuData();
if (row) {
getRole(row.id).then((res: any) => {
if (res.data.role) {
state.formData = res.data.role;
state.formData.menuIds = res.data.menuIds ?? [];
// 编辑模式:获取角色详情
getRole(row.id)
.then((res: any) => {
if (res.data?.role) {
state.formData = {
...res.data.role,
content: res.data.role.content || '',
menuIds: res.data.menuIds ?? [],
};
}
})
.catch((error) => {
console.error('获取角色详情失败:', error);
ElMessage.error('获取角色详情失败');
});
}
state.isShowDialog = true;
};
// 关闭弹窗
/**
* 关闭对话框
*/
const closeDialog = () => {
state.isShowDialog = false;
};
// 取消
/**
* 取消操作
*/
const onCancel = () => {
closeDialog();
};
// 新增
const onSubmit = () => {
const formWrap = unref(formRef) as any;
if (!formWrap) return;
formWrap.validate((valid: boolean) => {
if (valid) {
/**
* 提交表单
*/
const onSubmit = async () => {
if (!formRef.value) return;
try {
// 表单验证
const valid = await formRef.value.validate();
if (!valid) return;
state.loading = true;
state.formData.menuIds = getMenuAllCheckedKeys();
if (state.formData.id === 0) {
//添加
addRole(state.formData)
.then(() => {
ElMessage.success('角色添加成功');
closeDialog(); // 关闭弹窗
resetMenuSession();
emit('getRoleList');
})
.finally(() => {
state.loading = false;
});
// 新增模式
await addRole(state.formData);
ElMessage.success('添加成功');
} else {
//修改
editRole(state.formData)
.then(() => {
ElMessage.success('角色修改成功');
closeDialog(); // 关闭弹窗
resetMenuSession();
// 编辑模式
await editRole(state.formData);
ElMessage.success('修改成功');
}
// 关闭对话框并刷新列表
closeDialog();
emit('getRoleList');
})
.finally(() => {
} catch (error) {
console.error('提交失败:', error);
ElMessage.error('操作失败');
} finally {
state.loading = false;
});
}
}
});
};
// 获取菜单结构数据
const getMenuData = () => {
getRoleParams().then((res: any) => {
state.menuData = proxy.handleTree(res.data.menu, 'id', 'pid');
});
};
/**
* 重置表单
*/
const resetForm = () => {
state.menuCheckStrictly = false;
state.menuExpand = false;
@@ -200,69 +185,48 @@ export default defineComponent({
listOrder: 0,
remark: '',
menuIds: [],
};
};
/** 树权限(展开/折叠)*/
const handleCheckedTreeExpand = (value: any) => {
let treeList = state.menuData;
for (let i = 0; i < treeList.length; i++) {
menuRef.value.store.nodesMap[treeList[i].id].expanded = value;
}
content: '',
};
/** 树权限(全选/全不选) */
const handleCheckedTreeNodeAll = (value: any) => {
menuRef.value.setCheckedNodes(value ? state.menuData : []);
// 重置表单验证状态
nextTick(() => {
formRef.value?.clearValidate();
});
};
/** 树权限(父子联动) */
const handleCheckedTreeConnect = (value: any) => {
state.menuCheckStrictly = value ? true : false;
};
/** 所有菜单节点数据 */
function getMenuAllCheckedKeys() {
// 目前被选中的菜单节点
let checkedKeys = menuRef.value.getCheckedKeys();
// 半选中的菜单节点
let halfCheckedKeys = menuRef.value.getHalfCheckedKeys();
checkedKeys.unshift.apply(checkedKeys, halfCheckedKeys);
return checkedKeys;
}
// 重置菜单session
const resetMenuSession = () => {
refreshBackEndControlRoutes();
};
return {
// 暴露方法给父组件使用
defineExpose({
openDialog,
closeDialog,
onCancel,
onSubmit,
menuRef,
formRef,
handleCheckedTreeExpand,
handleCheckedTreeNodeAll,
handleCheckedTreeConnect,
resetMenuSession,
...toRefs(state),
};
},
});
</script>
<style scoped lang="scss">
.system-edit-role-container {
:deep(.el-dialog__body) {
padding: 20px;
}
.mb20 {
margin-bottom: 20px;
}
.dialog-footer {
display: flex;
justify-content: flex-end;
align-items: center;
}
}
.tree-border {
margin-top: 5px;
border: 1px solid #e5e6e7 !important;
border: 1px solid #e5e6e7;
border-radius: 4px;
}
.system-edit-role-container {
.menu-data-tree {
border: var(--el-input-border, var(--el-border-base));
border-radius: var(--el-input-border-radius, var(--el-border-radius-base));
padding: 5px;
}
}
</style>

View File

@@ -47,7 +47,7 @@
import { toRefs, reactive, onMounted, ref, defineComponent, toRaw, getCurrentInstance } from 'vue';
import { ElMessageBox, ElMessage } from 'element-plus';
import EditRole from '/@/views/customerService/script/component/editRole.vue';
import { deleteRole, getRoleList } from '/@/api/system/role';
import { deleteRole, getRoleList } from '/@/api/customerService/script';
// 定义接口来定义对象的类型
interface TableData {
id: number;

View File

@@ -0,0 +1,273 @@
<template>
<div class="system-edit-role-container">
<el-dialog :title="(formData.id === 0 ? '添加' : '修改') + '角色'" v-model="isShowDialog" width="769px">
<el-form ref="formRef" :model="formData" :rules="rules" size="default" label-width="90px">
<el-row :gutter="35">
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="角色名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入角色名称" clearable></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="排序">
<el-input-number v-model="formData.listOrder" :min="0" controls-position="right" placeholder="请输入排序" class="w100" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="角色状态">
<el-switch
v-model="formData.status"
:active-value="1"
:inactive-value="0"
inline-prompt
active-text=""
inactive-text=""
></el-switch>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
<el-form-item label="角色描述">
<el-input v-model="formData.remark" type="textarea" placeholder="请输入角色描述" maxlength="150"></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
<el-form-item label="菜单权限">
<el-row :gutter="35">
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-checkbox v-model="menuExpand" @change="handleCheckedTreeExpand($event)">展开/折叠</el-checkbox>
<el-checkbox v-model="menuNodeAll" @change="handleCheckedTreeNodeAll($event)">全选/全不选</el-checkbox>
<el-checkbox v-model="menuCheckStrictly" @change="handleCheckedTreeConnect($event)">父子联动</el-checkbox>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
<el-tree
:data="menuData"
ref="menuRef"
:props="menuProps"
:default-checked-keys="formData.menuIds"
node-key="id"
show-checkbox
class="menu-data-tree tree-border"
:check-strictly="!menuCheckStrictly"
/>
</el-col>
</el-row>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel" size="default"> </el-button>
<el-button type="primary" @click="onSubmit" size="default" :loading="loading">{{ formData.id === 0 ? '新 增' : '修 改' }}</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts">
import { reactive, toRefs, defineComponent, ref, getCurrentInstance, unref } from 'vue';
import { addRole, editRole, getRole, getRoleParams } from '/@/api/system/role';
import { ElMessage } from 'element-plus';
import { refreshBackEndControlRoutes } from '/@/router/backEnd';
// 定义接口来定义对象的类型
interface MenuDataTree {
id: number;
pid: number;
title: string;
children?: MenuDataTree[];
}
interface DialogRow {
id: number;
name: string;
status: number;
listOrder: number;
remark: string;
menuIds: Array<number>;
}
interface RoleState {
loading: boolean;
isShowDialog: boolean;
formData: DialogRow;
menuData: Array<MenuDataTree>;
menuExpand: boolean;
menuNodeAll: boolean;
menuCheckStrictly: boolean;
menuProps: {
children: string;
label: string;
};
rules: object;
}
export default defineComponent({
name: 'systemEditRole',
setup(props, { emit }) {
const { proxy } = getCurrentInstance() as any;
const formRef = ref<HTMLElement | null>(null);
const menuRef = ref();
const state = reactive<RoleState>({
loading: false,
isShowDialog: false,
formData: {
id: 0,
name: '',
status: 1,
listOrder: 0,
remark: '',
menuIds: [],
},
// 表单校验
rules: {
name: [{ required: true, message: '角色名称不能为空', trigger: 'blur' }],
},
menuData: [],
menuExpand: false,
menuNodeAll: false,
menuCheckStrictly: false,
menuProps: {
children: 'children',
label: 'title',
},
});
// 打开弹窗
const openDialog = (row?: DialogRow) => {
resetForm();
getMenuData();
if (row) {
getRole(row.id).then((res: any) => {
if (res.data.role) {
state.formData = res.data.role;
state.formData.menuIds = res.data.menuIds ?? [];
}
});
}
state.isShowDialog = true;
};
// 关闭弹窗
const closeDialog = () => {
state.isShowDialog = false;
};
// 取消
const onCancel = () => {
closeDialog();
};
// 新增
const onSubmit = () => {
const formWrap = unref(formRef) as any;
if (!formWrap) return;
formWrap.validate((valid: boolean) => {
if (valid) {
state.loading = true;
state.formData.menuIds = getMenuAllCheckedKeys();
if (state.formData.id === 0) {
//添加
addRole(state.formData)
.then(() => {
ElMessage.success('角色添加成功');
closeDialog(); // 关闭弹窗
resetMenuSession();
emit('getRoleList');
})
.finally(() => {
state.loading = false;
});
} else {
//修改
editRole(state.formData)
.then(() => {
ElMessage.success('角色修改成功');
closeDialog(); // 关闭弹窗
resetMenuSession();
emit('getRoleList');
})
.finally(() => {
state.loading = false;
});
}
}
});
};
// 获取菜单结构数据
const getMenuData = () => {
getRoleParams().then((res: any) => {
state.menuData = proxy.handleTree(res.data.menu, 'id', 'pid');
});
};
const resetForm = () => {
state.menuCheckStrictly = false;
state.menuExpand = false;
state.menuNodeAll = false;
state.formData = {
id: 0,
name: '',
status: 1,
listOrder: 0,
remark: '',
menuIds: [],
};
};
/** 树权限(展开/折叠)*/
const handleCheckedTreeExpand = (value: any) => {
let treeList = state.menuData;
for (let i = 0; i < treeList.length; i++) {
menuRef.value.store.nodesMap[treeList[i].id].expanded = value;
}
};
/** 树权限(全选/全不选) */
const handleCheckedTreeNodeAll = (value: any) => {
menuRef.value.setCheckedNodes(value ? state.menuData : []);
};
/** 树权限(父子联动) */
const handleCheckedTreeConnect = (value: any) => {
state.menuCheckStrictly = value ? true : false;
};
/** 所有菜单节点数据 */
function getMenuAllCheckedKeys() {
// 目前被选中的菜单节点
let checkedKeys = menuRef.value.getCheckedKeys();
// 半选中的菜单节点
let halfCheckedKeys = menuRef.value.getHalfCheckedKeys();
checkedKeys.unshift.apply(checkedKeys, halfCheckedKeys);
return checkedKeys;
}
// 重置菜单session
const resetMenuSession = () => {
refreshBackEndControlRoutes();
};
return {
openDialog,
closeDialog,
onCancel,
onSubmit,
menuRef,
formRef,
handleCheckedTreeExpand,
handleCheckedTreeNodeAll,
handleCheckedTreeConnect,
resetMenuSession,
...toRefs(state),
};
},
});
</script>
<style scoped lang="scss">
.tree-border {
margin-top: 5px;
border: 1px solid #e5e6e7 !important;
border-radius: 4px;
}
.system-edit-role-container {
.menu-data-tree {
border: var(--el-input-border, var(--el-border-base));
border-radius: var(--el-input-border-radius, var(--el-border-radius-base));
padding: 5px;
}
}
</style>

View File

@@ -42,8 +42,12 @@
<el-table-column prop="createdAt" label="创建时间" show-overflow-tooltip></el-table-column>
<el-table-column label="操作" width="220">
<template #default="scope">
<el-button size="small" text type="primary" @click="onOpenEditRole(scope.row)"><el-icon><ele-EditPen /></el-icon>修改</el-button>
<el-button size="small" text type="primary" @click="onRowDel(scope.row)"><el-icon><ele-DeleteFilled /></el-icon>删除</el-button>
<el-button size="small" text type="primary" @click="onOpenEditRole(scope.row)"
><el-icon><ele-EditPen /></el-icon>修改</el-button
>
<el-button size="small" text type="primary" @click="onRowDel(scope.row)"
><el-icon><ele-DeleteFilled /></el-icon>删除</el-button
>
</template>
</el-table-column>
</el-table>
@@ -63,7 +67,7 @@
import { toRefs, reactive, onMounted, ref, defineComponent, toRaw, getCurrentInstance } from 'vue';
import { ElMessageBox, ElMessage } from 'element-plus';
import EditRole from '/@/views/system/role/component/editRole.vue';
import {deleteRole, getRoleList} from "/@/api/system/role";
import { deleteRole, getRoleList } from '/@/api/system/role';
// 定义接口来定义对象的类型
interface TableData {
id: number;
@@ -111,12 +115,12 @@ export default defineComponent({
});
// 初始化表格数据
const initTableData = () => {
roleList()
roleList();
};
const roleList = () => {
const data: Array<TableData> = [];
getRoleList(state.tableData.param).then(res=>{
const list = res.data.list??[]
getRoleList(state.tableData.param).then((res) => {
const list = res.data.list ?? [];
list.map((item: TableData) => {
data.push({
id: item.id,
@@ -127,10 +131,10 @@ export default defineComponent({
dataScope: item.dataScope,
createdAt: item.createdAt,
});
})
});
state.tableData.data = data;
state.tableData.total = res.data.total;
})
});
};
// 打开新增角色弹窗
const onOpenAddRole = () => {
@@ -153,7 +157,7 @@ export default defineComponent({
ElMessage.success('删除成功');
proxy.$refs['editRoleRef'].resetMenuSession();
roleList();
})
});
})
.catch(() => {});
};