Răsfoiți Sursa

feat:完善部分角色管理 人员管理页面

叶静 1 lună în urmă
părinte
comite
a7ab691288

+ 64 - 21
src/app/admin/views/auth/access/components/sa-access.vue

@@ -111,6 +111,7 @@
   import admin from '@/app/admin/api';
   import SaDraggable from 'vuedraggable';
   import { useModal } from '@/sheep/hooks';
+  import { menuRulesData } from '@/sheep/local-data/admin';
 
   import { isEmpty } from 'lodash';
   import AccessEdit from '../edit.vue';
@@ -143,27 +144,51 @@
   });
   watch(
     () => props.modelValue,
-    () => {
-      state.checkedIds = props.modelValue;
+    (newValue) => {
+      state.checkedIds = newValue || [];
+
+      // 当modelValue变化时,重新计算权限状态
+      if (state.app.length > 0) {
+        let appItem = initAppItem();
+        initCalculate(state.app, appItem);
+        calculateShow(appItem);
+      }
+    },
+    { immediate: true },
+  );
+
+  // 监听app数据变化,确保新增时也能显示
+  watch(
+    () => state.app,
+    (newApp) => {
+      if (newApp.length > 0) {
+        let appItem = initAppItem();
+        initCalculate(newApp, appItem);
+        calculateShow(appItem);
+      }
     },
   );
 
   async function getData() {
     state.loading = true;
-    const { error, data } = await admin.auth.access[props.type]({
-      parent_id: 0,
-      role_id: props.role_id,
-    });
-    if (error === 0) {
-      state.app = data;
-
-      // 初始化数据
-      let appItem = initAppItem();
 
-      initCalculate(state.app, appItem);
+    try {
+      // 使用本地menuRulesData数据
+      if (menuRulesData.error === 0) {
+        // 根据type类型处理数据
+        if (props.type === 'select') {
+          // 权限选择模式,使用完整的菜单数据
+          state.app = menuRulesData.data.menu || [];
+        } else {
+          // 其他模式(如list),也使用菜单数据
+          state.app = menuRulesData.data.menu || [];
+        }
 
-      // 是否选中
-      await calculateShow(appItem);
+        // 只加载数据,不立即计算权限状态
+        // 权限状态计算会在watch中处理
+      }
+    } catch (error) {
+      console.error('获取菜单数据失败:', error);
     }
 
     state.loading = false;
@@ -199,11 +224,30 @@
     data.forEach((item) => {
       item.parent = parent;
 
-      if (state.checkedIds.includes(item.id + '')) {
+      // 兼容数字和字符串ID的比较
+      const itemIdStr = String(item.id);
+      const itemIdNum = Number(item.id);
+      const isChecked = state.checkedIds.some((checkedId) => {
+        const checkedIdStr = String(checkedId);
+        const checkedIdNum = Number(checkedId);
+        return checkedIdStr === itemIdStr || checkedIdNum === itemIdNum;
+      });
+
+      if (isChecked) {
         if (!isEmpty(item.children)) {
           let arr = [];
           flattenData(item.children, arr);
-          if (arr.every((k) => state.checkedIds.includes(k.id + ''))) {
+          const allChildrenChecked = arr.every((k) => {
+            const kIdStr = String(k.id);
+            const kIdNum = Number(k.id);
+            return state.checkedIds.some((checkedId) => {
+              const checkedIdStr = String(checkedId);
+              const checkedIdNum = Number(checkedId);
+              return checkedIdStr === kIdStr || checkedIdNum === kIdNum;
+            });
+          });
+
+          if (allChildrenChecked) {
             item.checked = true;
             item.indeterminate = false;
           } else {
@@ -211,8 +255,8 @@
             item.indeterminate = true;
           }
         } else {
-          item.checked = state.checkedIds.includes(item.id + '');
-          item.indeterminate = !state.checkedIds.includes(item.id + '');
+          item.checked = true;
+          item.indeterminate = false;
         }
       } else {
         item.checked = false;
@@ -439,9 +483,8 @@
     });
   }
   onMounted(() => {
-    if (props.role_id) {
-      getData();
-    }
+    // 无论是新增还是编辑都需要加载权限数据
+    getData();
   });
 </script>
 <style lang="scss" scoped>

+ 80 - 65
src/app/admin/views/auth/admin/edit.vue

@@ -2,46 +2,34 @@
   <el-container>
     <el-main>
       <el-form :model="form.model" :rules="form.rules" ref="formRef" label-width="100px">
-        <el-form-item label="角色组" prop="role_id">
-          <el-cascader
-            v-model="form.model.role_id"
-            :options="role.select"
-            :props="{
-              label: 'name',
-              value: 'id',
-              checkStrictly: true,
-              emitPath: false,
-            }"
-            placeholder="请选择角色组"
-            clearable
-          ></el-cascader>
-        </el-form-item>
-        <el-form-item label="账号" prop="account">
-          <el-input v-model="form.model.account" placeholder="请输入账号"></el-input>
-        </el-form-item>
-        <el-form-item
-          label="密码"
-          v-if="modal.params"
-          :prop="modal.params.type == 'add' ? 'password' : ''"
-        >
-          <el-input v-model="form.model.password" placeholder="请输入密码"></el-input>
-        </el-form-item>
-        <!-- TODO√ 默认直接上传 operType  -->
-        <el-form-item label="头像" prop="avatar">
-          <sa-uploader v-model="form.model.avatar" fileType="image"></sa-uploader>
-        </el-form-item>
-        <el-form-item label="昵称" prop="nickname">
-          <el-input v-model="form.model.nickname" placeholder="请输入昵称"></el-input>
+        <el-form-item label="用户名" prop="username">
+          <el-input v-model="form.model.username" placeholder="请输入用户名"></el-input>
         </el-form-item>
-        <el-form-item label="手机号">
+        <el-form-item label="手机号" prop="mobile">
           <el-input v-model="form.model.mobile" placeholder="请输入手机号"></el-input>
         </el-form-item>
-        <el-form-item label="邮箱">
-          <el-input v-model="form.model.email" placeholder="请输入邮箱"></el-input>
+        <el-form-item label="授权角色" prop="role_id">
+          <el-select
+            v-model="form.model.role_id"
+            placeholder="请选择授权角色"
+            clearable
+            style="width: 100%"
+          >
+            <el-option
+              v-for="item in role.select"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id"
+            ></el-option>
+          </el-select>
         </el-form-item>
-        <el-form-item label="状态">
-          <el-switch v-model="form.model.status" active-value="normal" inactive-value="disabled">
-          </el-switch>
+        <el-form-item label="用户说明" prop="description">
+          <el-input
+            v-model="form.model.description"
+            type="textarea"
+            :rows="3"
+            placeholder="请输入用户说明"
+          ></el-input>
         </el-form-item>
       </el-form>
     </el-main>
@@ -65,8 +53,9 @@
 </template>
 <script setup>
   import { onMounted, reactive, ref, unref } from 'vue';
-  import admin from '@/app/admin/api';
   import { cloneDeep } from 'lodash';
+  import { adminMockData } from '@/sheep/mock/admin.js';
+  import { roleMockData } from '@/sheep/mock/role.js';
 
   const emit = defineEmits(['modalCallBack']);
   const props = defineProps({
@@ -79,44 +68,57 @@
   let formRef = ref(null);
   const form = reactive({
     model: {
-      role_id: null,
-      account: '',
-      avatar: '',
-      email: '',
-      nickname: '',
+      username: '',
       mobile: '',
-      password: '',
-      status: 'normal',
+      role_id: null,
+      description: '',
+      role_name: '', // 用于显示角色名称
     },
     rules: {
-      role_id: [{ required: true, message: '请选择角色组', trigger: 'blur' }],
-      account: [{ required: true, message: '请输入账号', trigger: 'blur' }],
-      password: [
-        {
-          required: true,
-          message: '请输入密码',
-          trigger: 'blur',
-        },
+      username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
+      mobile: [
+        { required: false, message: '请输入手机号', trigger: 'blur' },
+        { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' },
       ],
-      avatar: [{ required: true, message: '请上传头像', trigger: 'blur' }],
-      nickname: [{ required: true, message: '请选择昵称', trigger: 'blur' }],
+      role_id: [{ required: true, message: '请选择授权角色', trigger: 'change' }],
+      description: [{ required: false, message: '请输入用户说明', trigger: 'blur' }],
     },
   });
   const loading = ref(false);
+
   // 获取详情
   async function getDetail(id) {
     loading.value = true;
-    const { error, data } = await admin.auth.admin.detail(id);
-    error === 0 && (form.model = data);
+    try {
+      const result = adminMockData.getDetail(id);
+      if (result.error === 0) {
+        form.model = { ...result.data };
+      }
+    } catch (error) {
+      console.error('获取详情失败:', error);
+    }
     loading.value = false;
   }
-  // 角色组
+
+  // 角色选择
   const role = reactive({
     select: [],
   });
+
   async function getRoleSelect() {
-    const { data } = await admin.auth.role.select();
-    role.select = data;
+    try {
+      const result = roleMockData.getList();
+      if (result.error === 0) {
+        // 处理分页数据格式
+        const roleList = result.data.data || result.data || [];
+        role.select = roleList.map((item) => ({
+          id: item.id,
+          name: item.name,
+        }));
+      }
+    } catch (error) {
+      console.error('获取角色列表失败:', error);
+    }
   }
 
   // 表单关闭时提交
@@ -124,13 +126,26 @@
     // 表单验证
     unref(formRef).validate(async (valid) => {
       if (!valid) return;
+
       let submitForm = cloneDeep(form.model);
-      const { error } =
-        props.modal.params.type == 'add'
-          ? await admin.auth.admin.add(submitForm)
-          : await admin.auth.admin.edit(props.modal.params.id, submitForm);
-      if (error == 0) {
-        emit('modalCallBack', { event: 'confirm' });
+
+      // 根据选择的角色ID设置角色名称
+      const selectedRole = role.select.find((r) => r.id === submitForm.role_id);
+      if (selectedRole) {
+        submitForm.role_name = selectedRole.name;
+      }
+
+      try {
+        const result =
+          props.modal.params.type == 'add'
+            ? adminMockData.add(submitForm)
+            : adminMockData.edit(props.modal.params.id, submitForm);
+
+        if (result.error === 0) {
+          emit('modalCallBack', { event: 'confirm' });
+        }
+      } catch (error) {
+        console.error('提交失败:', error);
       }
     });
   }

+ 69 - 291
src/app/admin/views/auth/admin/index.vue

@@ -1,109 +1,52 @@
 <template>
-  <el-container class="panel-block">
+  <el-container class="admin-view panel-block">
     <el-header class="sa-header">
       <div class="sa-title sa-flex sa-row-between">
-        <div class="label sa-flex">
-          <span class="left">管理员列表</span>
-          <search-condition
-            :conditionLabel="filterParams.conditionLabel"
-            @deleteFilter="deleteFilter"
-          ></search-condition>
-        </div>
+        <div class="label sa-flex">人员管理</div>
         <div>
           <el-button class="sa-button-refresh" icon="RefreshRight" @click="getData()"></el-button>
-          <el-button class="sa-button-refresh" icon="Search" @click="openFilter"></el-button>
-          <el-button v-auth="'admin.auth.admin.add'" icon="Plus" type="primary" @click="addRow"
-            >新建</el-button
-          >
+          <el-button icon="Plus" type="primary" @click="addRow">新增人员</el-button>
         </div>
       </div>
     </el-header>
-    <el-main class="sa-p-0" v-loading="loading">
-      <el-table
-        class="sa-table"
-        height="100%"
-        :data="table.data"
-        @selection-change="changeSelection"
-        @sort-change="fieldFilter"
-        @row-dblclick="editRow"
-        stripe
-      >
-        <template #empty>
-          <sa-empty />
-        </template>
-        <el-table-column
-          :selectable="checkSelect"
-          type="selection"
-          width="48"
-          align="center"
-        ></el-table-column>
-        <el-table-column sortable="custom" prop="id" label="ID" min-width="100"> </el-table-column>
-        <el-table-column prop="account" label="账号" min-width="100">
-          <template #default="scope">
-            <div class="sa-table-line-1">
-              {{ scope.row.account || '-' }}
-            </div>
-          </template>
-        </el-table-column>
-        <el-table-column prop="avatar" label="头像" min-width="60">
-          <template #default="scope">
-            <sa-preview :url="scope.row.avatar" :size="32"></sa-preview>
-          </template>
-        </el-table-column>
-        <el-table-column prop="nickname" label="昵称" min-width="160">
-          <template #default="scope">
-            <div class="sa-table-line-1">
-              {{ scope.row.nickname || '-' }}
-            </div>
+    <el-main class="sa-p-0">
+      <div class="sa-table-wrap panel-block panel-block--bottom" v-loading="loading">
+        <el-table
+          height="100%"
+          class="sa-table"
+          :data="table.data"
+          @row-dblclick="editRow"
+          row-key="id"
+          stripe
+        >
+          <template #empty>
+            <sa-empty />
           </template>
-        </el-table-column>
-        <el-table-column prop="roles" label="角色组" min-width="160">
-          <template #default="scope">
-            {{ scope.row.role?.name || '-' }}
-          </template>
-        </el-table-column>
-        <el-table-column prop="mobile" label="手机号" min-width="110">
-          <template #default="scope">
-            <div class="sa-table-line-1">
+          <el-table-column prop="username" label="用户名" min-width="120">
+            <template #default="scope">
+              <span class="sa-table-line-1">{{ scope.row.username || '-' }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column prop="mobile" label="手机号" min-width="140" align="center">
+            <template #default="scope">
               {{ scope.row.mobile || '-' }}
-            </div>
-          </template>
-        </el-table-column>
-        <el-table-column prop="email" label="邮箱" min-width="180">
-          <template #default="scope">
-            <div class="sa-table-line-1">
-              {{ scope.row.email || '-' }}
-            </div>
-          </template>
-        </el-table-column>
-        <el-table-column prop="login_time" label="上次登录" min-width="160">
-          <template #default="scope">
-            <div class="login-time" v-if="scope.row.login_time">
-              {{ scope.row.login_time }}
-            </div>
-            <span v-if="!scope.row.login_time">-</span>
-            <el-tag v-if="scope.row.login_ip" type="info">
-              {{ scope.row.login_ip }}
-            </el-tag>
-          </template>
-        </el-table-column>
-        <el-table-column prop="status" label="状态" min-width="70">
-          <template #default="scope">
-            <el-tag :type="scope.row.status == 'normal' ? 'success' : 'info'">
-              {{ scope.row.status_text }}
-            </el-tag>
-          </template>
-        </el-table-column>
-        <el-table-column fixed="right" label="操作" min-width="120">
-          <template #default="scope">
-            <template v-if="account.id != scope.row.id">
-              <el-button
-                v-auth="'admin.auth.admin.detail'"
-                class="is-link"
-                type="primary"
-                @click="editRow(scope.row)"
-                >编辑</el-button
-              >
+            </template>
+          </el-table-column>
+          <el-table-column prop="role_name" label="授权角色" min-width="120" align="center">
+            <template #default="scope">
+              <el-tag type="info">
+                {{ scope.row.role_name || '-' }}
+              </el-tag>
+            </template>
+          </el-table-column>
+          <el-table-column prop="description" label="用户说明" min-width="150" align="center">
+            <template #default="scope">
+              {{ scope.row.description || '-' }}
+            </template>
+          </el-table-column>
+          <el-table-column fixed="right" label="操作" min-width="120">
+            <template #default="scope">
+              <el-button link type="primary" @click="editRow(scope.row)">编辑</el-button>
               <el-popconfirm
                 width="fit-content"
                 confirm-button-text="确认"
@@ -112,24 +55,15 @@
                 @confirm="deleteApi(scope.row.id)"
               >
                 <template #reference>
-                  <el-button v-auth="'admin.auth.admin.delete'" class="is-link" type="danger">
-                    删除
-                  </el-button>
+                  <el-button link type="danger">删除</el-button>
                 </template>
               </el-popconfirm>
             </template>
-          </template>
-        </el-table-column>
-      </el-table>
+          </el-table-column>
+        </el-table>
+      </div>
     </el-main>
     <sa-view-bar>
-      <template #left>
-        <sa-batch-handle
-          :batchHandleTools="batchHandleTools"
-          :selectedLeng="table.selected.length"
-          @batchHandle="batchHandle"
-        ></sa-batch-handle>
-      </template>
       <template #right>
         <sa-pagination :pageData="pageData" @updateFn="getData" />
       </template>
@@ -142,142 +76,40 @@
   };
 </script>
 <script setup>
-  import { onMounted, reactive, ref, unref, computed } from 'vue';
-  import admin from '@/app/admin/api';
-  import { ElMessageBox } from 'element-plus';
+  import { onMounted, reactive, ref } from 'vue';
   import { useModal, usePagination } from '@/sheep/hooks';
-  import { useSearch } from '@/sheep/components/sa-table/sa-search/useSearch';
-  import sheep from '@/sheep';
   import AdminEdit from './edit.vue';
-  import { composeFilter } from '@/sheep/utils';
-  import { cloneDeep } from 'lodash';
-
-  const accountStore = sheep.$store('account');
-  const account = computed(() => accountStore.info);
-
-  const filterParams = reactive({
-    tools: {
-      user: {
-        type: 'tinputprepend',
-        placeholder: '请输入查询内容',
-        field: 'user',
-        user: {
-          field: 'id',
-          value: '',
-        },
-        options: [
-          {
-            label: 'ID',
-            value: 'id',
-          },
-          {
-            label: '昵称',
-            value: 'nickname',
-          },
-          {
-            label: '手机号',
-            value: 'mobile',
-          },
-        ],
-      },
-      role_id: {
-        type: 'tcascader',
-        label: '角色组',
-        field: 'role_id',
-        value: '',
-        options: {
-          data: [],
-          props: {
-            children: 'children',
-            label: 'name',
-            value: 'id',
-            checkStrictly: true,
-            emitPath: false,
-          },
-        },
-      },
-      status: {
-        type: 'tselect',
-        label: '状态',
-        field: 'status',
-        value: '',
-        options: {
-          data: [
-            {
-              label: '正常',
-              value: 'normal',
-            },
-            {
-              label: '禁用',
-              value: 'disabled',
-            },
-          ],
-        },
-      },
-    },
-    data: {
-      user: { field: 'id', value: '' },
-      role_id: '',
-      status: '',
-    },
-    conditionLabel: {},
-  });
-  const { openFilter, deleteFilter } = useSearch({
-    filterParams,
-    getData,
-  });
+  import { adminMockData } from '@/sheep/mock/admin.js';
 
   const { pageData } = usePagination();
 
   // 表格状态
   const table = reactive({
     data: [],
-    order: '',
-    sort: '',
-    selected: [],
   });
   const loading = ref(true);
+
   // 获取数据
-  async function getData(page) {
+  async function getData() {
     loading.value = true;
-    if (page) pageData.page = page;
-    let tempSearch = cloneDeep(filterParams.data);
-    let search = composeFilter(tempSearch, {
-      nickname: 'like',
-      mobile: 'like',
-      role_id: {
-        spacer: ',',
-      },
-    });
-    const { error, data } = await admin.auth.admin.list({
-      page: pageData.page,
-      list_rows: pageData.list_rows,
-      ...search,
-      order: table.order,
-      sort: table.sort,
-    });
-    if (error === 0) {
-      table.data = data.data;
-      pageData.page = data.current_page;
-      pageData.list_rows = data.per_page;
-      pageData.total = data.total;
+    try {
+      const result = adminMockData.getList();
+      if (result.error === 0) {
+        // 确保table.data是数组格式
+        table.data = result.data.data || result.data || [];
+      }
+    } catch (error) {
+      console.error('获取人员列表失败:', error);
+      table.data = [];
     }
     loading.value = false;
   }
 
-  // 默认管理员自己不可操作
-  function checkSelect(row) {
-    if (row.role_id == unref(account).role_id) {
-      return false;
-    }
-    return true;
-  }
-
-  // open 打开之后操作类型
+  // 新增人员
   function addRow() {
     useModal(
       AdminEdit,
-      { title: '新', type: 'add' },
+      { title: '新增人员', type: 'add' },
       {
         confirm: () => {
           getData();
@@ -285,11 +117,13 @@
       },
     );
   }
+
+  // 编辑人员
   function editRow(row) {
     useModal(
       AdminEdit,
       {
-        title: '编辑',
+        title: '编辑人员',
         type: 'edit',
         id: row.id,
       },
@@ -300,76 +134,20 @@
       },
     );
   }
-  // 删除api 单独批量可以直接调用
-  async function deleteApi(id) {
-    await admin.auth.admin.delete(id);
-    getData();
-  }
 
-  const batchHandleTools = [
-    {
-      type: 'delete',
-      label: '删除',
-      auth: 'admin.auth.admin.delete',
-      class: 'danger',
-    },
-    {
-      type: 'normal',
-      label: '启用',
-      auth: 'admin.auth.admin.edit',
-      class: 'success',
-    },
-    {
-      type: 'disabled',
-      label: '禁用',
-      auth: 'admin.auth.admin.edit',
-      class: 'info',
-    },
-  ];
-  async function batchHandle(type) {
-    let ids = [];
-    table.selected.forEach((row) => {
-      ids.push(row.id);
-    });
-    switch (type) {
-      case 'delete':
-        ElMessageBox.confirm('此操作将删除, 是否继续?', '提示', {
-          confirmButtonText: '确定',
-          cancelButtonText: '取消',
-          type: 'warning',
-        }).then(() => {
-          deleteApi(ids.join(','));
-        });
-        break;
-      default:
-        await admin.auth.admin.edit(ids.join(','), {
-          status: type,
-        });
+  // 删除人员
+  async function deleteApi(id) {
+    try {
+      const result = adminMockData.delete(id);
+      if (result.error === 0) {
         getData();
+      }
+    } catch (error) {
+      console.error('删除失败:', error);
     }
   }
 
-  // table 字段排序
-  function fieldFilter({ prop, order }) {
-    table.order = order == 'ascending' ? 'asc' : 'desc';
-    table.sort = prop;
-    getData();
-  }
-  // table 批量选择
-  function changeSelection(row) {
-    table.selected = row;
-  }
-
-  // 角色组
-  const role = reactive({
-    select: [],
-  });
-  async function getRoles() {
-    const { data } = await admin.auth.role.select();
-    filterParams.tools.role_id.options.data = data;
-  }
   onMounted(async () => {
-    getRoles();
     await getData();
   });
 </script>

+ 63 - 66
src/app/admin/views/auth/role/edit.vue

@@ -2,43 +2,18 @@
   <el-container>
     <el-main>
       <el-form :model="form.model" :rules="form.rules" ref="formRef" label-width="100px">
-        <el-form-item label="所属上级" prop="parent_id">
-          <el-cascader
-            v-model="form.model.parent_id"
-            :options="role.select"
-            :props="{
-              label: 'name',
-              value: 'id',
-              checkStrictly: true,
-              emitPath: false,
-            }"
-            clearable
-            placeholder="请选择上级角色组"
-            :disabled="props.modal.params.type == 'edit'"
-            @change="isChangeParentId = true"
-          ></el-cascader>
-        </el-form-item>
         <el-form-item label="角色名称" prop="name">
           <el-input v-model="form.model.name" placeholder="请输入角色名称"></el-input>
         </el-form-item>
-        <el-form-item label="角色标识" prop="code">
-          <el-input v-model="form.model.code" placeholder="请输入角色标识"></el-input>
-        </el-form-item>
-        <el-form-item label="角色描述">
+        <el-form-item label="角色说明">
           <el-input
             type="textarea"
-            maxlength="40"
+            maxlength="200"
             show-word-limit
             v-model="form.model.description"
-            placeholder="请输入角色描述"
+            placeholder="请输入角色说明"
           ></el-input>
         </el-form-item>
-        <el-form-item label="状态">
-          <el-radio-group v-model="form.model.status">
-            <el-radio label="normal">启用</el-radio>
-            <el-radio label="hidden">禁用</el-radio>
-          </el-radio-group>
-        </el-form-item>
         <el-form-item label="权限信息">
           <sa-access
             type="select"
@@ -52,26 +27,15 @@
       </el-form>
     </el-main>
     <el-footer class="sa-footer--submit">
-      <el-button
-        v-if="modal.params.type == 'add'"
-        v-auth="'admin.auth.role.add'"
-        type="primary"
-        @click="confirm"
-        >保存</el-button
-      >
-      <el-button
-        v-if="modal.params.type == 'edit'"
-        v-auth="'admin.auth.role.edit'"
-        type="primary"
-        @click="confirm"
-        >更新</el-button
-      >
+      <el-button v-if="modal.params.type == 'add'" type="primary" @click="confirm">保存</el-button>
+      <el-button v-if="modal.params.type == 'edit'" type="primary" @click="confirm">更新</el-button>
     </el-footer>
   </el-container>
 </template>
 <script setup>
   import { reactive, ref, unref, onMounted } from 'vue';
-  import admin from '@/app/admin/api';
+  import { ElMessage } from 'element-plus';
+  import { roleMockData } from '@/sheep/mock/role';
   import SaAccess from '../access/components/sa-access.vue';
   import { cloneDeep } from 'lodash';
 
@@ -89,46 +53,80 @@
       parent_id: '',
       name: '',
       description: '',
-      code: '',
       status: 'normal',
       rules: [],
     },
     rules: {
-      parent_id: [{ required: true, message: '选择上级角色组', trigger: 'blur' }],
       name: [{ required: true, message: '请输入角色名称', trigger: 'blur' }],
-      code: [{ required: true, message: '请输入角色标识', trigger: 'blur' }],
     },
   });
 
+  const isChangeParentId = ref(false);
+
   // 获取详情
   async function getDetail(id) {
-    const { error, data } = await admin.auth.role.detail(id);
-    error === 0 && (form.model = data);
-  }
+    try {
+      const result = roleMockData.getDetail(id);
+      if (result.error === 0) {
+        form.model = { ...result.data };
 
-  // 所属上级列表
-  const role = reactive({
-    select: [],
-  });
-  // 获取所属上级数据
-  async function getRoleSelect() {
-    ({ data: role.select } = await admin.auth.role.select());
-  }
+        // 确保rules是数组格式,用于sa-access组件
+        if (!form.model.rules || !Array.isArray(form.model.rules)) {
+          form.model.rules = [];
+        }
 
-  const isChangeParentId = ref(false);
+        console.log('角色详情数据:', form.model);
+      } else {
+        ElMessage.error(result.msg);
+      }
+    } catch (error) {
+      console.error('获取详情失败:', error);
+      ElMessage.error('获取详情失败');
+    }
+  }
 
   // 表单关闭时提交
   function confirm() {
     // 表单验证
     unref(formRef).validate(async (valid) => {
       if (!valid) return;
-      let submitForm = cloneDeep(form.model);
-      const { error } =
-        props.modal.params.type == 'add'
-          ? await admin.auth.role.add(submitForm)
-          : await admin.auth.role.edit(props.modal.params.id, submitForm);
-      if (error == 0) {
-        emit('modalCallBack', { event: 'confirm' });
+
+      try {
+        let submitForm = cloneDeep(form.model);
+        console.log('提交的表单数据:', submitForm);
+
+        // 处理权限数据,从rules数组中提取权限信息
+        if (submitForm.rules && submitForm.rules.length > 0) {
+          const permissions = [];
+          const extractPermissions = (rules) => {
+            rules.forEach((rule) => {
+              if (rule.title && rule.checked) {
+                permissions.push(rule.title);
+              }
+              if (rule.children && rule.children.length > 0) {
+                extractPermissions(rule.children);
+              }
+            });
+          };
+          extractPermissions(submitForm.rules);
+          submitForm.permissions = permissions;
+          submitForm.permissions_text = permissions.join('、');
+        }
+
+        const result =
+          props.modal.params.type == 'add'
+            ? roleMockData.create(submitForm)
+            : roleMockData.update(props.modal.params.id, submitForm);
+
+        if (result.error === 0) {
+          ElMessage.success(props.modal.params.type == 'add' ? '创建成功' : '更新成功');
+          emit('modalCallBack', { event: 'confirm' });
+        } else {
+          ElMessage.error(result.msg);
+        }
+      } catch (error) {
+        console.error('提交失败:', error);
+        ElMessage.error('提交失败');
       }
     });
   }
@@ -137,7 +135,6 @@
     if (props.modal.params.type == 'edit') {
       await getDetail(props.modal.params.id);
     }
-    await getRoleSelect();
   });
 </script>
 <style lang="scss" scoped>

+ 146 - 171
src/app/admin/views/auth/role/index.vue

@@ -1,200 +1,148 @@
 <template>
-  <el-container class="panel-block">
+  <el-container class="role-view panel-block">
     <el-header class="sa-header">
       <div class="sa-title sa-flex sa-row-between">
-        <span class="label sa-flex">
-          <span class="left">角色列表</span>
-        </span>
+        <div class="label sa-flex">角色列表</div>
         <div>
-          <el-button class="sa-button-refresh" icon="RefreshRight" @click="getData"></el-button>
-          <el-button v-auth="'admin.auth.role.add'" icon="Plus" type="primary" @click="addRow">
-            新建
-          </el-button>
+          <el-button class="sa-button-refresh" icon="RefreshRight" @click="getData()"></el-button>
+          <el-button icon="Plus" type="primary" @click="addRow">新建角色</el-button>
         </div>
       </div>
     </el-header>
-    <el-main class="sa-tree-table sa-p-0" v-loading="loading">
-      <div v-if="tree.data.length > 0" class="sa-tree-table-wrap">
-        <div class="sa-tree-table-header sa-flex">
-          <div class="sa-flex sa-row-between sa-flex-1">
-            <template v-for="tc in treeColumn" :key="tc">
-              <span class="item" :style="{ 'min-width': tc.width, width: tc.width }">
-                {{ tc.label }}
-              </span>
+    <el-main class="sa-p-0">
+      <div class="sa-table-wrap panel-block panel-block--bottom" v-loading="loading">
+        <el-table
+          height="100%"
+          class="sa-table"
+          :data="table.data"
+          @sort-change="fieldFilter"
+          @row-dblclick="editRow"
+          row-key="id"
+          stripe
+        >
+          <template #empty>
+            <sa-empty />
+          </template>
+          <el-table-column prop="name" label="角色名称" min-width="150" sortable="custom">
+            <template #default="scope">
+              <span class="sa-table-line-1">{{ scope.row.name || '-' }}</span>
             </template>
-          </div>
-        </div>
-        <div class="sa-tree-table-content sa-flex">
-          <el-tree
-            :data="tree.data"
-            node-key="id"
-            default-expand-all
-            :expand-on-click-node="false"
-            :props="{ class: customNodeClass }"
-            :indent="0"
-            empty-text="无数据,请去添加吧"
-          >
-            <template #default="{ node, data }">
-              <div class="sa-flex custom-tree-node">
-                <template v-for="tc in treeColumn" :key="tc">
-                  <template v-if="tc.type != 'oper'" class="sa-flex">
-                    <!-- expanded -->
-                    <span
-                      v-if="tc.type == 'expanded'"
-                      class="item sa-line-1"
-                      :style="{
-                        'padding-left': 10 + 20 * (node.level - 1) + 'px',
-                        'min-width': tc.width,
-                      }"
-                    >
-                      <span class="tree-arrow">
-                        <el-icon
-                          v-if="data.children?.length > 0"
-                          @click="() => isexpanded(node)"
-                          :class="node.expanded ? 'arrow-open' : 'arrow-close'"
-                        >
-                          <ArrowRight />
-                        </el-icon>
-                      </span>
-                      {{ data[tc.field] }}
-                    </span>
-                    <!-- 状态 -->
-                    <span
-                      v-else-if="tc.type == 'status'"
-                      class="item"
-                      :style="{ 'min-width': tc.width, width: tc.width }"
-                    >
-                      <el-tag :type="data.status == 'normal' ? 'success' : 'info'">
-                        {{ data.status_text }}
-                      </el-tag>
-                    </span>
-                    <!-- text形式的数据 -->
-                    <span
-                      v-else
-                      class="item item-text sa-line-1"
-                      :style="{ 'min-width': tc.width, width: tc.width }"
-                    >
-                      {{ data[tc.field] || '-' }}
-                    </span>
-                  </template>
-                  <span
-                    v-else
-                    class="item"
-                    :style="{
-                      'min-width': tc.width,
-                      width: tc.width,
-                    }"
-                  >
-                    <template v-if="account.role_id != data.id">
-                      <el-button
-                        v-auth="'admin.auth.role.detail'"
-                        class="is-link"
-                        type="primary"
-                        @click="editRow(data)"
-                        >编辑</el-button
-                      >
-                      <el-popconfirm
-                        width="fit-content"
-                        confirm-button-text="确认"
-                        cancel-button-text="取消"
-                        title="确认删除这条记录?"
-                        @confirm="deleteRow(data)"
-                      >
-                        <template #reference>
-                          <el-button
-                            v-auth="'admin.auth.role.delete'"
-                            class="is-link"
-                            type="danger"
-                          >
-                            删除
-                          </el-button>
-                        </template>
-                      </el-popconfirm>
-                    </template>
-                  </span>
+          </el-table-column>
+          <el-table-column label="授权功能" min-width="300">
+            <template #default="scope">
+              <span class="sa-table-line-2">{{ scope.row.permissions_text || '-' }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column label="角色备注" min-width="200">
+            <template #default="scope">
+              <span class="sa-table-line-1">{{ scope.row.remark || '-' }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column label="状态" min-width="100" align="center">
+            <template #default="scope">
+              <el-tag :type="getStatusType(scope.row.status)">
+                {{ scope.row.status_text || '-' }}
+              </el-tag>
+            </template>
+          </el-table-column>
+          <el-table-column prop="create_time" label="创建时间" min-width="160" sortable="custom">
+            <template #default="scope">
+              {{ scope.row.create_time || '-' }}
+            </template>
+          </el-table-column>
+          <el-table-column fixed="right" label="操作" min-width="120">
+            <template #default="scope">
+              <el-button link type="primary" @click="editRow(scope.row)">编辑</el-button>
+              <el-popconfirm
+                width="fit-content"
+                confirm-button-text="确认"
+                cancel-button-text="取消"
+                title="确认删除这条记录?"
+                @confirm="deleteRow(scope.row)"
+              >
+                <template #reference>
+                  <el-button link type="danger">删除</el-button>
                 </template>
-              </div>
+              </el-popconfirm>
             </template>
-          </el-tree>
-        </div>
+          </el-table-column>
+        </el-table>
       </div>
-      <sa-empty v-if="tree.data.length == 0 && !loading"></sa-empty>
     </el-main>
+    <sa-view-bar>
+      <template #right>
+        <sa-pagination :pageData="pageData" @updateFn="getData" />
+      </template>
+    </sa-view-bar>
   </el-container>
 </template>
 
 <script setup>
-  import { onMounted, reactive, ref, computed } from 'vue';
-  import admin from '@/app/admin/api';
-  import { useModal } from '@/sheep/hooks';
-  import sheep from '@/sheep';
+  import { onMounted, reactive, ref } from 'vue';
+  import { ElMessage } from 'element-plus';
+  import { useModal, usePagination } from '@/sheep/hooks';
+  import { roleMockData } from '@/sheep/mock/role';
   import RoleEdit from './edit.vue';
+  const { pageData } = usePagination();
 
-  const accountStore = sheep.$store('account');
-  const account = computed(() => accountStore.info);
-
-  const treeColumn = [
-    { label: 'ID', field: 'id', width: '100px' },
-    { type: 'expanded', label: '角色名称', field: 'name', width: '300px' },
-    { label: '标识', field: 'code', width: '100px' },
-    {
-      label: '描述',
-      field: 'description',
-      width: '100px',
-    },
-    {
-      type: 'status',
-      label: '状态',
-      field: 'status',
-      width: '80px',
-    },
-    {
-      label: '创建时间',
-      field: 'create_time',
-      width: '172px',
-    },
-    {
-      label: '更新时间',
-      field: 'update_time',
-      width: '172px',
-    },
-    {
-      type: 'oper',
-      label: '操作',
-      width: '120px',
-    },
-  ];
-
-  const tree = reactive({
+  // 列表
+  const table = reactive({
     data: [],
+    order: '',
+    sort: '',
   });
-  const loading = ref(null);
-  async function getData() {
-    loading.value = true;
-    const { error, data } = await admin.auth.role.list();
-    error === 0 && (tree.data = data);
-    loading.value = false;
-  }
+  const loading = ref(true);
 
-  // 折叠展开子集
-  function isexpanded(data) {
-    data.expanded = !data.expanded;
+  // 获取状态类型
+  function getStatusType(status) {
+    const statusMap = {
+      normal: 'success',
+      disabled: 'danger',
+    };
+    return statusMap[status] || 'info';
   }
 
-  function customNodeClass(data, node) {
-    let cls = null;
-    // 斑马线
-    if (node.id % 2 == 0) {
-      cls = 'is-striped';
+  // 获取数据
+  async function getData(page, searchParams = {}) {
+    if (page) pageData.page = page;
+    loading.value = true;
+
+    try {
+      let params = {
+        page: pageData.page,
+        list_rows: pageData.list_rows,
+        order: table.order,
+        sort: table.sort,
+        ...searchParams,
+      };
+
+      const result = roleMockData.getList(params);
+      if (result.error === 0) {
+        table.data = result.data.data;
+        pageData.page = result.data.current_page;
+        pageData.list_rows = result.data.per_page;
+        pageData.total = result.data.total;
+      }
+    } catch (error) {
+      console.error('获取数据失败:', error);
+      ElMessage.error('获取数据失败');
+    } finally {
+      loading.value = false;
     }
-    return cls;
   }
 
-  // dialog 添加 编辑 删除
+  // table 字段排序
+  function fieldFilter({ prop, order }) {
+    table.order = order == 'ascending' ? 'asc' : 'desc';
+    table.sort = prop;
+    getData();
+  }
+
+  // 新建角色
   function addRow() {
     useModal(
       RoleEdit,
-      { title: '新建', type: 'add' },
+      { title: '新建角色', type: 'add' },
       {
         confirm: () => {
           getData();
@@ -202,11 +150,13 @@
       },
     );
   }
+
+  // 编辑角色
   function editRow(row) {
     useModal(
       RoleEdit,
       {
-        title: '编辑',
+        title: '编辑角色',
         type: 'edit',
         id: row.id,
       },
@@ -217,12 +167,37 @@
       },
     );
   }
+
+  // 删除角色
   async function deleteRow(row) {
-    await admin.auth.role.delete(row.id);
-    getData();
+    try {
+      const result = roleMockData.delete(row.id);
+      if (result.error === 0) {
+        ElMessage.success('删除成功');
+        getData();
+      } else {
+        ElMessage.error(result.msg);
+      }
+    } catch (error) {
+      console.error('删除失败:', error);
+      ElMessage.error('删除失败');
+    }
   }
 
   onMounted(() => {
     getData();
   });
 </script>
+
+<style lang="scss" scoped>
+  .role-view {
+    .el-header {
+      height: auto;
+    }
+    .el-main {
+      .sa-table-wrap {
+        height: 100%;
+      }
+    }
+  }
+</style>

+ 3 - 0
src/sheep/local-data/admin.js

@@ -444,6 +444,9 @@ const menuRulesData = {
   },
 };
 
+// 导出menuRulesData供其他组件使用
+export { menuRulesData };
+
 export default {
   /**
    * 系统初始化接口

+ 246 - 0
src/sheep/mock/admin.js

@@ -0,0 +1,246 @@
+// 人员管理mock数据
+export const adminMockData = {
+  // 人员列表
+  list: [
+    {
+      id: 1,
+      username: 'USER1',
+      mobile: '13800008888',
+      role_name: '管理员',
+      description: '系统管理员',
+      role_id: 1,
+      status: 'normal',
+      status_text: '正常',
+      create_time: '2025-01-10 10:00:00',
+      update_time: '2025-01-15 14:30:00',
+    },
+    {
+      id: 2,
+      username: 'USER2',
+      mobile: '13800008888',
+      role_name: '店长',
+      description: '系统管理员',
+      role_id: 2,
+      status: 'normal',
+      status_text: '正常',
+      create_time: '2025-01-10 11:00:00',
+      update_time: '2025-01-14 16:20:00',
+    },
+    {
+      id: 3,
+      username: 'USER3',
+      mobile: '13800008888',
+      role_name: '财务',
+      description: '高级管理员',
+      role_id: 3,
+      status: 'normal',
+      status_text: '正常',
+      create_time: '2025-01-11 09:30:00',
+      update_time: '2025-01-13 11:45:00',
+    },
+    {
+      id: 4,
+      username: 'USER4',
+      mobile: '13800008888',
+      role_name: '客服',
+      description: '系统管理员',
+      role_id: 4,
+      status: 'normal',
+      status_text: '正常',
+      create_time: '2025-01-11 14:15:00',
+      update_time: '2025-01-12 09:20:00',
+    },
+    {
+      id: 5,
+      username: 'USER5',
+      mobile: '13800008888',
+      role_name: '运营',
+      description: '系统管理员',
+      role_id: 5,
+      status: 'normal',
+      status_text: '正常',
+      create_time: '2025-01-12 08:45:00',
+      update_time: '2025-01-15 13:10:00',
+    },
+    {
+      id: 6,
+      username: 'USER6',
+      mobile: '13800008888',
+      role_name: '市场',
+      description: '高级管理员',
+      role_id: 6,
+      status: 'normal',
+      status_text: '正常',
+      create_time: '2025-01-12 10:15:00',
+      update_time: '2025-01-13 16:20:00',
+    },
+    {
+      id: 7,
+      username: 'USER7',
+      mobile: '13800008888',
+      role_name: '编辑1',
+      description: '高级管理员',
+      role_id: 7,
+      status: 'normal',
+      status_text: '正常',
+      create_time: '2025-01-12 15:30:00',
+      update_time: '2025-01-14 10:45:00',
+    },
+    {
+      id: 8,
+      username: 'USER8',
+      mobile: '13800008888',
+      role_name: '编辑2',
+      description: '系统管理员',
+      role_id: 8,
+      status: 'normal',
+      status_text: '正常',
+      create_time: '2025-01-13 08:45:00',
+      update_time: '2025-01-15 12:10:00',
+    },
+  ],
+
+  // 获取列表数据
+  getList(params = {}) {
+    const { page = 1, list_rows = 10, username = '', mobile = '', role_name = '' } = params;
+
+    let filteredData = this.list;
+
+    // 根据用户名筛选
+    if (username) {
+      filteredData = filteredData.filter((item) =>
+        item.username.toLowerCase().includes(username.toLowerCase()),
+      );
+    }
+
+    // 根据手机号筛选
+    if (mobile) {
+      filteredData = filteredData.filter((item) => item.mobile.includes(mobile));
+    }
+
+    // 根据角色筛选
+    if (role_name) {
+      filteredData = filteredData.filter((item) => item.role_name.includes(role_name));
+    }
+
+    // 简化返回,直接返回数组数据
+    return {
+      error: 0,
+      msg: '获取成功',
+      data: filteredData,
+    };
+  },
+
+  // 获取详情
+  getDetail(id) {
+    const item = this.list.find((item) => item.id == id);
+    if (item) {
+      return {
+        error: 0,
+        msg: '获取成功',
+        data: item,
+      };
+    } else {
+      return {
+        error: 1,
+        msg: '人员不存在',
+        data: null,
+      };
+    }
+  },
+
+  // 新增人员
+  add(data) {
+    const newId = Math.max(...this.list.map((item) => item.id)) + 1;
+    const newItem = {
+      id: newId,
+      username: data.username,
+      mobile: data.mobile,
+      role_name: data.role_name || '普通用户',
+      description: data.description || '',
+      role_id: data.role_id || 1,
+      status: 'normal',
+      status_text: '正常',
+      create_time: new Date().toLocaleString('zh-CN'),
+      update_time: new Date().toLocaleString('zh-CN'),
+    };
+
+    this.list.push(newItem);
+
+    return {
+      error: 0,
+      msg: '新增成功',
+      data: newItem,
+    };
+  },
+
+  // 编辑人员
+  edit(id, data) {
+    const index = this.list.findIndex((item) => item.id == id);
+    if (index !== -1) {
+      this.list[index] = {
+        ...this.list[index],
+        ...data,
+        update_time: new Date().toLocaleString('zh-CN'),
+      };
+
+      return {
+        error: 0,
+        msg: '编辑成功',
+        data: this.list[index],
+      };
+    } else {
+      return {
+        error: 1,
+        msg: '人员不存在',
+        data: null,
+      };
+    }
+  },
+
+  // 删除人员
+  delete(id) {
+    const index = this.list.findIndex((item) => item.id == id);
+    if (index !== -1) {
+      this.list.splice(index, 1);
+      return {
+        error: 0,
+        msg: '删除成功',
+        data: null,
+      };
+    } else {
+      return {
+        error: 1,
+        msg: '人员不存在',
+        data: null,
+      };
+    }
+  },
+};
+
+export default {
+  /**
+   * 人员列表接口
+   */
+  list: (params) => adminMockData.getList(params),
+
+  /**
+   * 人员详情接口
+   */
+  detail: (id) => adminMockData.getDetail(id),
+
+  /**
+   * 新增人员接口
+   */
+  add: (data) => adminMockData.add(data),
+
+  /**
+   * 编辑人员接口
+   */
+  edit: (id, data) => adminMockData.edit(id, data),
+
+  /**
+   * 删除人员接口
+   */
+  delete: (id) => adminMockData.delete(id),
+};

+ 2 - 0
src/sheep/mock/handlers/index.js

@@ -8,6 +8,7 @@ import { categoryHandlers } from './category.js';
 import { orderHandlers } from './order.js';
 import { userHandlers } from './user.js';
 import { commissionHandlers } from './commission.js';
+import { roleHandlers } from './role.js';
 
 // 合并所有处理器
 export const handlers = [
@@ -18,6 +19,7 @@ export const handlers = [
   ...orderHandlers,
   ...userHandlers,
   ...commissionHandlers,
+  ...roleHandlers,
   // 后续可以添加更多处理器
   // ...adminHandlers,
 ];

+ 64 - 0
src/sheep/mock/handlers/role.js

@@ -0,0 +1,64 @@
+import { http, HttpResponse } from 'msw';
+import { roleMockData } from '../role.js';
+
+export const roleHandlers = [
+  // 获取角色列表
+  http.get('https://shop.trust-will.com/admin/auth/role', ({ request }) => {
+    const url = new URL(request.url);
+    const params = {
+      page: parseInt(url.searchParams.get('page') || '1'),
+      list_rows: parseInt(url.searchParams.get('list_rows') || '10'),
+      name: url.searchParams.get('name') || '',
+      status: url.searchParams.get('status') || 'all',
+      order: url.searchParams.get('order') || '',
+      sort: url.searchParams.get('sort') || '',
+    };
+
+    const result = roleMockData.getList(params);
+    return HttpResponse.json(result);
+  }),
+
+  // 获取角色详情
+  http.get('https://shop.trust-will.com/admin/auth/role/:id', ({ params }) => {
+    const result = roleMockData.getDetail(params.id);
+    return HttpResponse.json(result);
+  }),
+
+  // 创建角色
+  http.post('https://shop.trust-will.com/admin/auth/role', async ({ request }) => {
+    const body = await request.json();
+    const result = roleMockData.create(body);
+    return HttpResponse.json(result);
+  }),
+
+  // 更新角色
+  http.put('https://shop.trust-will.com/admin/auth/role/:id', async ({ params, request }) => {
+    const body = await request.json();
+    const result = roleMockData.update(params.id, body);
+    return HttpResponse.json(result);
+  }),
+
+  // 删除角色
+  http.delete('https://shop.trust-will.com/admin/auth/role/:id', ({ params }) => {
+    const result = roleMockData.delete(params.id);
+    return HttpResponse.json(result);
+  }),
+
+  // 获取角色选择列表(用于级联选择)
+  http.get('https://shop.trust-will.com/admin/auth/role/select', () => {
+    const selectData = roleMockData.list
+      .filter(item => item.status === 'normal')
+      .map(item => ({
+        id: item.id,
+        name: item.name,
+        value: item.id,
+        label: item.name,
+      }));
+
+    return HttpResponse.json({
+      error: 0,
+      msg: 'success',
+      data: selectData,
+    });
+  }),
+];

+ 267 - 0
src/sheep/mock/role.js

@@ -0,0 +1,267 @@
+// 角色管理mock数据
+export const roleMockData = {
+  // 角色列表
+  list: [
+    {
+      id: 1,
+      name: '管理员',
+      code: 'admin',
+      description: '系统全部功能的访问权限',
+      permissions: ['首页', '商品', '订单', '用户', '营销', '内容', '数据', '财务', '设置'],
+      permissions_text: '首页、商品、订单、用户、营销、内容、数据、财务、设置',
+      remark: '拥有所有权限',
+      status: 'normal',
+      status_text: '正常',
+      rules: [
+        '1',
+        '90',
+        '901',
+        '300',
+        '113',
+        '125',
+        '400',
+        '154',
+        '171',
+        '172',
+        '500',
+        '501',
+        '502',
+        '503',
+        '600',
+        '601',
+        '700',
+        '701',
+        '702',
+        '703',
+        '800',
+        '801',
+        '900',
+        '901',
+        '902',
+        '903',
+        '1000',
+        '1002',
+      ],
+      create_time: '2025-01-10 10:00:00',
+      update_time: '2025-01-15 14:30:00',
+    },
+    {
+      id: 2,
+      name: '店长',
+      code: 'manager',
+      description: '店铺管理、订单管理等核心功能',
+      permissions: ['商品', '订单', '营销', '数据'],
+      permissions_text: '商品、订单、营销、数据',
+      remark: '店铺运营权限',
+      status: 'normal',
+      status_text: '正常',
+      rules: [
+        { id: 2, checked: true },
+        { id: 3, checked: true },
+        { id: 5, checked: true },
+        { id: 7, checked: true },
+      ],
+      create_time: '2025-01-10 11:00:00',
+      update_time: '2025-01-14 16:20:00',
+    },
+    {
+      id: 3,
+      name: '财务',
+      code: 'finance',
+      description: '财务管理、数据查看权限',
+      permissions: ['财务', '数据'],
+      permissions_text: '财务、数据',
+      remark: '财务相关权限',
+      status: 'normal',
+      status_text: '正常',
+      rules: [],
+      create_time: '2025-01-10 12:00:00',
+      update_time: '2025-01-13 09:15:00',
+    },
+    {
+      id: 4,
+      name: '客服',
+      code: 'service',
+      description: '用户管理、订单查看权限',
+      permissions: ['用户', '订单'],
+      permissions_text: '用户、订单',
+      remark: '客服支持权限',
+      status: 'normal',
+      status_text: '正常',
+      rules: [],
+      create_time: '2025-01-11 09:30:00',
+      update_time: '2025-01-12 15:45:00',
+    },
+    {
+      id: 5,
+      name: '运营',
+      code: 'operation',
+      description: '营销活动、内容管理权限',
+      permissions: ['营销', '内容', '数据'],
+      permissions_text: '营销、内容、数据',
+      remark: '运营推广权限',
+      status: 'normal',
+      status_text: '正常',
+      rules: [],
+      create_time: '2025-01-11 14:20:00',
+      update_time: '2025-01-14 11:30:00',
+    },
+    {
+      id: 6,
+      name: '小编',
+      code: 'editor',
+      description: '内容管理权限',
+      permissions: ['内容'],
+      permissions_text: '内容',
+      remark: '内容编辑权限',
+      status: 'normal',
+      status_text: '正常',
+      rules: [],
+      create_time: '2025-01-12 10:15:00',
+      update_time: '2025-01-13 16:20:00',
+    },
+    {
+      id: 7,
+      name: '统计员',
+      code: 'analyst',
+      description: '数据查看和分析权限',
+      permissions: ['数据'],
+      permissions_text: '数据',
+      remark: '数据分析权限',
+      status: 'disabled',
+      status_text: '禁用',
+      rules: [],
+      create_time: '2025-01-12 15:30:00',
+      update_time: '2025-01-14 10:45:00',
+    },
+    {
+      id: 8,
+      name: '仓库管理',
+      code: 'warehouse',
+      description: '商品库存管理权限',
+      permissions: ['商品', '订单'],
+      permissions_text: '商品、订单',
+      remark: '仓库管理权限',
+      status: 'normal',
+      status_text: '正常',
+      rules: [],
+      create_time: '2025-01-13 08:45:00',
+      update_time: '2025-01-15 12:10:00',
+    },
+  ],
+
+  // 获取列表数据
+  getList(params = {}) {
+    const { page = 1, list_rows = 10, name = '', status = 'all' } = params;
+
+    let filteredData = [...this.list];
+
+    // 搜索过滤
+    if (name) {
+      filteredData = filteredData.filter(
+        (item) =>
+          item.name.toLowerCase().includes(name.toLowerCase()) ||
+          item.description.toLowerCase().includes(name.toLowerCase()),
+      );
+    }
+    if (status && status !== 'all') {
+      filteredData = filteredData.filter((item) => item.status === status);
+    }
+
+    // 分页
+    const total = filteredData.length;
+    const startIndex = (page - 1) * list_rows;
+    const endIndex = startIndex + list_rows;
+    const data = filteredData.slice(startIndex, endIndex);
+
+    return {
+      error: 0,
+      msg: 'success',
+      data: {
+        data,
+        total,
+        per_page: list_rows,
+        current_page: page,
+        last_page: Math.ceil(total / list_rows),
+      },
+    };
+  },
+
+  // 获取详情数据
+  getDetail(id) {
+    const item = this.list.find((item) => item.id == id);
+    if (!item) {
+      return {
+        error: 1,
+        msg: '记录不存在',
+        data: null,
+      };
+    }
+
+    return {
+      error: 0,
+      msg: 'success',
+      data: item,
+    };
+  },
+
+  // 创建角色
+  create(data) {
+    const newRole = {
+      id: Date.now(),
+      ...data,
+      create_time: new Date().toLocaleString('zh-CN'),
+      update_time: new Date().toLocaleString('zh-CN'),
+    };
+    this.list.unshift(newRole);
+
+    return {
+      error: 0,
+      msg: '创建成功',
+      data: newRole,
+    };
+  },
+
+  // 更新角色
+  update(id, data) {
+    const index = this.list.findIndex((item) => item.id == id);
+    if (index === -1) {
+      return {
+        error: 1,
+        msg: '记录不存在',
+        data: null,
+      };
+    }
+
+    this.list[index] = {
+      ...this.list[index],
+      ...data,
+      update_time: new Date().toLocaleString('zh-CN'),
+    };
+
+    return {
+      error: 0,
+      msg: '更新成功',
+      data: this.list[index],
+    };
+  },
+
+  // 删除角色
+  delete(id) {
+    const index = this.list.findIndex((item) => item.id == id);
+    if (index === -1) {
+      return {
+        error: 1,
+        msg: '记录不存在',
+        data: null,
+      };
+    }
+
+    this.list.splice(index, 1);
+    return {
+      error: 0,
+      msg: '删除成功',
+      data: null,
+    };
+  },
+};