| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469 |
- <template>
- <div class="sub-account-form-container">
- <!-- 头部:返回按钮和标题 -->
- <div class="header-section">
- <el-button :icon="ArrowLeft" text @click="handleBack"> 返回 </el-button>
- </div>
- <!-- 表单内容 -->
- <div class="form-content">
- <el-form ref="subAccountFormRef" :model="subAccountForm" :rules="subAccountFormRules" label-width="120px">
- <!-- 基本信息 -->
- <div class="form-section">
- <h3 class="section-title">基本信息</h3>
- <el-form-item label="账号名称" prop="accountName">
- <el-input v-model="subAccountForm.accountName" placeholder="请输入" maxlength="50" clearable :disabled="isView" />
- </el-form-item>
- <el-form-item label="手机号" prop="phone">
- <el-input v-model="subAccountForm.phone" placeholder="请输入" maxlength="11" clearable :disabled="isView" />
- </el-form-item>
- </div>
- <!-- 权限 -->
- <div class="form-section">
- <h3 class="section-title">权限</h3>
- <el-form-item label="选择角色" prop="roleId">
- <el-select
- v-model="subAccountForm.roleId"
- placeholder="请选择角色"
- clearable
- style="width: 100%"
- :disabled="isView"
- @change="handleRoleChange"
- >
- <el-option
- v-for="role in roleList"
- :key="role.roleId || role.id"
- :label="role.roleName"
- :value="role.roleId || role.id"
- />
- </el-select>
- </el-form-item>
- <!-- 权限展示:与角色管理相同的树状图,仅禁用展示 -->
- <el-form-item label="权限" v-if="subAccountForm.roleId && permissionTreeData.length > 0">
- <div class="permission-tree-container">
- <el-tree
- ref="permissionTreeRef"
- :data="permissionTreeData"
- :props="treeProps"
- show-checkbox
- node-key="id"
- :default-expand-all="true"
- :default-checked-keys="defaultCheckedKeys"
- :expand-on-click-node="false"
- >
- <template #default="{ data }">
- <span class="tree-node-label">
- <span>{{ data.label }}</span>
- </span>
- </template>
- </el-tree>
- </div>
- </el-form-item>
- </div>
- </el-form>
- </div>
- <!-- 底部按钮 -->
- <div class="form-footer">
- <el-button @click="handleCancel">
- {{ isView ? "返回" : "取消" }}
- </el-button>
- <el-button v-if="!isView" type="primary" @click="handleSave" :loading="saveLoading"> 确定 </el-button>
- </div>
- </div>
- </template>
- <script setup lang="ts" name="subAccountCreate">
- import { ref, reactive, onMounted, computed, nextTick } from "vue";
- import { useRouter, useRoute } from "vue-router";
- import { ElMessage, type FormInstance, type FormRules } from "element-plus";
- import { ArrowLeft } from "@element-plus/icons-vue";
- import {
- getAllNormalRoles,
- getRolePermissionTable,
- type RolePermissionItem,
- type RoleItem,
- createAccountAndAssignRole,
- getSubAccountDetail,
- updateSubAccount
- } from "@/api/modules/accountRoleManagement";
- import { localGet } from "@/utils";
- // 路由
- const router = useRouter();
- const route = useRoute();
- // 判断是编辑、查看还是创建
- const accountId = computed(() => (route.params.id as string) || (route.query.id as string) || undefined);
- const isEdit = computed(() => !!accountId.value && route.query.type !== "view");
- const isView = computed(() => !!accountId.value && route.query.type === "view");
- // 表单引用
- const subAccountFormRef = ref<FormInstance>();
- // 表单数据
- const subAccountForm = reactive({
- accountName: "",
- phone: "",
- roleId: undefined as number | undefined,
- userId: undefined as number | undefined
- });
- // 表单验证规则
- const subAccountFormRules: FormRules = {
- accountName: [{ required: true, message: "请输入账号名称", trigger: "blur" }],
- phone: [
- { required: true, message: "请输入手机号", trigger: "blur" },
- { pattern: /^1[3-9]\d{9}$/, message: "请输入正确的手机号", trigger: "blur" }
- ],
- roleId: [{ required: true, message: "请选择角色", trigger: "change" }]
- };
- // 角色列表
- const roleList = ref<RoleItem[]>([]);
- // 树形组件配置(disabled 用于只读展示)
- const treeProps = {
- children: "children",
- label: "label",
- disabled: "disabled"
- };
- // 权限树数据(与角色管理同结构,节点全部 disabled)
- const permissionTreeData = ref<any[]>([]);
- const permissionTreeRef = ref<any>();
- const defaultCheckedKeys = ref<number[]>([]);
- // 保存加载状态
- const saveLoading = ref(false);
- // 加载角色列表
- const loadRoleList = async () => {
- try {
- const storeId = localGet("createdId") || "";
- const res = await getAllNormalRoles({ storeId });
- const code = typeof res.code === "string" ? parseInt(res.code) : res.code;
- if (code === 200) {
- // 处理角色数据,确保有 id 或 roleId 字段
- const roles = (res.data || []).map((role: any) => ({
- ...role,
- // 如果后端返回的是 roleId,也确保 id 字段存在
- id: role.roleId || role.id,
- // 如果后端返回的是 id,也确保 roleId 字段存在
- roleId: role.roleId || role.id
- }));
- roleList.value = roles;
- } else {
- ElMessage.error(res.msg || "获取角色列表失败");
- }
- } catch (error) {
- console.error("获取角色列表失败:", error);
- ElMessage.error("获取角色列表失败,请重试");
- }
- };
- // 将角色权限(仅已选中的)转成树结构,不包含未选中的节点
- const transformRolePermissionsToTree = (list: RolePermissionItem[]): { tree: any[]; allIds: number[] } => {
- const level1Map = new Map<string, any>();
- const level2Map = new Map<string, any>();
- const level3Map = new Map<string, any>();
- const allIds: number[] = [];
- let idCounter = 1;
- for (const item of list || []) {
- if (!item.level1Permission) continue;
- let level1 = level1Map.get(item.level1Permission);
- if (!level1) {
- level1 = { id: idCounter++, label: item.level1Permission, disabled: true, children: [] };
- allIds.push(level1.id);
- level1Map.set(item.level1Permission, level1);
- }
- if (item.level2Permission) {
- const k2 = `${item.level1Permission}-${item.level2Permission}`;
- let level2 = level2Map.get(k2);
- if (!level2) {
- level2 = { id: idCounter++, label: item.level2Permission, disabled: true, children: [] };
- allIds.push(level2.id);
- level2Map.set(k2, level2);
- level1.children.push(level2);
- }
- if (item.level3Permission?.trim()) {
- const names = item.level3Permission
- .split(/\s+/)
- .map(s => s.trim())
- .filter(Boolean);
- for (const name of names) {
- const k3 = `${k2}-${name}`;
- if (level3Map.has(k3)) continue;
- const level3 = { id: idCounter++, label: name, disabled: true };
- allIds.push(level3.id);
- level3Map.set(k3, level3);
- level2.children.push(level3);
- }
- }
- }
- }
- return { tree: Array.from(level1Map.values()), allIds };
- };
- // 加载当前角色权限:只展示已选中的权限树(不展示未选中的)
- const loadPermissionList = async () => {
- if (!subAccountForm.roleId) {
- permissionTreeData.value = [];
- defaultCheckedKeys.value = [];
- return;
- }
- try {
- const roleId = Number(subAccountForm.roleId);
- if (!roleId || isNaN(roleId)) {
- permissionTreeData.value = [];
- defaultCheckedKeys.value = [];
- return;
- }
- const res = await getRolePermissionTable({ roleId });
- const code = typeof res.code === "string" ? parseInt(res.code) : res.code;
- if (code === 200 && res.data) {
- const { tree, allIds } = transformRolePermissionsToTree(res.data);
- permissionTreeData.value = tree;
- defaultCheckedKeys.value = allIds;
- nextTick(() => {
- permissionTreeRef.value?.setCheckedKeys(allIds);
- });
- } else {
- ElMessage.error(res.msg || "获取权限列表失败");
- permissionTreeData.value = [];
- defaultCheckedKeys.value = [];
- }
- } catch (error) {
- console.error("获取权限列表失败:", error);
- ElMessage.error("获取权限列表失败,请重试");
- permissionTreeData.value = [];
- defaultCheckedKeys.value = [];
- }
- };
- // 初始化数据
- onMounted(() => {
- loadRoleList();
- if (isEdit.value || isView.value) {
- loadSubAccountData();
- }
- });
- // (编辑或查看时调用详情接口获取子账号数据)
- const loadSubAccountData = async () => {
- if (!accountId.value) return;
- try {
- const storeId = localGet("createdId") || localGet("geeker-user")?.userInfo?.storeId || "";
- const userId = route.query.userId;
- const roleIdNew = Number(route.query.roleId);
- const res = await getSubAccountDetail(Number(storeId), userId, roleIdNew);
- const code = typeof res.code === "string" ? parseInt(res.code) : res.code;
- if (code === 200 && res.data) {
- // 处理返回数据:可能是数组或对象
- let accountData: any;
- accountData = res.data;
- // 填充表单数据
- subAccountForm.accountName = accountData.accountName || "";
- subAccountForm.phone = accountData.phone || "";
- subAccountForm.roleId = accountData.roleId;
- subAccountForm.userId = accountData.userId || Number(accountId.value);
- // 按角色加载权限树勾选(权限由角色决定,仅展示)
- if (subAccountForm.roleId) {
- await loadPermissionList();
- }
- } else {
- ElMessage.error(res.msg || "获取子账号详情失败");
- }
- } catch (error) {
- console.error("获取子账号详情失败:", error);
- ElMessage.error("获取子账号详情失败,请重试");
- }
- };
- // 返回
- const handleBack = () => {
- router.back();
- };
- // 取消
- const handleCancel = () => {
- router.back();
- };
- // 保存
- const handleSave = async () => {
- if (!subAccountFormRef.value) return;
- await subAccountFormRef.value.validate(async valid => {
- if (!valid) return;
- saveLoading.value = true;
- try {
- const createdId = localGet("createdId") || localGet("geeker-user")?.userInfo?.storeId || "";
- const storeId = Number(createdId) || 0;
- if (isEdit.value) {
- // 编辑模式:调用更新接口
- if (!subAccountForm.userId) {
- ElMessage.error("缺少用户ID,无法更新");
- return;
- }
- const updateAccountDto = {
- accountName: subAccountForm.accountName,
- phone: subAccountForm.phone,
- roleId: route.query.roleId || 0,
- storeId: storeId,
- userId: route.query.userId,
- id: accountId.value
- };
- const res = await updateSubAccount(updateAccountDto);
- const code = typeof res.code === "string" ? parseInt(res.code) : res.code;
- if (code == 200) {
- ElMessage.success("编辑成功");
- setTimeout(() => {
- router.push("/accountRoleManagement/subAccountManagement");
- }, 1000);
- }
- } else {
- // 创建模式:调用创建接口
- const createAccountDto = {
- accountName: subAccountForm.accountName,
- phone: subAccountForm.phone,
- roleId: subAccountForm.roleId,
- storeId: storeId
- };
- const res = await createAccountAndAssignRole(createAccountDto);
- const code = typeof res.code === "string" ? parseInt(res.code) : res.code;
- if (code === 200) {
- ElMessage.success("创建成功后,员工可以使用手机号与验证码进行登录");
- setTimeout(() => {
- router.push("/accountRoleManagement/subAccountManagement");
- }, 1000);
- }
- }
- } catch (error) {
- console.error("保存失败:", error);
- } finally {
- saveLoading.value = false;
- }
- });
- };
- // 处理角色选择变化
- const handleRoleChange = (value: number | null | undefined) => {
- subAccountForm.roleId = value as number | undefined;
- permissionTreeData.value = [];
- defaultCheckedKeys.value = [];
- if (value) {
- loadPermissionList();
- } else {
- nextTick(() => permissionTreeRef.value?.setCheckedKeys([]));
- }
- };
- </script>
- <style lang="scss" scoped>
- .sub-account-form-container {
- min-height: calc(100vh - 84px);
- padding: 20px;
- background: #ffffff;
- }
- // 头部区域
- .header-section {
- padding-bottom: 16px;
- margin-bottom: 24px;
- border-bottom: 1px solid #e4e7ed;
- }
- // 表单内容
- .form-content {
- max-width: 800px;
- padding: 24px 0;
- .form-section {
- margin-bottom: 32px;
- .section-title {
- margin: 0 0 24px;
- font-size: 16px;
- font-weight: 600;
- color: var(--el-text-color-primary);
- }
- }
- :deep(.el-form-item) {
- margin-bottom: 24px;
- .el-form-item__label {
- font-weight: 500;
- color: var(--el-text-color-regular);
- }
- }
- // 权限树(与角色管理同款样式,仅禁用展示)
- .permission-tree-container {
- width: 100%;
- min-height: 200px;
- max-height: 500px;
- padding: 16px;
- overflow-y: auto;
- background: #f5f7fa;
- border: 1px solid #e4e7ed;
- border-radius: 4px;
- :deep(.el-tree) {
- background: transparent;
- .el-tree-node {
- .el-tree-node__content {
- height: 40px;
- padding: 0 12px;
- margin-bottom: 2px;
- border-radius: 4px;
- transition: background-color 0.2s;
- &:hover {
- background-color: #ecf5ff;
- }
- .tree-node-label {
- display: flex;
- flex: 1;
- align-items: center;
- font-size: 14px;
- color: var(--el-text-color-primary);
- }
- }
- .el-checkbox {
- margin-right: 8px;
- }
- .el-tree-node__children {
- padding-left: 20px;
- }
- }
- .el-tree-node__expand-icon {
- margin-right: 6px;
- font-size: 14px;
- color: #909399;
- transform: translateX(-5px);
- }
- .el-tree-node__expand-icon.is-leaf {
- display: none;
- }
- }
- }
- }
- // 底部按钮
- .form-footer {
- display: flex;
- gap: 12px;
- justify-content: center;
- padding: 24px 0;
- margin-top: 24px;
- border-top: 1px solid #e4e7ed;
- }
- </style>
|