فهرست منبع

添加公司,套餐及权限模块

yangg 1 سال پیش
والد
کامیت
719b693423

+ 1 - 1
.env

@@ -1 +1 @@
-VITE_API_URL=https://api.raycos.net
+VITE_API_URL=http://192.168.8.101:9998

+ 41 - 0
src/api/company.js

@@ -0,0 +1,41 @@
+import request from '@/utils/request'
+/* 获取公司列表 */
+export function getPage(params) {
+    return request({
+      url: '/admin/erp/company/getPage',
+      method: 'get',
+      loaidng: false,
+      params
+    })
+  }
+  /* 创建公司 */
+  export function addCompany(parmas) {
+    return request({
+      url: `/admin/erp/company/createCompany`,
+      method: 'post',
+      headers: { 'Content-Type': 'application/json' },
+      data: parmas
+    })
+  }
+
+  /*编辑公司*/
+
+export function editCompany(id,parmas) {
+  return request({
+    url: `/admin/erp/company/updateCompany/${id}`,
+    method: 'post',
+    headers: { 'Content-Type': 'application/json' },
+    data: parmas
+  })
+}
+
+/* 套餐续费  */
+
+export function renewCompany(id,parmas) {
+  return request({
+    url: `/admin/erp/company/renewCompany/${id}`,
+    method: 'post',
+    headers: { 'Content-Type': 'application/json' },
+    data: parmas
+  })
+}

+ 1 - 1
src/api/goodsExchange.js

@@ -217,7 +217,7 @@ export function getWarehouseOut(params) {
 
 export function getWarehouseEntry(params) {
   return request({
-    url: '/admin/erp/warehouseEntry',//'/admin/erp/warehouseEntryProduct/query',
+    url: '/admin/erp/warehouseEntry',
     method: 'get',
     loaidng: false,
     params

+ 40 - 0
src/api/package.js

@@ -0,0 +1,40 @@
+import request from '@/utils/request'
+
+/*新增套餐*/
+export function submitPackage(parmas) {
+    return request({
+      url: `/admin/erp/companyPackage/createCompanyPackage`,
+      method: 'post',
+      headers: { 'Content-Type': 'application/json' },
+      data: parmas
+    })
+  }
+
+  /*修改套餐 */
+export function editPackage(id,parmas) {
+    return request({
+      url: `/admin/erp/companyPackage/updateCompanyPackage/${id}`,
+      method: 'post',
+      headers: { 'Content-Type': 'application/json' },
+      data: parmas
+    })
+  }
+
+  /* 套餐列表 */
+  export function packageList(parmas) {
+    return request({
+      url: '/admin/erp/companyPackage',
+      method: 'post',
+      headers: { 'Content-Type': 'application/json' },
+      data: parmas
+    })
+  }
+
+  /*  /admin/erp/companyPackage/disAbleUse/{id}*/
+  export function switchPackage(id) {
+    return request({
+      url: `/admin/erp/companyPackage/disAbleUse/${id}`,
+      method: 'post',
+      headers: { 'Content-Type': 'application/json' },
+    })
+  }

+ 11 - 0
src/api/setting.js

@@ -367,4 +367,15 @@ export function getOpenIdCode(id,params) {
     needToken: false,
     data: params
   })
+}
+
+/* 获取权限列表 */
+
+export function getRoles(params) {
+  return request({
+    url: '/admin/systems/roles',
+    method: 'get',
+    loaidng: false,
+    params
+  })
 }

+ 0 - 2
src/app-setting.ts

@@ -44,7 +44,6 @@ export default {
 
     toggleLanguage(item: any) {
        /*  const store = useStore(); */
-        console.log(item);
         let lang: any = null;
         if (item) {
             lang = item;
@@ -63,7 +62,6 @@ export default {
         if (!lang) {
             lang = store.getters.languageList.find((d: any) => d.code === 'zh');
         }
-        console.log(lang);
         store.dispatch('toggleLocale', lang.code);
         return lang;
     },

+ 8 - 5
src/components/layout/Sidebar.vue

@@ -65,7 +65,7 @@
                 </ul>
               </vue-collapsible>
             </li>
-            <li class="nav-item">
+            <!-- <li class="nav-item">
               <ul>
                 <li class="menu nav-item">
                   <button
@@ -104,7 +104,7 @@
                   </vue-collapsible>
                 </li>
               </ul>
-            </li>
+            </li> -->
             <li class="menu nav-item">
               <button
                 type="button"
@@ -115,7 +115,7 @@
                 <div class="flex items-center">
                   <icon-menu-users class="group-hover:!text-primary shrink-0" />
 
-                  <span class="ltr:pl-3 rtl:pr-3 text-black dark:text-[#506690] dark:group-hover:text-white-dark">系统管理</span>
+                  <span class="ltr:pl-3 rtl:pr-3 text-black dark:text-[#506690] dark:group-hover:text-white-dark">{{ $t('setting') }}</span>
                 </div>
                 <div :class="{ 'rtl:rotate-90 -rotate-90': activeDropdown == 'users' }">
                   <icon-caret-down />
@@ -124,10 +124,13 @@
               <vue-collapsible :isOpen="activeDropdown === 'users'">
                 <ul class="sub-menu text-gray-500">
                   <li>
-                    <router-link to="/users/profile" @click="toggleMobileMenu">个人设置</router-link>
+                    <router-link to="/setting/package" @click="toggleMobileMenu">{{ $t('package') }}</router-link>
+                  </li>
+                  <li>
+                    <router-link to="/setting/company" @click="toggleMobileMenu">{{ $t('company') }}</router-link>
                   </li>
                   <li>
-                    <router-link to="/users/contacts" @click="toggleMobileMenu">账户列表</router-link>
+                    <router-link to="/setting/roleManage" @click="toggleMobileMenu">{{ $t('roleManage') }}</router-link>
                   </li>
                 </ul>
               </vue-collapsible>

+ 6 - 0
src/locales/zh.json

@@ -6,6 +6,11 @@
     "goodsExchange":"入库管理",
     "goodsOutBound":"出库管理",
     
+    "setting":"系统管理",
+    "package":"套餐管理",
+    "company":"公司管理",
+    "roleManage":"权限管理",
+
     "sales": "销售量",
     "analytics": "分析",
     "apps": "应用",
@@ -131,6 +136,7 @@
     "coming_soon_cover": "即将推出封面",
     "contact_us_boxed": "联系我们 盒装",
     "contact_us_cover": "联系我们封面",
+
     "logIn": "登录",
     "username": "请输入管理员名称",
     "password": "请输入管理员密码",

+ 54 - 6
src/router/index.ts

@@ -4,7 +4,7 @@ import appSetting from '@/app-setting';
 import HomeView from '../views/index.vue';
 
 const constantRouterMap: RouteRecordRaw[] = [
-    { path: '/login', component: () => import('@/views/auth/boxed-signin.vue'),meta: { layout: 'auth' } },
+    { path: '/login', component: () => import('@/views/auth/boxed-signin.vue'), meta: { layout: 'auth' } },
 ];
 const router = createRouter({
     history: createWebHashHistory(),
@@ -17,7 +17,7 @@ const asyncRouterMap: RouteRecordRaw[] = [
         redirect: '/dashboard',
         name: 'dashboard',
         children: [
-            { path: 'dashboard', component: () => import('@/views/index.vue'), name: 'dashboard', meta: { title: 'home', icon: 'dashboard', layout: 'app'  } }
+            { path: 'dashboard', component: () => import('@/views/index.vue'), name: 'dashboard', meta: { title: 'home', icon: 'dashboard', layout: 'app' } }
         ]
     },
     {
@@ -25,18 +25,66 @@ const asyncRouterMap: RouteRecordRaw[] = [
         redirect: '/stock',
         name: 'stock',
         children: [
-            { path: 'warehouseList', component: () => import('@/views/stock/warehouseList.vue'), name: 'warehouseList', meta: { title: 'warehouseList',layout: 'app' } },
-            { path: 'goodsLend', component: () => import('@/views/stock/goodsLend.vue'), name: 'goodsLend', meta: { title: 'goodsLend',layout: 'app' } },
-            { path: 'goodsTransfer', component: () => import('@/views/stock/goodsTransfer.vue'), name: 'goodsTransfer', meta: { title: 'goodsTransfer',layout: 'app' } },
+            { path: 'warehouseList', component: () => import('@/views/stock/warehouseList.vue'), name: 'warehouseList', meta: { title: 'warehouseList', layout: 'app' } },
+            { path: 'goodsLend', component: () => import('@/views/stock/goodsLend.vue'), name: 'goodsLend', meta: { title: 'goodsLend', layout: 'app' } },
+            { path: 'goodsTransfer', component: () => import('@/views/stock/goodsTransfer.vue'), name: 'goodsTransfer', meta: { title: 'goodsTransfer', layout: 'app' } },
             { path: 'goodsExchange', component: () => import('@/views/stock/warehousing.vue'), name: 'goodsExchange', meta: { title: 'goodsExchange' } },
             { path: 'goodsOutBound', component: () => import('@/views/stock/outbound.vue'), name: 'goodsOutBound', meta: { title: 'goodsOutBound' } },
         ]
+    }, // 系统管理
+    {
+        path: '/setting',
+        redirect: '/setting',
+        name: 'setting',
+        meta: {
+            title: 'setting',
+            icon: 'setting-manage'
+        },
+        children: [
+            { path: 'package', component: () => import('@/views/setting/package.vue'), name: 'package', meta: { title: 'package',layout: 'app' } },
+            { path: 'company', component: () => import('@/views/setting/company.vue'), name: 'company', meta: { title: 'company',layout: 'app' } },
+            { path: 'roleManage', component: () => import('@/views/setting/roleManage.vue'), name: 'roleManage', meta: { title: 'roleManage',layout: 'app' } },
+            /* {
+                path: '/setting/system-params',
+                component: () => import('@/views/setting/shop-settings/index'),
+                redirect: '/setting/system-params/system',
+                name: 'systemParams',
+                meta: { title: 'systemParams' },
+                children: [
+                    { path: 'system', component: () => import('@/views/setting/shop-settings/systemSettings'), name: 'systemSettings', meta: { title: 'systemSettings' } },
+                    { path: 'storage-solution', component: () => import('@/views/setting/shop-settings/storageSolution'), name: 'storageSolution', meta: { title: 'storageSolution' } },
+                    { path: 'dict-data', component: () => import('@/views/setting/tool-manage/dictData'), name: 'dictData', meta: { title: 'dictData' } },
+                    { path: 'system-logs', component: () => import('@/views/setting/systemLogs'), name: 'systemLogs', meta: { title: 'systemLogs' } },
+                    // serialList
+
+                ]
+            },
+            {
+                path: '/setting/oauth2',
+                component: () => import('@/views/setting/shop-settings/index'),
+                redirect: '/setting/oauth2/client',
+                name: 'oauth2',
+                hidden: true,
+                meta: { title: 'oauth2' },
+                children: [
+                    { path: 'client', component: () => import('@/views/setting/oauth2/client'), name: 'oauth2Client', meta: { title: 'oauth2Client' } },
+                    { path: 'token', component: () => import('@/views/setting/oauth2/token'), name: 'oauth2Token', meta: { title: 'oauth2Token' } }
+                ]
+            }, */
+           /*  { path: 'administrator-manage', component: () => import('@/views/setting/auth-settings/administratorManage'), name: 'administratorManage', meta: { title: 'administratorManage' } },
+            { path: 'role-manage', component: () => import('@/views/setting/auth-settings/roleManage'), name: 'roleManage', meta: { title: 'roleManage' } },
+            { path: 'role-permission/:id(\\d+)', component: () => import('@/views/setting/auth-settings/rolePermission'), name: 'roleManageEdit', hidden: true, meta: { title: 'rolePermission', noCache: true } },
+            { path: 'role-permission', component: () => import('@/views/setting/auth-settings/rolePermission'), name: 'roleManageAdd', hidden: true, meta: { title: 'rolePermission', noCache: true } },
+            { path: 'menu-manage', component: () => import('@/views/setting/tool-manage/menuManage'), name: 'menuManage', meta: { title: 'menuManage' } },
+            { path: 'branch', component: () => import('@/views/setting/tool-manage/branch'), name: 'branch', meta: { title: 'branch' } },
+            { path: 'station', component: () => import('@/views/setting/tool-manage/station'), name: 'station', meta: { title: 'station' } }, */
+        ]
     },
 ]
 asyncRouterMap.forEach(route => {
     router.addRoute(route);
 });
-/* router.beforeEach((to, from, next) => {
+/* router.beforeEach((to, from, next) => {package
     const store = useStore();
     //判断当前页面是否带有侧边栏及顶部
     if (to?.meta?.layout == 'auth') {

+ 0 - 2
src/stores/modules/permission.js

@@ -3,8 +3,6 @@ import { getUserRolesPermissions } from '@/api/login'
 import Storage from '@/utils/storage'
 import tagsView from './tagsView'
 import router from '@/router';
-console.log("constantRouterMap:",constantRouterMap);
-console.log("asyncRouterMap:",asyncRouterMap);
 const permission = {
   state: {
     routers: constantRouterMap,

+ 6 - 7
src/utils/request.js

@@ -1,5 +1,5 @@
 import * as Vue from 'vue';
-import { ElLoading  } from 'element-plus'
+import { ElLoading,ElMessage  } from 'element-plus'
 import axios from 'axios'
 import { useStore } from 'vuex';
 import router from '@/router'
@@ -16,11 +16,9 @@ import * as qs from 'qs'
 // 创建axios实例
 const service = axios.create({
   baseURL: import.meta.env.VITE_API_URL,
-  timeout: 5000,
-
+  timeout: 10000,
   paramsSerializer: params => qs.stringify(params, { arrayFormat: 'repeat' })
 })
-
 // request拦截器
 service.interceptors.request.use(config => {
   // 如果是put/post请求,用qs.stringify序列化参数
@@ -69,14 +67,16 @@ service.interceptors.response.use(
     const error_data = error_response.data || {}
     if (error_response.status === 403) {
       if (!Storage.getItem('admin_refresh_token')) return
-      useStore.dispatch('fedLogoutAction')
+      const store = useStore(); // 确保在 setup 函数中使用 useStore
+      store.dispatch('fedLogoutAction');
       let to = Storage.getItem('admin_franchise') ? '/franchise/login' : '/login'
       window.location.replace(router.resolve(to).href)
       return
     }
     if (error.config.message !== false) {
       let _message = error.code === 'ECONNABORTED' ? '连接超时,请稍候再试!' : '网络错误,请稍后再试!'
-      Vue.prototype.$message.error(error_data.message || _message)
+      ElMessage.error(error_data.message || _message)
+      //Vue.prototype.$message.error(error_data.message || _message)
     }
     return Promise.reject(error)
   }
@@ -88,7 +88,6 @@ service.interceptors.response.use(
  * @param target
  */
 const closeLoading = (target) => {
-  console.log(target);
   if (!target.config.loading) return true
   return new Promise((resolve, reject) => {
     setTimeout(() => {

+ 15 - 9
src/views/auth/boxed-signin.vue

@@ -17,7 +17,7 @@
           <div class="mx-auto w-full max-w-[440px]">
             <div class="mb-10">
               <h1 class="text-3xl font-extrabold uppercase !leading-snug text-primary md:text-4xl">Sign in</h1>
-           <!--    <p class="text-base font-bold leading-normal text-white-dark">欢迎登录V3StudioApp</p> -->
+              <!--    <p class="text-base font-bold leading-normal text-white-dark">欢迎登录V3StudioApp</p> -->
             </div>
             <!--  <form class="space-y-5 dark:text-white" @submit.prevent="router.push('/')">
                             <div>
@@ -53,19 +53,24 @@
                         </form> -->
             <!-- <el-button @click="goHome">测试</el-button> -->
             <el-form :model="loginForm" :rules="loginRules" ref="loginFormRef" class="login-input">
-              <el-form-item prop="company_name">
-                <el-select v-model="loginForm.company_name" filterable :placeholder="$t('company_name')" style="width: 240px">
+              <el-form-item label="公司名称:" prop="company_name">
+                <el-input v-model="loginForm.company_name" :placeholder="$t('company_name')" autofocus clearable>
+                  <template v-slot:prefix>
+                    <svg-icon class="el-input__icon" icon-class="user" />
+                  </template>
+                </el-input>
+                <!--  <el-select v-model="loginForm.company_name" filterable :placeholder="$t('company_name')" style="width: 240px">
                   <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
-                </el-select>
+                </el-select> -->
               </el-form-item>
-              <el-form-item prop="username">
+              <el-form-item label="账  号:" prop="username">
                 <el-input v-model="loginForm.username" :placeholder="$t('username')" autofocus clearable>
                   <template v-slot:prefix>
                     <svg-icon class="el-input__icon" icon-class="user" />
                   </template>
                 </el-input>
               </el-form-item>
-              <el-form-item prop="password">
+              <el-form-item label="密  码:" prop="password">
                 <el-input
                   v-model="loginForm.password"
                   type="password"
@@ -80,7 +85,7 @@
                   </template>
                 </el-input>
               </el-form-item>
-              <el-form-item prop="validcode" v-if="validateInFO.validator_type === 'IMAGE'">
+              <el-form-item label="验证码:" prop="validcode" v-if="validateInFO.validator_type === 'IMAGE'">
                 <el-input v-model="loginForm.validcode" :placeholder="$t('validcode')" :maxlength="4" clearable>
                   <template v-slot:append>
                     <img class="login-validcode-img h-8 w-full p-0" :src="validcodeImg" @click="changeValidcode" />
@@ -131,20 +136,21 @@ import app from '@/stores/modules/app';
 
 const loading: any = ref(false);
 
-const options:any=ref([])
+const options: any = ref([]);
 
 const loginForm = reactive({
   username: '',
   password: '',
   scene: 'LOGIN',
   validcode: '',
-  company_name: 'aslkf',
+  company_name: '',
 });
 const { t } = useI18n();
 const loginRules = reactive({
   username: [{ required: true, message: t('val_username'), trigger: 'blur' }],
   password: [{ required: true, message: t('val_password'), trigger: 'blur' }],
   validcode: [{ required: true, message: t('val_validcode'), trigger: 'blur' }],
+  company_name: [{ required: true, message: t('company_name'), trigger: 'blur' }],
 });
 // 验证信息
 const validateInFO: any = ref({});

+ 186 - 0
src/views/setting/branch.vue

@@ -0,0 +1,186 @@
+<template>
+    <div style="display:flex">
+      <div class="menus-container">
+        <div class="toolbar">
+          <div class="inner-toolbar">
+            <div class="toolbar-btns">
+              <el-button size="mini" v-if="checkPermission(['branch:add'])" type="primary"
+                @click="handleAddBranch">新增</el-button>
+            </div>
+          </div>
+        </div>
+        <div style="overflow:auto;height:540px;background:#fff;">
+          <el-tree :data="deptList" node-key="identifier" :expand-on-click-node="false" default-expand-all>
+            <template #default="{ node, data }">
+              <span class="custom-tree-node" @click="handleEditBranch(data)">
+                <span>{{ data.name }}</span>
+              </span>
+            </template>
+          </el-tree>
+        </div>
+        <el-dialog title="添加部门" v-model:visible="dialogVisible" width="500px" :close-on-click-modal="false"
+          :close-on-press-escape="false">
+          <el-form :model="deptForm" ref="deptForm" label-width="130px">
+            <el-form-item label="部门名称" prop="name">
+              <el-input v-model="deptForm.name" :maxlength="6"></el-input>
+            </el-form-item>
+            <el-form-item label="部门编码" prop="sn">
+              <el-input v-model="deptForm.sn" :maxlength="50"></el-input>
+            </el-form-item>
+            <el-form-item label="上级部门" prop="auth_regular">
+              <el-cascader v-model="deptForm.parent_id" :options="deptList" :props="{
+                label: 'name',
+                value: 'id',
+                emitPath: false,
+                checkStrictly: true
+              }" clearable></el-cascader>
+            </el-form-item>
+            <el-form-item label="所在地区" prop="auth_regular">
+              <en-region-picker :api="MixinRegionApi" :default="defaultRegions"
+                @changed="(object) => { deptForm.region = object.last_id }" />
+            </el-form-item>
+            <el-form-item label="部门类型" prop="auth_regular">
+              <el-select v-model="deptForm.type" placeholder="请选择部门类型" size="small">
+                <el-option v-for="item in deptType" :key="item.value" :label="item.label" :value="item.value" />
+              </el-select>
+            </el-form-item>
+            <el-form-item label="显示顺序" prop="sort">
+              <el-input v-model="deptForm.sort" :maxlength="2"></el-input>
+            </el-form-item>
+            <el-form-item label="部门说明" prop="remark">
+              <el-input v-model="deptForm.remark" type="textarea" :autosize="{ minRows: 3, maxRows: 4 }"
+                placeholder="请输入部门说明"></el-input>
+            </el-form-item>
+          </el-form>
+          <template #footer>
+            <el-button @click="dialogVisible = false">取 消</el-button>
+            <el-button type="primary" @click="submitBranchForm">确 定</el-button>
+          </template>
+        </el-dialog>
+      </div>
+      <div class="basicsetting-container">
+        <el-form class="form" :model="deptForm" ref="deptForm" label-width="120px" v-if="dialogEditVisible">
+          <el-form-item label="部门名称" prop="name">
+            <el-input v-model="deptForm.name" :maxlength="6"></el-input>
+          </el-form-item>
+          <el-form-item label="部门编码" prop="sn">
+            <el-input v-model="deptForm.sn" :maxlength="50"></el-input>
+          </el-form-item>
+          <el-form-item label="上级部门" prop="parent_id">
+            <el-cascader v-model="deptForm.parent_id" :options="deptList" :props="{
+              label: 'name',
+              value: 'id',
+              emitPath: false,
+              checkStrictly: true
+            }" clearable></el-cascader>
+          </el-form-item>
+          <el-form-item label="所在地区" prop="region">
+            <en-region-picker :api="MixinRegionApi" :default="defaultRegions"
+              @changed="(object) => { deptForm.region = object.last_id }" />
+          </el-form-item>
+          <el-form-item label="部门类型" prop="type">
+            <el-select v-model="deptForm.type" placeholder="请选择部门类型" size="small">
+              <el-option v-for="item in deptType" :key="item.value" :label="item.label" :value="item.value" />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="显示顺序" prop="sort">
+            <el-input v-model="deptForm.sort" :maxlength="2"></el-input>
+          </el-form-item>
+          <el-form-item label="部门说明" prop="remark">
+            <el-input v-model="deptForm.remark" type="textarea" :autosize="{ minRows: 3, maxRows: 4 }"
+              placeholder="请输入部门说明"></el-input>
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" @click="submitBranchForm" v-if="checkPermission(['branch:edit'])">保 存</el-button>
+            <el-button type="danger" @click="handleDeleteBranch" v-if="checkPermission(['branch:del'])">删 除</el-button>
+          </el-form-item>
+        </el-form>
+      </div>
+    </div>
+  </template>
+  
+  <script lang="ts" setup>
+  import { ref, onMounted } from 'vue';
+  import { ElMessage, ElMessageBox } from 'element-plus';
+  import * as API_Setting from '@/api/setting';
+ /*  import {   } from '~/ui-utils'; */
+  
+  const defaultRegions = ref(null);
+  const deptList = ref([]);
+  const deptType = ref([]);
+  const deptForm = ref({
+    sort: 1
+  });
+  const dialogVisible = ref(false);
+  const dialogEditVisible = ref(false);
+  
+  const checkPermission = (permissions: string[]): boolean => {
+    return true;
+  };
+  
+  const GET_DictDataInfo = () => {
+    API_Setting.getDictDataInfo('erp_dept_type').then(response => {
+      deptType.value = response;
+    });
+  };
+  
+  const GET_DeptList = () => {
+    API_Setting.getDeptList().then(response => {
+      //deptList.value = Foundation.buildTree(response, '0');
+    });
+  };
+  
+  const handleAddBranch = () => {
+    deptForm.value = {
+      sort: 1,
+      type: deptType.value.find(sc => sc.default_flag === true)?.value || ''
+    };
+    defaultRegions.value = [];
+    dialogEditVisible.value = false;
+    dialogVisible.value = true;
+  };
+  
+  const handleEditBranch = (row: any) => {
+    deptForm.value = { ...row };
+    defaultRegions.value = row.region_ids;
+    dialogEditVisible.value = true;
+  };
+  
+  const handleDeleteBranch = () => {
+    ElMessageBox.confirm('确定要删除这个部门吗?', '提示', { type: 'warning' }).then(() => {
+      const { id } = deptForm.value;
+      API_Setting.deleteDept({ id }).then(() => {
+        ElMessage.success('删除成功!');
+        dialogEditVisible.value = false;
+        GET_DeptList();
+      });
+    }).catch(() => { });
+  };
+  
+  const submitBranchForm = () => {
+    const { id, region } = deptForm.value;
+    const params = { ...deptForm.value };
+    if (!region) {
+      ElMessage.error('请选择所在地区!');
+      return false;
+    }
+    if (id) {
+      API_Setting.editDept(params).then(() => {
+        dialogVisible.value = false;
+        ElMessage.success('修改成功!');
+        GET_DeptList();
+      });
+    } else {
+      API_Setting.addDept(params).then(() => {
+        dialogVisible.value = false;
+        ElMessage.success('添加成功!');
+        GET_DeptList();
+      });
+    }
+  };
+  
+  onMounted(() => {
+    GET_DeptList();
+    GET_DictDataInfo();
+  });
+  </script>

+ 388 - 0
src/views/setting/company.vue

@@ -0,0 +1,388 @@
+<template>
+  <div>
+    <en-table-layout :tableData="tableData.records" :loading="loading">
+      <template #toolbar>
+        <div class="inner-toolbar w-11/12 flex justify-between">
+          <div class="toolbar-btns">
+            <el-button size="mini" v-if="checkPermission(['warehouse:add'])" type="primary" @click="handleAddWarehouse">新增</el-button>
+          </div>
+          <div class="toolbar-search">
+            <en-table-search @search="searchEvent" placeholder="请输入关键字搜索" />
+          </div>
+        </div>
+      </template>
+
+      <template #table-columns>
+        <el-table-column prop="name" label="公司名称" />
+        <el-table-column prop="industry" label="行业" />
+        <el-table-column prop="user_name" label="联系人" />
+        <el-table-column prop="phone" label="电话" />
+        <el-table-column prop="email" label="邮箱" />
+        <el-table-column label="地址">
+          <template #default="scope">
+            {{ adminName(scope.row.province) + scope.row.address }}
+          </template>
+        </el-table-column>
+
+        <el-table-column label="操作">
+          <template #default="scope">
+            <el-button size="mini" v-if="checkPermission(['warehouse:update'])" @click="handleEditWarehouse(scope.$index, scope.row)">编辑</el-button>
+            <el-button size="mini" v-if="checkPermission(['warehouse:update'])" @click="handleRenew(scope.$index, scope.row)">续费</el-button>
+          </template>
+        </el-table-column>
+      </template>
+      <template #pagination>
+        <el-pagination
+          v-if="tableData.data && tableData.data.length"
+          @size-change="handlePageSizeChange"
+          @current-change="handlePageCurrentChange"
+          :current-page="tableData.page_no"
+          :page-sizes="[10, 20, 50, 100]"
+          :page-size="tableData.page_size"
+          layout="total, sizes, prev, pager, next, jumper"
+          :total="tableData.data_total"
+        >
+        </el-pagination>
+      </template>
+    </en-table-layout>
+
+    <el-dialog
+      :title="companyForm.id ? '编辑公司' : '新建公司'"
+      v-model="dialogVisible"
+      @open="handleDialogOpen"
+      :modal-append-to-body="false"
+      :close-on-click-modal="false"
+      :close-on-press-escape="false"
+    >
+      <el-form :model="companyForm" :rules="stockRules" ref="companyFormRef" label-width="140px">
+        <el-row>
+          <el-col :span="10" v-if="!companyForm.id">
+            <el-form-item label="套餐:" prop="company_package_id">
+              <el-select v-model="companyForm.company_package_id" clearable placeholder="请选择套餐">
+                <el-option v-for="item in packageList" :key="item.id" :label="item.name" :value="item.id" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="10">
+            <el-form-item label="公司名称:" prop="name">
+              <el-input v-model="companyForm.name" :minlength="2" :maxlength="20" clearable placeholder="请输入公司名称"></el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :span="10">
+            <el-form-item label="统一社会信用代码:" prop="credit_code">
+              <el-input v-model="companyForm.credit_code" :minlength="2" :maxlength="20" clearable placeholder="请输入统一社会信用代码"></el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :span="10">
+            <el-form-item label="行业:" prop="industry">
+              <el-input v-model="companyForm.industry" :minlength="2" :maxlength="20" clearable placeholder="请输入行业"></el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :span="10">
+            <el-form-item label="联系人:" prop="user_name">
+              <el-input v-model="companyForm.user_name" :minlength="2" :maxlength="20" clearable placeholder="请输入联系人"></el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :span="10">
+            <el-form-item label="联系人电话:" prop="phone">
+              <el-input v-model="companyForm.phone" :minlength="2" :maxlength="20" clearable placeholder="请输入联系人电话"></el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :span="10">
+            <el-form-item label="邮箱:" prop="email">
+              <el-input v-model="companyForm.email" :minlength="2" :maxlength="20" clearable placeholder="请输入邮箱"></el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :span="10">
+            <el-form-item label="邮编:" prop="postal_code">
+              <el-input v-model="companyForm.postal_code" :minlength="2" :maxlength="20" clearable placeholder="请输入邮箱"></el-input>
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="10">
+            <el-form-item label="官网:" prop="website">
+              <el-input v-model="companyForm.website" :minlength="2" :maxlength="20" clearable placeholder="请输入官网地址"></el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :span="10">
+            <el-form-item label="地址:" prop="">
+              <el-cascader class="w-full" size="default" :options="options" clearable v-model="selectedOptions"> </el-cascader>
+              <span>
+                <el-input class="!w-full mt-3" v-model="companyForm.address" placeholder="请输入"></el-input>
+              </span>
+            </el-form-item>
+          </el-col>
+          <el-col :span="10">
+            <el-form-item label="营业执照:" prop="license">
+              <el-upload
+                class="avatar-uploader"
+                action="http://192.168.8.101:9998/uploaders"
+                :show-file-list="false"
+                :on-success="handleAvatarSuccess"
+                :before-upload="beforeAvatarUpload"
+              >
+                <img v-if="companyForm.license" :src="companyForm.license" class="avatar w-28 h-28" />
+                <el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
+              </el-upload>
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="dialogVisible = false">取 消</el-button>
+          <el-button type="primary" @click="submitWarehouseForm">确 定</el-button>
+        </div>
+      </template>
+    </el-dialog>
+    <el-dialog title="续费" v-model="renewVisible" :modal-append-to-body="false" :close-on-click-modal="false" :close-on-press-escape="false">
+      <el-form :model="packageForm" :rules="stockRules" ref="packageFormRef" label-width="140px">
+        <el-form-item label="套餐:" prop="company_package_id">
+          <el-select v-model="packageForm.company_package_id" clearable placeholder="请选择套餐">
+            <el-option v-for="item in packageList" :key="item.id" :label="item.name" :value="item.id" />
+          </el-select>
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="renewVisible = false">取 消</el-button>
+          <el-button type="primary" @click="submitPackageForm">确 定</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+    
+    <script lang="ts" setup>
+import { ref, onMounted } from 'vue';
+import { useStore } from 'vuex';
+import { ElMessageBox, FormInstance, ElMessage } from 'element-plus';
+import * as API_BasicSetting from '@/api/basicSetting';
+import * as API_Setting from '@/api/setting';
+import * as API_Company from '@/api/company';
+import * as API_Package from '@/api/package';
+import Storage from '@/utils/storage';
+import router from '@/router';
+import { provinceAndCityData, pcTextArr, regionData, pcaTextArr, codeToText } from 'element-china-area-data';
+import { Plus } from '@element-plus/icons-vue';
+import type { UploadProps } from 'element-plus';
+
+const handleAvatarSuccess: UploadProps['onSuccess'] = (response, uploadFile) => {
+  console.log(response, uploadFile);
+  companyForm.value.license = response.url; //URL.createObjectURL(uploadFile.raw!);
+};
+
+const beforeAvatarUpload: UploadProps['beforeUpload'] = (rawFile) => {
+  if (rawFile.size / 1024 / 1024 > 10) {
+    ElMessage.error('Avatar picture size can not exceed 10MB!');
+    return false;
+  }
+  return true;
+};
+
+const store = useStore();
+const tableData = ref<any>({});
+const loading = ref(false);
+const dialogVisible = ref(false);
+const companyForm = ref<any>({
+  role_id: '',
+  name: '',
+  money: '',
+  day: '',
+  use_able: false,
+  remark: '',
+  address: '',
+  license: '',
+  company_package_id: '',
+});
+const stockRules = ref<any>({
+  role_id: [{ required: true, message: '请选择权限!', trigger: 'change' }],
+  name: [{ required: true, message: '请输入公司名称!', trigger: 'blur' }],
+  credit_code: [{ required: true, message: '请输入统一社会信用代码!', trigger: 'blur' }],
+  industry: [{ required: true, message: '请输入行业!', trigger: 'blur' }],
+  user_name: [{ required: true, message: '请输入联系人姓名!', trigger: 'blur' }],
+  phone: [
+    { required: true, message: '请输入联系人电话!', trigger: 'blur' },
+    { pattern: /^1[3-9]\d{9}$/, message: '请输入有效的手机号', trigger: 'blur' },
+  ],
+  email: [
+    { required: true, message: '请输入邮箱!', trigger: 'blur' },
+    { type: 'email', message: '请输入有效的邮箱地址', trigger: ['blur', 'change'] },
+  ],
+  postal_code: [
+    { required: true, message: '请输入邮编!', trigger: 'blur' },
+    { pattern: /^[1-9]\d{5}$/, message: '请输入有效的邮政编码', trigger: 'blur' },
+  ],
+});
+const selectedOptions = ref<any[]>([]);
+const marketingList = ref<any[]>([]);
+const options = ref<any[]>(regionData);
+const params = ref({
+  data: [],
+  page_no: 1,
+  page_size: 10,
+  keyword: '',
+});
+const roleArray = ref<any[]>([]);
+
+const renewVisible = ref(false);
+/* 套餐 */
+const packageForm = ref({
+  company_package_id: '',
+  this_money: '',
+});
+const packageFormRef = ref();
+/* 续费 */
+const handleRenew = (index: number, row: any) => {
+  renewVisible.value = true;
+};
+const submitPackageForm = () => {
+  companyFormRef.value.validate((valid: boolean) => {
+    if (valid) {
+      API_Company.renewCompany(id, params).then(res=>{
+        
+      })
+    }
+  });
+};
+const companyFormRef = ref();
+const checkPermission = (permissions: string[]): boolean => {
+  // Implement your permission check logic here
+  return true;
+};
+
+const handleAddWarehouse = () => {
+  selectedOptions.value = [];
+  companyForm.value = {};
+  dialogVisible.value = true;
+};
+
+const handleEditWarehouse = (index: number, row: any) => {
+  companyForm.value = { ...row };
+  selectedOptions.value = row.province;
+  console.log(companyForm);
+  dialogVisible.value = true;
+};
+
+const searchEvent = (keyword: string) => {
+  params.value = {
+    ...params.value,
+    keyword: keyword,
+  };
+  params.value.page_no = 1;
+  GET_CompanyList();
+};
+
+const handlePageSizeChange = (size: number) => {
+  params.value.page_size = size;
+  GET_CompanyList();
+};
+
+const handlePageCurrentChange = (page: number) => {
+  params.value.page_no = page;
+  GET_CompanyList();
+};
+
+const handleDialogOpen = () => {
+  setTimeout(() => {
+    companyFormRef.value.clearValidate();
+  });
+};
+
+const submitWarehouseForm = () => {
+  companyFormRef.value.validate((valid: boolean) => {
+    if (valid) {
+      const { id } = companyForm.value;
+      const params = { ...companyForm.value };
+      params.province = selectedOptions.value.join(',');
+      if (id) {
+        API_Company.editCompany(id, params).then(() => {
+          dialogVisible.value = false;
+          ElMessage.success('修改成功!');
+          GET_CompanyList();
+        });
+      } else {
+        API_Company.addCompany(params).then(() => {
+          dialogVisible.value = false;
+          ElMessage.success('添加成功!');
+          GET_CompanyList();
+        });
+      }
+    } else {
+      ElMessage.error('表单填写有误,请核对!');
+      /*    store.dispatch('showMessage', { message: '表单填写有误,请核对!', type: 'error' }); */
+      return false;
+    }
+  });
+};
+
+const GET_CompanyList = () => {
+  loading.value = true;
+  API_Company.getPage(params.value)
+    .then((response) => {
+      loading.value = false;
+      response.records.map((el: any) => {
+        if (el.province !== undefined) {
+          el.license = el.license.replace('blob:', '');
+          el.province = el.province.split(',');
+        }
+        return el;
+      });
+      tableData.value = response;
+      console.log(tableData.value);
+    })
+    .catch(() => {
+      loading.value = false;
+    });
+};
+
+const GET_MarketingList = () => {
+  API_BasicSetting.getUserByDept().then((res) => {
+    marketingList.value = res;
+  });
+};
+/* 获取公司列表 */
+const packageList = ref([]);
+const GET_PackageList = () => {
+  API_Package.packageList({ page_no: 1, page_size: 1000, use_able: true }).then((res) => {
+    packageList.value = res.records;
+  });
+};
+const adminName = (val: string[]) => {
+  let text = '';
+  console.log(val);
+  if (val !== undefined) {
+    text += codeToText[val[0]];
+    text += codeToText[val[1]];
+    text += codeToText[val[2]];
+  }
+  return text;
+};
+onMounted(() => {
+  GET_CompanyList();
+  GET_MarketingList();
+  GET_PackageList();
+});
+</script>
+<style>
+.avatar-uploader .el-upload {
+  border: 1px dashed var(--el-border-color);
+  border-radius: 6px;
+  cursor: pointer;
+  position: relative;
+  overflow: hidden;
+  transition: var(--el-transition-duration-fast);
+}
+
+.avatar-uploader .el-upload:hover {
+  border-color: var(--el-color-primary);
+}
+
+.el-icon.avatar-uploader-icon {
+  font-size: 28px;
+  color: #8c939d;
+  width: 112px;
+  height: 112px;
+  text-align: center;
+}
+</style>

+ 250 - 0
src/views/setting/package.vue

@@ -0,0 +1,250 @@
+<template>
+  <div>
+    <div class="search bg-white p-3">
+      <el-form :model="searchForm" label-width="100px">
+        <el-row>
+          <el-col :span="5">
+            <el-form-item label="状态">
+              <el-select v-model="searchForm.use_able" clearable>
+                <el-option label="启用" :value="true" />
+                <el-option label="禁用" :value="false" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="5">
+            <el-form-item label="套餐名称">
+              <el-input v-model="searchForm.name" clearable placeholder="请输入套餐名称"></el-input>
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+      <div class="flex justify-end">
+        <el-button class="mr-3" @click="searchEvent"> 搜索 </el-button>
+      </div>
+    </div>
+    <en-table-layout :tableData="tableData.records" :loading="loading">
+      <template #toolbar>
+        <div class="inner-toolbar w-11/12 flex justify-between">
+          <div class="toolbar-btns">
+            <el-button size="default" v-if="checkPermission(['warehouse:add'])" type="primary" @click="handleAddWarehouse">新增</el-button>
+          </div>
+          <!-- <div class="toolbar-search">
+            <en-table-search @search="searchEvent" placeholder="请输入关键字搜索" />
+          </div> -->
+        </div>
+      </template>
+
+      <template #table-columns>
+        <el-table-column prop="name" label="套餐名称" />
+        <el-table-column prop="money" label="套餐金额" />
+        <el-table-column prop="day" label="天数" />
+        <el-table-column prop="user_num" label="可创建用户数" />
+        <el-table-column prop="use_able" label="状态">
+          <template #default="scope">
+            <el-switch v-model="scope.row.use_able" inline-prompt active-text="启用" inactive-text="禁用" @change="switchUseAble($event, scope.row)" />
+          </template>
+        </el-table-column>
+        <el-table-column prop="remark" label="套餐说明" />
+        <el-table-column label="操作">
+          <template #default="scope">
+            <el-button size="default" v-if="checkPermission(['warehouse:update'])" @click="handleEditWarehouse(scope.$index, scope.row)">编辑</el-button>
+          </template>
+        </el-table-column>
+      </template>
+      <template #pagination>
+        <el-pagination
+          v-if="tableData.records && tableData.records.length"
+          @size-change="handlePageSizeChange"
+          @current-change="handlePageCurrentChange"
+          :current-page="tableData.page"
+          :page-sizes="[10, 20, 50, 100]"
+          :page-size="tableData.size"
+          layout="total, sizes, prev, pager, next, jumper"
+          :total="tableData.total"
+        >
+        </el-pagination>
+      </template>
+    </en-table-layout>
+
+    <el-dialog
+      :title="packageForm.id ? '编辑套餐' : '添加套餐'"
+      v-model="dialogVisible"
+      width="500px"
+      @open="handleDialogOpen"
+      :modal-append-to-body="false"
+      :close-on-click-modal="false"
+      :close-on-press-escape="false"
+    >
+      <el-form :model="packageForm" :rules="stockRules" ref="packageFormRef" label-width="120px">
+        <el-form-item label="权限:" prop="role_id">
+          <el-select v-model="packageForm.role_id" clearable placeholder="请选择权限">
+            <el-option v-for="item in roleArray" :key="item.role_id" :label="item.role_name" :value="item.role_id" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="套餐名称:" prop="name">
+          <el-input v-model="packageForm.name" :minlength="2" :maxlength="20" clearable placeholder="请输入套餐名称"></el-input>
+        </el-form-item>
+        <el-form-item label="套餐金额:" prop="money">
+          <el-input v-model="packageForm.money" :minlength="2" :maxlength="20" clearable placeholder="请输入套餐金额"></el-input>
+        </el-form-item>
+        <el-form-item label="购买天数:" prop="day">
+          <el-input v-model="packageForm.day" :minlength="2" :maxlength="20" clearable placeholder="请输入套餐天数"></el-input>
+        </el-form-item>
+        <el-form-item label="可创建用户数:" prop="user_num">
+          <el-input v-model="packageForm.user_num" :minlength="2" :maxlength="20" clearable placeholder="请输入可创建用户数"></el-input>
+        </el-form-item>
+        <el-form-item label="状态:" prop="use_able">
+          <el-switch v-model="packageForm.use_able" inline-prompt active-text="启用" inactive-text="禁用" />
+        </el-form-item>
+        <el-form-item label="套餐说明:" prop="remark">
+          <el-input v-model="packageForm.remark" type="textarea" :autosize="{ minRows: 3, maxRows: 4 }" placeholder="请输入"></el-input>
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="dialogVisible = false">取 消</el-button>
+          <el-button type="primary" @click="submitPackageForm">确 定</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+  
+  <script lang="ts" setup>
+import { ref, onMounted, reactive } from 'vue';
+import { useStore } from 'vuex';
+import { ElMessageBox, FormInstance, ElMessage } from 'element-plus';
+import * as API_BasicSetting from '@/api/basicSetting';
+import * as API_Setting from '@/api/setting';
+import * as API_Package from '@/api/package';
+import Storage from '@/utils/storage';
+import router from '@/router';
+
+const store = useStore();
+const tableData = ref<any>({});
+const loading = ref(false);
+const dialogVisible = ref(false);
+const packageForm = ref<any>({
+  role_id: '',
+  name: '',
+  money: '',
+  day: '',
+  use_able: false,
+  remark: '',
+});
+const stockRules = ref<any>({
+  role_id: [{ required: true, message: '请选择权限!', trigger: 'change' }],
+  name: [{ required: true, message: '请输入套餐名称', trigger: 'blur' }],
+  money: [{ required: true, message: '请输入套餐金额', trigger: 'blur' }],
+  day: [{ required: true, message: '请输入套餐天数', trigger: 'blur' }],
+  use_able: [{ required: true, message: '请选择状态', trigger: 'change' }],
+  user_num: [{ required: true, message: '请输入可创建用户数', trigger: 'blur' }],
+});
+const marketingList = ref<any[]>([]);
+
+const params = ref({
+  data: [],
+  page_no: 1,
+  page_size: 10,
+});
+const roleArray = ref<any[]>([]);
+
+const packageFormRef = ref();
+const checkPermission = (permissions: string[]): boolean => {
+  return true;
+};
+/* 启用禁用 */
+const switchUseAble = (event, row) => {
+  API_Package.switchPackage(row.id).then((res) => {
+    GET_PackageList();
+  });
+};
+/* 打开新增窗口 */
+const handleAddWarehouse = () => {
+  packageForm.value = {};
+  dialogVisible.value = true;
+};
+/* 打开编辑窗口 */
+const handleEditWarehouse = (index: number, row: any) => {
+  packageForm.value = { ...row };
+  dialogVisible.value = true;
+};
+/* 搜索 */
+const searchForm = reactive({
+  use_able: '',
+  name: '',
+});
+const searchEvent = () => {
+  Object.assign(params.value, searchForm);
+  console.log(params);
+  params.value.page_no = 1;
+  GET_PackageList();
+};
+/* 更改条数 */
+const handlePageSizeChange = (size: number) => {
+  params.value.page_size = size;
+  GET_PackageList();
+};
+/* 更改页数 */
+const handlePageCurrentChange = (page: number) => {
+  params.value.page_no = page;
+  GET_PackageList();
+};
+
+const handleDialogOpen = () => {
+  setTimeout(() => {
+    packageFormRef.value.clearValidate();
+  });
+};
+
+/* 提交套餐表单 */
+const submitPackageForm = () => {
+  packageFormRef.value.validate((valid: boolean) => {
+    if (valid) {
+      const { id } = packageForm.value;
+      const params = { ...packageForm.value };
+      if (id) {
+        API_Package.editPackage(id, params).then(() => {
+          dialogVisible.value = false;
+          ElMessage.success('修改成功!');
+          GET_PackageList();
+        });
+      } else {
+        API_Package.submitPackage(params).then(() => {
+          dialogVisible.value = false;
+          ElMessage.success('添加成功!');
+          GET_PackageList();
+        });
+      }
+    } else {
+      ElMessage.error('表单填写有误,请核对!');
+      /*    store.dispatch('showMessage', { message: '表单填写有误,请核对!', type: 'error' }); */
+      return false;
+    }
+  });
+};
+/* 获取套餐列表 */
+const GET_PackageList = () => {
+  console.log(params);
+  loading.value = true;
+  API_Package.packageList(params.value)
+    .then((response) => {
+      loading.value = false;
+      tableData.value = response;
+    })
+    .catch(() => {
+      loading.value = false;
+    });
+};
+/* 获取权限列表 */
+const GET_RoleList = () => {
+  API_Setting.getRoles({ page_no: 1, page_size: 10000 }).then((res) => {
+    roleArray.value = res.data;
+  });
+};
+
+onMounted(() => {
+  GET_PackageList();
+  GET_RoleList();
+});
+</script>

+ 112 - 0
src/views/setting/roleManage.vue

@@ -0,0 +1,112 @@
+<template>
+  <en-table-layout :tableData="tableData.data" :loading="loading">
+    <template #toolbar>
+      <div class="inner-toolbar w-11/12 flex justify-between">
+        <div class="toolbar-btns">
+          <el-button size="mini" type="primary" icon="el-icon-circle-plus-outline" v-if="checkPermission(['roleManage:add'])" @click="handleAddRole"
+            >添加</el-button
+          >
+        </div>
+        <div class="toolbar-search">
+          <en-table-search @search="searchEvent" placeholder="请输入关键字" />
+        </div>
+      </div>
+    </template>
+
+    <template #table-columns>
+      <el-table-column prop="role_name" label="角色名称" />
+      <el-table-column prop="role_describe" label="角色描述" />
+      <el-table-column label="操作">
+        <template #default="scope">
+          <el-button size="mini" type="primary" @click="handleEditRole(scope.row)" v-if="checkPermission(['roleManage:edit'])">编辑</el-button>
+          <el-button size="mini" type="danger" @click="handleDeleteRole(scope.row)" v-if="checkPermission(['roleManage:del'])">删除</el-button>
+        </template>
+      </el-table-column>
+    </template>
+
+    <template #pagination>
+      <el-pagination
+        v-if="tableData"
+        @size-change="handlePageSizeChange"
+        @current-change="handlePageCurrentChange"
+        :current-page="tableData.page_no"
+        :page-sizes="[10, 20, 50, 100]"
+        :page-size="tableData.page_size"
+        layout="total, sizes, prev, pager, next, jumper"
+        :total="tableData.data_total"
+      >
+      </el-pagination>
+    </template>
+  </en-table-layout>
+</template>
+  
+  <script lang="ts" setup>
+import { ref, onMounted } from 'vue';
+import { useRouter } from 'vue-router';
+import { ElMessage, ElMessageBox } from 'element-plus';
+import * as API_Auth from '@/api/auth';
+
+const router = useRouter();
+const loading = ref(false);
+const params = ref({
+  page_no: 1,
+  page_size: 10,
+  keyword: '',
+});
+const tableData = ref<any>({});
+
+const checkPermission = (permissions: string[]): boolean => {
+  return true;
+};
+
+const handlePageSizeChange = (size: number) => {
+  params.value.page_size = size;
+  GET_RoleList();
+};
+
+const handlePageCurrentChange = (page: number) => {
+  params.value.page_no = page;
+  GET_RoleList();
+};
+
+const handleAddRole = () => {
+  //router.push({ name: 'roleManageAdd', params: { id: 0, callback: GET_RoleList } });
+};
+
+const handleEditRole = (row: any) => {
+  //router.push({ name: 'roleManageEdit', params: { id: row.role_id, callback: GET_RoleList } });
+};
+
+const handleDeleteRole = (row: any) => {
+  ElMessageBox.confirm('确定要删除这个角色吗?', '提示', { type: 'warning' })
+    .then(() => {
+      API_Auth.deleteRole(row.role_id).then(() => {
+        ElMessage.success('删除成功!');
+        GET_RoleList();
+      });
+    })
+    .catch(() => {});
+};
+
+const searchEvent = (data: string) => {
+  params.value.keyword = data;
+  params.value.page_no = 1;
+  GET_RoleList();
+};
+
+const GET_RoleList = () => {
+  loading.value = true;
+  API_Auth.getRoleList(params.value)
+    .then((response) => {
+      loading.value = false;
+      tableData.value = response;
+    })
+    .catch(() => {
+      loading.value = false;
+    });
+};
+
+onMounted(() => {
+  GET_RoleList();
+});
+</script>