Explorar o código

添加公司分页及对接仓库 库位接口

yangg hai 11 meses
pai
achega
f558c8d496

+ 9 - 0
src/api/company.js

@@ -54,4 +54,13 @@ export function companyPackage(id) {
     method: 'get',
     loaidng: false,
   })
+}
+
+/* 获取公司详情 /admin/erp/company/getOne/{id} */
+
+export function getOne(id) {
+  return request({
+    url: `/admin/erp/company/getOne/${id}`,
+    method: 'get',
+  })
 }

+ 39 - 0
src/api/goods.js

@@ -0,0 +1,39 @@
+import request from '@/utils/request'
+
+/*新增套餐*/
+export function addProduct(parmas) {
+    return request({
+      url: `/admin/erp/product/addProduct`,
+      method: 'post',
+      headers: { 'Content-Type': 'application/json' },
+      data: parmas
+    })
+  }
+
+  /*修改套餐 */
+export function editProduct(id,parmas) {
+    return request({
+      url: `/admin/erp/product/updateProduct/${id}`,
+      method: 'post',
+      headers: { 'Content-Type': 'application/json' },
+      data: parmas
+    })
+  }
+
+  /*商品列表 */
+  export function productList(parmas) {
+    return request({
+      url: '/admin/erp/product',
+      method: 'get',
+     parmas
+    })
+  }
+
+  /*  删除商品*/
+  export function delProduct(id) {
+    return request({
+      url: `/admin/erp/product/deletedProduct/${id}`,
+      method: 'post',
+      headers: { 'Content-Type': 'application/json' },
+    })
+  }

+ 4 - 4
src/api/setting.js

@@ -102,12 +102,12 @@ export function getDeptList() {
 }
 
 /**
- * 添加部门
+ * 添加部门?region=${parmas.region}
  * @param params
  */
 export function addDept(parmas) {
   return request({
-    url: `admin/systems/dept/create?region=${parmas.region}`,
+    url: `admin/systems/dept/create`,
     method: 'post',
     headers: { 'Content-Type': 'application/json' },
     data: parmas
@@ -115,13 +115,13 @@ export function addDept(parmas) {
 }
 
 /**
- * 更新部门
+ * 更新部门?region=${parmas.region}
  * @param id
  * @param parmas
  */
 export function editDept(parmas) {
   return request({
-    url: `admin/systems/dept/update?region=${parmas.region}`,
+    url: `admin/systems/dept/update`,
     method: 'put',
     headers: { 'Content-Type': 'application/json' },
     data: parmas

+ 6 - 0
src/components/layout/Header.vue

@@ -66,6 +66,7 @@
               <icon-search class="w-4.5 h-4.5 mx-auto dark:text-[#d0d2d6]" />
             </button>
           </div>
+          <a href="javascript:;" v-if="superMenu=='super'" @click="getBranch('super')"> 返回</a>
           <div>
             <a
               href="javascript:;"
@@ -92,6 +93,7 @@
               <!-- <icon-laptop /> -->
               <icon-sun />
             </a>
+           
           </div>
 
           <!-- <div class="dropdown shrink-0">
@@ -260,6 +262,7 @@ const changeLanguage = (item: any) => {
   i18n.locale = item.code;
   appSetting.toggleLanguage(item);
 };
+const superMenu = computed(() => store.state.superMenu);
 const currentFlag = computed(() => {
   return `/assets/images/flags/${i18n.locale.toUpperCase()}.svg`;
 });
@@ -382,4 +385,7 @@ const handleLogoutClick = () => {
       /* Handle cancel action if needed */
     });
 };
+const getBranch=(value)=>{
+  store.dispatch('toggleSuperMenu',value);
+}
 </script>

+ 212 - 7
src/components/layout/Sidebar.vue

@@ -21,14 +21,14 @@
           </el-menu>
         </el-scrollbar> -->
         <perfect-scrollbar
-          v-if="superMenu=='super'"
+          v-if="superMenu == 'super'"
           :options="{
             swipeEasing: true,
             wheelPropagation: false,
           }"
           class="h-[calc(100vh-80px)] relative"
         >
-           <ul class="relative font-semibold space-y-0.5 p-4 py-0">
+          <ul class="relative font-semibold space-y-0.5 p-4 py-0">
             <li class="menu nav-item">
               <button
                 type="button"
@@ -87,7 +87,7 @@
                   <li>
                     <router-link to="/package/packageList" @click="toggleMobileMenu">{{ $t('packageList') }}</router-link>
                   </li>
-                   <li>
+                  <li>
                     <router-link to="/package/packagePermissions" @click="toggleMobileMenu">{{ $t('packagePermissions') }}</router-link>
                   </li>
                 </ul>
@@ -123,7 +123,7 @@
                   <li>
                     <router-link to="/templateManage/accountTemplate" @click="toggleMobileMenu">{{ $t('accountTemplate') }}</router-link>
                   </li>
-                   <li>
+                  <li>
                     <router-link to="/templateManage/systemMessage" @click="toggleMobileMenu">{{ $t('systemMessage') }}</router-link>
                   </li>
                 </ul>
@@ -151,14 +151,21 @@
               <vue-collapsible :isOpen="activeDropdown === 'users'">
                 <ul class="sub-menu text-gray-500">
                   <li>
-                    <router-link to="/setting/company" @click="toggleMobileMenu">{{ $t('company') }}</router-link>
+                    <router-link to="/setting/companyList" @click="toggleMobileMenu">{{ $t('companyList') }}</router-link>
                   </li>
                   <li>
                     <router-link to="/setting/roleManage" @click="toggleMobileMenu">{{ $t('roleManage') }}</router-link>
                   </li>
                   <li>
-                    <router-link to="/setting/branch" @click="toggleMobileMenu">{{ $t('branch') }}</router-link>
+                    <router-link to="/setting/dicData" @click="toggleMobileMenu">{{ $t('dicData') }}</router-link>
+                  </li>
+                  <li>
+                    <router-link to="/setting/menuManage" @click="toggleMobileMenu">{{ $t('menuManage') }}</router-link>
+                  </li>
+                  <li>
+                    <router-link to="/setting/systemLog" @click="toggleMobileMenu">{{ $t('systemLog') }}</router-link>
                   </li>
+                  <!-- systemLog -->
                 </ul>
               </vue-collapsible>
               <vue-collapsible :isOpen="activeDropdown === 'users'">
@@ -167,6 +174,205 @@
             </li>
           </ul>
         </perfect-scrollbar>
+        <perfect-scrollbar
+          v-if="superMenu == 'compan'"
+          :options="{
+            swipeEasing: true,
+            wheelPropagation: false,
+          }"
+          class="h-[calc(100vh-80px)] relative"
+        >
+          <ul class="relative font-semibold space-y-0.5 p-4 py-0">
+            <li class="menu nav-item">
+              <button
+                type="button"
+                class="nav-link group w-full"
+                :class="{ active: activeDropdown === 'dashboard' }"
+                @click="activeDropdown === 'dashboard' ? (activeDropdown = null) : (activeDropdown = 'dashboard')"
+              >
+                <div class="flex items-center">
+                  <icon-menu-dashboard 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">
+                    {{ $t('warehouse') }}
+                  </span>
+                </div>
+                <div :class="{ 'rtl:rotate-90 -rotate-90': activeDropdown == 'dashboard' }">
+                  <icon-caret-down />
+                </div>
+              </button>
+              <vue-collapsible :isOpen="activeDropdown === 'dashboard'">
+                <ul class="sub-menu text-gray-500">
+                  <li>
+                    <router-link to="/stock/warehouseList" @click="toggleMobileMenu">{{ $t('warehouseList') }}</router-link>
+                  </li>
+                  <li>
+                    <router-link to="/stock/warehouseLocation" @click="toggleMobileMenu">{{ $t('warehouseLocation') }}</router-link>
+                  </li>
+                  <li>
+                    <router-link to="/stock/goodsLend" @click="toggleMobileMenu">{{ $t('goodsLend') }}</router-link>
+                  </li>
+                  <li>
+                    <router-link to="/stock/goodsTransfer" @click="toggleMobileMenu">{{ $t('goodsTransfer') }}</router-link>
+                  </li>
+                  <li>
+                    <router-link to="/stock/goodsExchange" @click="toggleMobileMenu">{{ $t('goodsExchange') }}</router-link>
+                  </li>
+                  <li>
+                    <router-link to="/stock/goodsOutBound" @click="toggleMobileMenu">{{ $t('goodsOutBound') }}</router-link>
+                  </li>
+                </ul>
+              </vue-collapsible>
+            </li>
+            <li class="menu nav-item">
+              <button
+                type="button"
+                class="nav-link group w-full"
+                :class="{ active: activeDropdown === 'companyMent' }"
+                @click="activeDropdown === 'companyMent' ? (activeDropdown = null) : (activeDropdown = 'companyMent')"
+              >
+                <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">{{ $t('companyMent') }}</span>
+                </div>
+                <div :class="{ 'rtl:rotate-90 -rotate-90': activeDropdown == 'companyMent' }">
+                  <icon-caret-down />
+                </div>
+              </button>
+              <vue-collapsible :isOpen="activeDropdown === 'companyMent'">
+                <ul class="sub-menu text-gray-500">
+                  <li>
+                    <router-link to="/companyMent/administratorManage" @click="toggleMobileMenu">{{ $t('administratorManage') }}</router-link>
+                  </li>
+                  <li>
+                    <router-link to="/setting/roleManage" @click="toggleMobileMenu">{{ $t('roleManage') }}</router-link>
+                  </li>
+                  <li>
+                    <router-link to="/companyMent/branch" @click="toggleMobileMenu">{{ $t('branch') }}</router-link>
+                  </li>
+                  <li>
+                    <router-link to="/companyMent/companyInfo" @click="toggleMobileMenu">{{ $t('companyInfo') }}</router-link>
+                  </li>
+                </ul>
+                <!-- companyInfo -->
+              </vue-collapsible>
+              <vue-collapsible :isOpen="activeDropdown === 'companyMent'">
+                <ul class="sub-menu text-gray-500"></ul>
+              </vue-collapsible>
+            </li>
+            <li class="menu nav-item">
+              <button
+                type="button"
+                class="nav-link group w-full"
+                :class="{ active: activeDropdown === 'goods' }"
+                @click="activeDropdown === 'goods' ? (activeDropdown = null) : (activeDropdown = 'goods')"
+              >
+                <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">{{ $t('goods') }}</span>
+                </div>
+                <div :class="{ 'rtl:rotate-90 -rotate-90': activeDropdown == 'goods' }">
+                  <icon-caret-down />
+                </div>
+              </button>
+              <vue-collapsible :isOpen="activeDropdown === 'goods'">
+                <ul class="sub-menu text-gray-500">
+                  <li>
+                    <router-link to="/goods/SKUlist" @click="toggleMobileMenu">{{ $t('SKUList') }}</router-link>
+                  </li>
+                  <!--   <li>
+                    <router-link to="/goods/emailtemplate" @click="toggleMobileMenu">{{ $t('emailtemplate') }}</router-link>
+                  </li>
+                  <li>
+                    <router-link to="/goods/accountTemplate" @click="toggleMobileMenu">{{ $t('accountTemplate') }}</router-link>
+                  </li>
+                  <li>
+                    <router-link to="/goods/systemMessage" @click="toggleMobileMenu">{{ $t('systemMessage') }}</router-link>
+                  </li> -->
+                </ul>
+              </vue-collapsible>
+              <vue-collapsible :isOpen="activeDropdown === 'goods'">
+                <ul class="sub-menu text-gray-500"></ul>
+              </vue-collapsible>
+            </li>
+            <!--   <li class="menu nav-item">
+              <button
+                type="button"
+                class="nav-link group w-full"
+                :class="{ active: activeDropdown === 'templateManage' }"
+                @click="activeDropdown === 'templateManage' ? (activeDropdown = null) : (activeDropdown = 'templateManage')"
+              >
+                <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">{{ $t('templateManage') }}</span>
+                </div>
+                <div :class="{ 'rtl:rotate-90 -rotate-90': activeDropdown == 'templateManage' }">
+                  <icon-caret-down />
+                </div>
+              </button>
+              <vue-collapsible :isOpen="activeDropdown === 'templateManage'">
+                <ul class="sub-menu text-gray-500">
+                  <li>
+                    <router-link to="/templateManage/smsTemplate" @click="toggleMobileMenu">{{ $t('smsTemplate') }}</router-link>
+                  </li>
+                  <li>
+                    <router-link to="/templateManage/emailtemplate" @click="toggleMobileMenu">{{ $t('emailtemplate') }}</router-link>
+                  </li>
+                  <li>
+                    <router-link to="/templateManage/accountTemplate" @click="toggleMobileMenu">{{ $t('accountTemplate') }}</router-link>
+                  </li>
+                  <li>
+                    <router-link to="/templateManage/systemMessage" @click="toggleMobileMenu">{{ $t('systemMessage') }}</router-link>
+                  </li>
+                </ul>
+              </vue-collapsible>
+              <vue-collapsible :isOpen="activeDropdown === 'package'">
+                <ul class="sub-menu text-gray-500"></ul>
+              </vue-collapsible>
+            </li>
+            <li class="menu nav-item">
+              <button
+                type="button"
+                class="nav-link group w-full"
+                :class="{ active: activeDropdown === 'users' }"
+                @click="activeDropdown === 'users' ? (activeDropdown = null) : (activeDropdown = 'users')"
+              >
+                <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">{{ $t('setting') }}</span>
+                </div>
+                <div :class="{ 'rtl:rotate-90 -rotate-90': activeDropdown == 'users' }">
+                  <icon-caret-down />
+                </div>
+              </button>
+              <vue-collapsible :isOpen="activeDropdown === 'users'">
+                <ul class="sub-menu text-gray-500">
+                  <li>
+                    <router-link to="/setting/companyList" @click="toggleMobileMenu">{{ $t('companyList') }}</router-link>
+                  </li>
+                  <li>
+                    <router-link to="/setting/roleManage" @click="toggleMobileMenu">{{ $t('roleManage') }}</router-link>
+                  </li>
+                  <li>
+                    <router-link to="/setting/dicData" @click="toggleMobileMenu">{{ $t('dicData') }}</router-link>
+                  </li>
+                  <li>
+                    <router-link to="/setting/menuManage" @click="toggleMobileMenu">{{ $t('menuManage') }}</router-link>
+                  </li>
+                  <li>
+                    <router-link to="/setting/systemLog" @click="toggleMobileMenu">{{ $t('systemLog') }}</router-link>
+                  </li>
+                </ul>
+              </vue-collapsible>
+              <vue-collapsible :isOpen="activeDropdown === 'users'">
+                <ul class="sub-menu text-gray-500"></ul>
+              </vue-collapsible>
+            </li> -->
+          </ul>
+        </perfect-scrollbar>
       </div>
     </nav>
   </div>
@@ -211,7 +417,6 @@ const permissionRouters = computed(() => store.getters.permission_routers);
 const sidebar = computed(() => store.getters.sidebar);
 const siteInfo = computed(() => store.getters.siteInfo);
 const superMenu = computed(() => store.state.superMenu);
-console.log("superMenu:",superMenu);
 const isCollapse = computed(() => !sidebar.value.opened);
 onMounted(() => {
   const selector = document.querySelector('.sidebar ul a[href="' + window.location.pathname + '"]');

+ 33 - 43
src/composables/ui-utils/RegExp.js

@@ -7,61 +7,51 @@
  * money    金钱
  * TINumber 纳税识别号
  * IDCard   身份证
- * userName 账户名称【汉字、字母、数字、“-”、“_”的组合】
+ * userName 账户名称【汉字、字母、数字、"-"、"_"的组合】
  * URL      URL
  * TEL      固定电话
  */
 
-// 手机号
-let mobile = /^0?(13[0-9]|14[0-9]|15[0-9]|16[0-9]|17[0-9]|18[0-9]|19[0-9])[0-9]{8}$/
+export const RegExp = {
+  // 手机号
+  mobile: /^0?(13[0-9]|14[0-9]|15[0-9]|16[0-9]|17[0-9]|18[0-9]|19[0-9])[0-9]{8}$/,
 
-// 电子邮箱
-let email = /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/
+  // 电子邮箱
+  email: /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/,
 
-// 密码【6-20位 包括特殊字符】
-let password = /^[A-Za-z0-9!#$%^&*.~,]{6,20}$/
+  // 密码【6-20位 包括特殊字符】
+  password: /^[A-Za-z0-9!#$%^&*.~,]{6,20}$/,
 
-// 正整数【不包含0】
-let integer = /^[1-9]\d*$/
+  // 正整数【不包含0】
+  integer: /^[1-9]\d*$/,
 
-// 正整数【包含0】
-let positiveInteger = /^[0-9]\d*$/
+  // 正整数【包含0】
+  positiveInteger: /^[0-9]\d*$/,
 
-// 金钱
-let money = /(^[1-9]([0-9]+)?(\.[0-9]{1,2})?$)|(^(0){1}$)|(^[0-9]\.[0-9]([0-9])?$)/
+  // 金钱
+  money: /(^[1-9]([0-9]+)?(\.[0-9]{1,2})?$)|(^(0){1}$)|(^[0-9]\.[0-9]([0-9])?$)/,
 
-// 纳税识别号
-// let TINumber = /^\d{15}$|^\d{18}$|^\d{20}$/
-let TINumber = /(^[a-zA-Z0-9]{15}$)|(^[a-zA-Z0-9]{18}$)|(^[a-zA-Z0-9]{20}$)/
+  // 纳税识别号
+  TINumber: /(^[a-zA-Z0-9]{15}$)|(^[a-zA-Z0-9]{18}$)|(^[a-zA-Z0-9]{20}$)/,
 
-// 身份证
-let IDCard = /^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$|^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/
+  // 身份证
+  IDCard: /^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$|^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/,
 
-// 账户名称【汉字、字母、数字、“-”、“_”的组合】以其开头并且匹配一个或任意多个
-let userName = /^[A-Za-z0-9_\-\u4e00-\u9fa5]+$/
+  // 账户名称【汉字、字母、数字、"-"、"_"的组合】以其开头并且匹配一个或任意多个
+  userName: /^[A-Za-z0-9_\-\u4e00-\u9fa5]+$/,
 
-// URL
-let URL = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|work|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|wang|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/
+  // URL
+  URL: /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|work|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|wang|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/,
 
-// 固话
-let TEL = /0\d{2,3}-\d{7,8}/
-//字母和数字
-let code=/^[A-Za-z0-9]+$/
-/* 禁止输入空格 */
-let reg=/^\S*$/
+  // 固话
+  TEL: /0\d{2,3}-\d{7,8}/,
 
-module.exports = {
-  mobile: mobile,
-  email: email,
-  password: password,
-  integer: integer,
-  positiveInteger: positiveInteger,
-  money: money,
-  TINumber: TINumber,
-  IDCard: IDCard,
-  userName: userName,
-  URL: URL,
-  TEL: TEL,
-  code:code,
-  reg:reg
-}
+  //字母和数字
+  code: /^[A-Za-z0-9]+$/,
+
+  /* 禁止输入空格 */
+  reg: /^\S*$/
+};
+
+// 如果需要默认导出,可以添加以下行
+// export default RegExp;

+ 11 - 1
src/locales/zh.json

@@ -7,8 +7,11 @@
     "goodsOutBound":"出库管理",
        
     "setting":"系统管理",
-    "company":"公司管理",
+    "companyList":"公司列表",
     "roleManage":"权限管理",
+    "dicData":"数据字典",
+    "menuManage":"菜单管理",
+    "systemLog":"日志管理",
     "branch":"部门管理",
     "package":"套餐管理",
     "packageList":"套餐列表",
@@ -19,6 +22,13 @@
     "emailtemplate":"邮件模板",
     "accountTemplate":"公众号模板",
 
+    "companyMent":"公司管理",
+    "administratorManage":"人员管理",
+    "companyInfo":"公司详情",
+    "goods":"商品管理",
+    "SKUList":"商品列表",
+    "warehouseLocation":"库位区域",
+
     "sales": "销售量",
     "analytics": "分析",
     "apps": "应用",

+ 15 - 1
src/main.ts

@@ -70,13 +70,14 @@ app.use(ElementPlus)
 /* 全局引入 */
 import './permission'
 
+
 import store from './stores';
 app.use(store);
 // 如果您正在使用CDN引入,请删除下面一行。
 import * as ElementPlusIconsVue from '@element-plus/icons-vue'
 for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
     app.component(key, component)
-  }
+}
 
 app.config.globalProperties.checkPermission = (value: any): boolean => {
     if (value && Array.isArray(value) && value.length > 0) {
@@ -96,4 +97,17 @@ app.config.globalProperties.checkPermission = (value: any): boolean => {
 import globalComponents from '../src/composables/global-components'; // 引入全局组件插件
 app.use(globalComponents); // 使用全局组件插件
 
+app.config.globalProperties.checkPermission = (value: string[]): boolean => {
+    if (value && Array.isArray(value) && value.length > 0) {
+        const permissions = store.getters.routersAlls;
+        const permissionDatas = value;
+        const all_permission = '*:*:*';
+        const hasPermission = permissions.some((permission: string) => {
+            return all_permission === permission || permissionDatas.includes(permission);
+        });
+        return !!hasPermission;
+    } else {
+        return false;
+    }
+};
 app.mount('#app');

+ 35 - 9
src/router/index.ts

@@ -4,7 +4,10 @@ import appSetting from '@/app-setting';
 import HomeView from '../views/index.vue';
 
 const constantRouterMap: RouteRecordRaw[] = [
+    /* platformLogin */
     { path: '/login', component: () => import('@/views/auth/boxed-signin.vue'), meta: { layout: 'auth' } },
+    { path: '/platformLogin', component: () => import('@/views/auth/platformLogin.vue'), meta: { layout: 'auth' } },
+    
 ];
 
 const router = createRouter({
@@ -27,11 +30,12 @@ const asyncRouterMap: RouteRecordRaw[] = [
         redirect: '/stock',
         name: 'stock',
         children: [
-            { path: 'warehouseList', component: () => import('@/views/stock/warehouseList.vue'), name: 'warehouseList', meta: { title: 'warehouseList', layout: 'app' }, hidden: true },
-            { path: 'goodsLend', component: () => import('@/views/stock/goodsLend.vue'), name: 'goodsLend', meta: { title: 'goodsLend', layout: 'app' }, hidden: true },
-            { path: 'goodsTransfer', component: () => import('@/views/stock/goodsTransfer.vue'), name: 'goodsTransfer', meta: { title: 'goodsTransfer', layout: 'app' }, hidden: true },
-            { path: 'goodsExchange', component: () => import('@/views/stock/warehousing.vue'), name: 'goodsExchange', meta: { title: 'goodsExchange' }, hidden: true },
-            { path: 'goodsOutBound', component: () => import('@/views/stock/outbound.vue'), name: 'goodsOutBound', meta: { title: 'goodsOutBound' }, hidden: true },
+            { 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: 'warehouseLocation', component: () => import('@/views/stock/warehouseLocation.vue'), name: 'warehouseLocation', meta: { title: 'warehouseLocation' ,layout: 'app'} },
         ]
     },
     {
@@ -65,10 +69,32 @@ const asyncRouterMap: RouteRecordRaw[] = [
             icon: 'setting-manage'
         },
         children: [
-            { 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' }, hidden: true },
-            { path: 'roleManageAdd', component: () => import('@/views/setting/roleManageAdd.vue'), name: 'roleManageAdd', meta: { title: 'roleManageAdd', layout: 'app' }, hidden: true },
-            { path: 'branch', component: () => import('@/views/setting/branch.vue'), name: 'branch', meta: { title: 'branch', layout: 'app' }, hidden: true },
+            { path: 'companyList', component: () => import('@/views/setting/companyList.vue'), name: 'companyList', meta: { title: 'companyList', layout: 'app' } },
+            { path: 'roleManage', component: () => import('@/views/setting/roleManage.vue'), name: 'roleManage', meta: { title: 'roleManage', layout: 'app' } },
+            { path: 'roleManageAdd', component: () => import('@/views/setting/roleManageAdd.vue'), name: 'roleManageAdd', meta: { title: 'roleManageAdd', layout: 'app' } },
+            { path: 'dicData', component: () => import('@/views/setting/dicData.vue'), name: 'dicData', meta: { title: 'dicData', layout: 'app' } },
+            { path: 'menuManage', component: () => import('@/views/setting/menuManage.vue'), name: 'menuManage', meta: { title: 'menuManage', layout: 'app' } },
+            { path: 'systemLog', component: () => import('@/views/setting/systemLog.vue'), name: 'systemLog', meta: { title: 'systemLog', layout: 'app' } },
+        ]
+    },
+    {
+        path: '/companyMent',
+        redirect: '/companyMent',
+        name: 'companyMent',
+        children: [
+            { path: 'administratorManage', component: () => import('@/views/companyMent/administratorManage.vue'), name: 'administratorManage', meta: { title: 'administratorManage', layout: 'app' } },
+            { path: 'branch', component: () => import('@/views/companyMent/branch.vue'), name: 'branch', meta: { title: 'branch', layout: 'app' } },
+           { path: 'companyInfo', component: () => import('@/views/companyMent/companyInfo.vue'), name: 'companyInfo', meta: { title: 'companyInfo', layout: 'app' } },
+        ]
+    },
+    {
+        path: '/goods',
+        redirect: '/goods',
+        name: 'goods',
+        children: [
+            { path: 'SKUList', component: () => import('@/views/goods/SKUList.vue'), name: 'SKUList', meta: { title: 'SKUList', layout: 'app' } },
+           /*  { path: 'branch', component: () => import('@/views/companyMent/branch.vue'), name: 'branch', meta: { title: 'branch', layout: 'app' } },
+           { path: 'companyInfo', component: () => import('@/views/companyMent/companyInfo.vue'), name: 'companyInfo', meta: { title: 'companyInfo', layout: 'app' } }, */
         ]
     },
 ];

+ 70 - 31
src/views/auth/boxed-signin.vue

@@ -11,12 +11,47 @@
         class="relative w-full max-w-[870px] rounded-md bg-[linear-gradient(45deg,#fff9f9_0%,rgba(255,255,255,0)_25%,rgba(255,255,255,0)_75%,_#fff9f9_100%)] p-2 dark:bg-[linear-gradient(52.22deg,#0E1726_0%,rgba(14,23,38,0)_18.66%,rgba(14,23,38,0)_51.04%,rgba(14,23,38,0)_80.07%,#0E1726_100%)]"
       >
         <div class="relative flex flex-col justify-center rounded-md bg-white/60 backdrop-blur-lg dark:bg-black/50 px-6 lg:min-h-[758px] py-20">
+          <div class="absolute top-6 start-6">
+            <el-button type="plain" text @click="typeLogin"> PlatForm </el-button>
+          </div>
           <div class="absolute top-6 end-6">
-            <div class="dropdown"></div>
+            <div class="dropdown">
+              <Popper :placement="store.state.rtlClass === 'rtl' ? 'bottom-start' : 'bottom-end'" offsetDistance="8">
+                <button
+                  type="button"
+                  class="flex items-center gap-2.5 rounded-lg border border-white-dark/30 bg-white px-2 py-1.5 text-white-dark hover:border-primary hover:text-primary dark:bg-black"
+                >
+                  <div>
+                    <img :src="currentFlag" alt="image" class="h-5 w-5 rounded-full object-cover" />
+                  </div>
+                  <div class="text-base font-bold uppercase">{{ store.state.locale }}</div>
+                  <span class="shrink-0">
+                    <icon-caret-down />
+                  </span>
+                </button>
+                <template #content="{ close }">
+                  <ul class="!px-2 text-dark dark:text-white-dark grid grid-cols-2 gap-2 font-semibold dark:text-white-light/90 w-[280px]">
+                    <template v-for="item in store.state.languageList" :key="item.code">
+                      <li>
+                        <button
+                          type="button"
+                          class="w-full hover:text-primary"
+                          :class="{ 'bg-primary/10 text-primary': i18n.locale === item.code }"
+                          @click="changeLanguage(item), close()"
+                        >
+                          <img class="w-5 h-5 object-cover rounded-full" :src="`/assets/images/flags/${item.code.toUpperCase()}.svg`" alt="" />
+                          <span class="ltr:ml-3 rtl:mr-3">{{ item.name }}</span>
+                        </button>
+                      </li>
+                    </template>
+                  </ul>
+                </template>
+              </Popper>
+            </div>
           </div>
           <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>
+              <h1 class="text-3xl font-extrabold uppercase !leading-snug text-primary md:text-4xl">Company Sign in</h1>
               <!--    <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('/')">
@@ -53,19 +88,16 @@
                         </form> -->
             <!-- <el-button @click="goHome">测试</el-button> -->
             <el-form :model="loginForm" :rules="loginRules" ref="loginFormRef" class="login-input" label-width="120px">
-              <el-form-item label="登录类型:" prop="type">
-                 <el-select v-model="loginForm.type"  :placeholder="$t('company_name')" style="width: 240px" >
-                  <el-option  label="公司登录" value="com" />
-                  <el-option  label="平台登录" value="sys" />
-                </el-select>
-              </el-form-item>
-              <el-form-item label="公司名称:" prop="company_id" v-if="loginForm.type=='com'">
-               <!--  <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_id" filterable :placeholder="$t('company_name')" style="width: 240px" @input="getInput" @change="getCompany">
+              <el-form-item label="公司名称:" prop="company_id" >
+                <el-select
+                  v-model="loginForm.company_id"
+                  filterable
+                  clearable
+                  :placeholder="$t('company_name')"
+                  style="width: 240px"
+                  @input="getInput"
+                  @change="getCompany"
+                >
                   <el-option v-for="item in options" :key="item.id" :label="item.name" :value="item.id" />
                 </el-select>
               </el-form-item>
@@ -150,7 +182,7 @@ const loginForm = reactive({
   scene: 'LOGIN',
   validcode: '',
   company_id: '',
-  type:'com'
+  type: 'com',
 });
 const { t } = useI18n();
 const loginRules = reactive({
@@ -191,22 +223,24 @@ const currentFlag = computed(() => {
   return `/assets/images/flags/${i18n.locale.toUpperCase()}.svg`;
 });
 
-/* 查询公司 */
-const getCompany=(value:any)=>{
-
-}
-const getInput=(val)=>{
-  searchText=val.data
-}
-let searchText=ref('')
-const companyList=()=>{
-  API_common.getCompany(searchText.value).then(res=>{
-    options.value=res
-  })
+const typeLogin=()=>{
+  router.push({ path: '/platformLogin' })
 }
 
+/* 查询公司 */
+const getCompany = (value: any) => {};
+const getInput = (val) => {
+  searchText = val.data;
+};
+let searchText = ref('');
+const companyList = () => {
+  API_common.getCompany(searchText.value).then((res) => {
+    options.value = res;
+  });
+};
+
 onMounted(() => {
-  companyList()
+  companyList();
   const uuids = Storage.getItem('admin_uuid');
   if (uuids) {
     uuid = uuids;
@@ -226,7 +260,7 @@ onMounted(() => {
       accountAfsSuccess.value = true;
     }
   });
- // companyList()
+  // companyList()
 });
 
 /** 阿里云滑动验证 通过后的回调函数 **/
@@ -266,6 +300,12 @@ const dispatchLoginAction = (params) => {
         const forward = route.query.forward;
         let query = JSON.parse(JSON.stringify(route.query));
         delete query.forward;
+        console.log(params);
+        if (params.type == 'com') {
+          store.dispatch('toggleSuperMenu', 'compan');
+        } else {
+          store.dispatch('toggleSuperMenu', 'super');
+        }
         router.push({ path: 'dashboard', query }).catch(() => {}); //forward ||
       })
       .catch(() => {
@@ -277,7 +317,6 @@ const dispatchLoginAction = (params) => {
 
 const submitLogin = (formEl: FormInstance | undefined) => {
   if (!accountAfsSuccess) return;
-  console.log(!afs);
   if (!afs) {
     const params = MixinClone(loginForm);
     params.c_sessionid = afsParam.c_sessionid;

+ 329 - 0
src/views/auth/platformLogin.vue

@@ -0,0 +1,329 @@
+<template>
+  <div>
+    <div class="absolute inset-0">
+      <img src="/assets/images/auth/bg-gradient.png" alt="image" class="h-full w-full object-cover" />
+    </div>
+
+    <div class="relative flex items-center justify-center bg-[url(/assets/images/auth/map.png)] bg-cover bg-center bg-no-repeat dark:bg-[#060818] sm:px-16">
+      <img src="/assets/images/auth/coming-soon-object3.png" alt="image" class="absolute right-0 top-0 h-[300px]" />
+
+      <div
+        class="relative w-full max-w-[870px] rounded-md bg-[linear-gradient(45deg,#fff9f9_0%,rgba(255,255,255,0)_25%,rgba(255,255,255,0)_75%,_#fff9f9_100%)] p-2 dark:bg-[linear-gradient(52.22deg,#0E1726_0%,rgba(14,23,38,0)_18.66%,rgba(14,23,38,0)_51.04%,rgba(14,23,38,0)_80.07%,#0E1726_100%)]"
+      >
+        <div class="relative flex flex-col justify-center rounded-md bg-white/60 backdrop-blur-lg dark:bg-black/50 px-6 lg:min-h-[758px] py-20">
+          <div class="absolute top-6 start-6">
+            <el-button type="plain" text @click="typeLogin"> Company </el-button>
+          </div>
+          <div class="absolute top-6 end-6">
+            <div class="dropdown">
+              <Popper :placement="store.state.rtlClass === 'rtl' ? 'bottom-start' : 'bottom-end'" offsetDistance="8">
+                <button
+                  type="button"
+                  class="flex items-center gap-2.5 rounded-lg border border-white-dark/30 bg-white px-2 py-1.5 text-white-dark hover:border-primary hover:text-primary dark:bg-black"
+                >
+                  <div>
+                    <img :src="currentFlag" alt="image" class="h-5 w-5 rounded-full object-cover" />
+                  </div>
+                  <div class="text-base font-bold uppercase">{{ store.state.locale }}</div>
+                  <span class="shrink-0">
+                    <icon-caret-down />
+                  </span>
+                </button>
+                <template #content="{ close }">
+                  <ul class="!px-2 text-dark dark:text-white-dark grid grid-cols-2 gap-2 font-semibold dark:text-white-light/90 w-[280px]">
+                    <template v-for="item in store.state.languageList" :key="item.code">
+                      <li>
+                        <button
+                          type="button"
+                          class="w-full hover:text-primary"
+                          :class="{ 'bg-primary/10 text-primary': i18n.locale === item.code }"
+                          @click="changeLanguage(item), close()"
+                        >
+                          <img class="w-5 h-5 object-cover rounded-full" :src="`/assets/images/flags/${item.code.toUpperCase()}.svg`" alt="" />
+                          <span class="ltr:ml-3 rtl:mr-3">{{ item.name }}</span>
+                        </button>
+                      </li>
+                    </template>
+                  </ul>
+                </template>
+              </Popper>
+            </div>
+          </div>
+          <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">Platform Sign in</h1>
+              <!--    <p class="text-base font-bold leading-normal text-white-dark">欢迎登录V3StudioApp</p> -->
+            </div>
+            <el-form :model="loginForm" :rules="loginRules" ref="loginFormRef" class="login-input" label-width="120px">
+              <!-- <el-form-item label="登录类型:" prop="type">
+                  <el-select v-model="loginForm.type" :placeholder="$t('company_name')" style="width: 240px">
+                    <el-option label="公司登录" value="com" />
+                    <el-option label="平台登录" value="sys" />
+                  </el-select>
+                </el-form-item> -->
+              <!--   <el-form-item label="公司名称:" prop="company_id">
+                  <el-select
+                    v-model="loginForm.company_id"
+                    filterable
+                    :placeholder="$t('company_name')"
+                    style="width: 240px"
+                    @input="getInput"
+                    @change="getCompany"
+                  >
+                    <el-option v-for="item in options" :key="item.id" :label="item.name" :value="item.id" />
+                  </el-select>
+                </el-form-item> -->
+              <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 label="密  码:" prop="password">
+                <el-input
+                  v-model="loginForm.password"
+                  type="password"
+                  autocomplete="off"
+                  :placeholder="$t('password')"
+                  :minlength="6"
+                  :maxlength="20"
+                  clearable
+                >
+                  <template v-slot:prefix>
+                    <svg-icon class="el-input__icon" icon-class="password" />
+                  </template>
+                </el-input>
+              </el-form-item>
+              <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" />
+                  </template>
+                </el-input>
+              </el-form-item>
+              <div v-else-if="validateInFO.validator_type === 'ALIYUN'" style="margin-bottom: 20px">
+                <div id="pc-valid" class="nc-container"></div>
+              </div>
+              <el-form-item>
+                <el-button type="primary" :loading="loading" :disabled="!accountAfsSuccess" class="btn" @click="submitLogin(loginFormRef)">
+                  {{ $t('logIn') }}
+                </el-button>
+              </el-form-item>
+            </el-form>
+            <!-- 新用户注册账号--暂不开放 -->
+            <!-- <div class="relative my-7 text-center md:mb-9">
+                              <span class="absolute inset-x-0 top-1/2 h-px w-full -translate-y-1/2 bg-white-light dark:bg-white-dark"></span>
+                              <span class="relative bg-white px-2 font-bold uppercase text-white-dark dark:bg-dark dark:text-white-light">or</span>
+                          </div> -->
+
+            <!-- <div class="text-center dark:text-white">
+                             新用户请注册 ?
+                              <router-link to="/auth/boxed-signup" class="uppercase text-primary underline transition hover:text-black dark:hover:text-white">
+                                  注册
+                              </router-link>
+                          </div> -->
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+  <script lang="ts" setup>
+import { computed, onMounted, reactive, ref } from 'vue';
+
+import { useI18n } from 'vue-i18n';
+import appSetting from '@/app-setting';
+import { useStore } from 'vuex';
+import { useRouter, useRoute } from 'vue-router';
+import { useMeta } from '@/composables/use-meta';
+import Storage from '@/utils/storage';
+import * as API_common from '@/api/common';
+import { v4 as uuidv4 } from 'uuid';
+import AliyunAfs from '@/composables/AliyunAfs';
+import { ElMessage, FormInstance } from 'element-plus';
+import app from '@/stores/modules/app';
+
+const loading: any = ref(false);
+
+const options: any = ref([]);
+
+const loginForm = reactive({
+  username: '',
+  password: '',
+  scene: 'LOGIN',
+  validcode: '',
+  company_id: '',
+  type: 'sys',
+});
+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_id: [{ required: true, message: t('company_name'), trigger: 'blur' }],
+});
+// 验证信息
+const validateInFO: any = ref({});
+let validcodeImg: any = ref('');
+let uuid: any = ref(Storage.getItem('admin_uuid'));
+// 阿里云验证条实例
+let afs: any = ref(null);
+// 当滑动验证成功之后返回的参数
+const afsParam: any = ref({
+  c_sessionid: '',
+  sig: '',
+  nc_token: '',
+});
+// 阿里云滑动验证是否通过 && 如果不是 阿里云验证则直接设置为 true
+const accountAfsSuccess: any = ref(false);
+// 是否为加盟商登录
+const is_franchise: any = ref(false);
+const loginFormRef = ref<FormInstance>();
+
+useMeta({ title: 'Login Boxed' });
+const router = useRouter();
+const route = useRoute();
+const store = useStore();
+// multi language
+const i18n = reactive(useI18n());
+const changeLanguage = (item: any) => {
+  i18n.locale = item.code;
+  appSetting.toggleLanguage(item);
+};
+const currentFlag = computed(() => {
+  return `/assets/images/flags/${i18n.locale.toUpperCase()}.svg`;
+});
+
+const typeLogin = () => {
+  router.push({ path: '/login' });
+};
+
+/* 查询公司 */
+const getCompany = (value: any) => {};
+const getInput = (val) => {
+  searchText = val.data;
+};
+let searchText = ref('');
+const companyList = () => {
+  API_common.getCompany(searchText.value).then((res) => {
+    options.value = res;
+  });
+};
+
+onMounted(() => {
+  companyList();
+  const uuids = Storage.getItem('admin_uuid');
+  if (uuids) {
+    uuid = uuids;
+  } else {
+    const _uuid = uuidv4();
+    uuid = _uuid;
+    Storage.setItem('admin_uuid', _uuid, { expires: 30 });
+  }
+  changeValidcode();
+  API_common.getValidator().then((res) => {
+    validateInFO.value = res;
+    const { validator_type, aliyun_afs } = res;
+    if (validator_type === 'ALIYUN') {
+      //afs = new AliyunAfs('#pc-valid', aliyun_afs.scene, aliyun_afs.app_key, initCaptcha)
+      afs.init();
+    } else {
+      accountAfsSuccess.value = true;
+    }
+  });
+  // companyList()
+});
+
+/** 阿里云滑动验证 通过后的回调函数 **/
+const initCaptcha = (data) => {
+  afsParam.c_sessionid = data.csessionid;
+  afsParam.sig = data.sig;
+  afsParam.nc_token = data.nc_token;
+  accountAfsSuccess.value = true;
+};
+
+/** 更换图片验证码 */
+const changeValidcode = () => {
+  validcodeImg.value = API_common.getValidateCodeUrl('LOGIN', uuid);
+  console.log(validcodeImg);
+};
+const MixinClone = (obj) => {
+  return JSON.parse(JSON.stringify(obj));
+};
+
+/** 登录处理 */
+const dispatchLoginAction = (params) => {
+  loading.value = true;
+  return new Promise((resolve, reject) => {
+    store
+      .dispatch('loginAction', params)
+      .then((res: any) => {
+        loading.value = false;
+        if (res) {
+          try {
+            const WS = new WebSocket(res.access_token);
+            // 使用 provide/inject 或其他方式在 Vue 3 中共享 WebSocket 实例
+            app.provide('websocket', WS);
+          } catch (error) {
+            console.log('websocket 实例化失败:', error);
+          }
+        }
+        const forward = route.query.forward;
+        let query = JSON.parse(JSON.stringify(route.query));
+        delete query.forward;
+        console.log(params);
+        if (params.type == 'com') {
+          store.dispatch('toggleSuperMenu', 'compan');
+        } else {
+          store.dispatch('toggleSuperMenu', 'super');
+        }
+        router.push({ path: 'dashboard', query }).catch(() => {}); //forward ||
+      })
+      .catch(() => {
+        loading.value = false;
+        reject();
+      });
+  });
+};
+
+const submitLogin = (formEl: FormInstance | undefined) => {
+  if (!accountAfsSuccess) return;
+  if (!afs) {
+    const params = MixinClone(loginForm);
+    params.c_sessionid = afsParam.c_sessionid;
+    params.sig = afsParam.sig;
+    params.nc_token = afsParam.nc_token;
+    delete params.validcode;
+    delete params.scene;
+    dispatchLoginAction(params).catch(() => {
+      accountAfsSuccess.value = false;
+    });
+  } else {
+    formEl?.validate((valid: boolean) => {
+      if (valid) {
+        const params = MixinClone(loginForm);
+        params.uuid = uuid;
+        params.captcha = params.validcode;
+        delete params.validcode;
+        dispatchLoginAction(params).catch(() => {
+          changeValidcode();
+        });
+      } else {
+        // 使用 Element Plus 的消息提示
+        ElMessage.error(t('val_form'));
+        return false;
+      }
+    });
+  }
+};
+const goHome = () => {
+  router.push({ path: 'dashboard' }).catch(() => {});
+};
+</script>
+  <style>
+.el-input-group__append {
+  padding: 0;
+}
+</style>
+  

+ 388 - 0
src/views/companyMent/administratorManage.vue

@@ -0,0 +1,388 @@
+<template>
+  <div>
+    <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(['administratorManage:add'])" @click="handleAddAdmin"
+              >添加</el-button
+            >
+          </div>
+          <div class="toolbar-search">
+            <en-table-search @search="searchEvent" placeholder="请输入关键字" @advancedSearch="advancedSearchEvent" advanced>
+             <!--  <template #advanced-content>
+                <el-form ref="advancedForm" :model="advancedForm" label-width="90px">
+                  <el-form-item label="状态">
+                    <el-select v-model="advancedForm.user_state">
+                      <el-option :key="0" label="正常" :value="0" />
+                      <el-option :key="-1" label="删除" :value="-1" />
+                    </el-select>
+                  </el-form-item>
+                </el-form>
+              </template> -->
+            </en-table-search>
+          </div>
+        </div>
+      </template>
+
+      <template #table-columns>
+        <el-table-column prop="username" label="登录名" />
+        <el-table-column prop="real_name" label="用户名" />
+        <el-table-column prop="nickname" label="用户昵称" />
+        <el-table-column prop="sn" label="用户编码" />
+        <el-table-column prop="dept_name" label="部门" />
+        <el-table-column prop="post_name" label="岗位" />
+        <el-table-column prop="user_state" label="状态">
+          <template #default="scope">
+            {{ scope.row.user_state === 0 ? '正常' : '删除' }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="role_name" label="所属角色" />
+        <el-table-column label="操作" width="200px">
+          <template #default="scope">
+            <el-button size="mini" type="primary" v-if="checkPermission(['administratorManage:edit'])" @click="handleEditAdmin(scope.$index, scope.row)"
+              >编辑</el-button
+            >
+            <el-button
+              size="mini"
+              type="danger"
+              v-if="scope.row.user_state === 0 && checkPermission(['administratorManage:del'])"
+              @click="handleDeleteAdmin(scope.$index, scope.row)"
+              >删除</el-button
+            >
+            <el-button
+              size="mini"
+              type="success"
+              v-if="scope.row.user_state !== 0 && checkPermission(['administratorManage:restore'])"
+              @click="handleRestoreAdmin(scope.$index, scope.row)"
+              >恢复</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>
+    <el-dialog
+      :title="adminForm.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="adminForm" :rules="adminRules" ref="adminFormRef" label-width="120px">
+        <el-form-item label="用户姓名" prop="real_name">
+          <el-input v-model="adminForm.real_name" :minlength="2" :maxlength="20" clearable placeholder="请输入用户姓名"></el-input>
+        </el-form-item>
+        <el-form-item label="用户编码" prop="sn">
+          <el-input v-model="adminForm.sn" :maxlength="20" clearable placeholder="请输入用户编码"></el-input>
+        </el-form-item>
+        <el-form-item label="昵称" prop="nickname">
+          <el-input v-model="adminForm.nickname" :maxlength="20" clearable placeholder="请输入昵称"></el-input>
+        </el-form-item>
+        <el-form-item label="登录名" prop="username">
+          <el-input v-model="adminForm.username" :maxlength="20" clearable placeholder="请输入登录名"></el-input>
+        </el-form-item>
+        <el-form-item label="密码" prop="password">
+          <el-input
+            type="password"
+            autocomplete="off"
+            v-model="adminForm.password"
+            :maxlength="20"
+            clearable
+            :placeholder="adminForm.id ? '不修改请留空' : '6-20位数字、英文字母,或者特殊字符'"
+          ></el-input>
+        </el-form-item>
+        <el-form-item label="所属角色" prop="role_id">
+          <el-select
+            v-model="adminForm.role_id"
+            :disabled="adminForm.founder === 1"
+            :placeholder="adminForm.founder === 1 ? '超级管理员无须选择角色' : '请选择角色'"
+            clearable
+          >
+            <el-option v-for="item in rolesOptions" :key="item.role_id" :label="item.role_name" :value="item.role_id"> </el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="所属部门" prop="dept_id">
+          <el-cascader
+            v-model="adminForm.dept_id"
+            :options="deptList"
+            :props="{
+              label: 'name',
+              value: 'id',
+              emitPath: false,
+              checkStrictly: true,
+            }"
+            clearable
+          ></el-cascader>
+        </el-form-item>
+        <el-form-item label="所属岗位" prop="post_id">
+          <el-select v-model="adminForm.post_id">
+            <el-option v-for="item in postList" :key="item.id" :label="item.name" :value="item.id" />
+          </el-select>
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="dialogVisible = false">取 消</el-button>
+        <el-button type="primary" @click="submitAdminForm">确 定</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+  
+  <script lang="ts" setup>
+import { ref, reactive, onMounted, watch } from 'vue';
+import { ElMessageBox, FormInstance, ElMessage } from 'element-plus';
+import * as API_Auth from '@/api/auth';
+import * as API_Setting from '@/api/setting';
+import md5 from 'js-md5';
+import { RegExp } from '../../composables/ui-utils/RegExp';
+//, Foundation
+const deptList = ref([]);
+const postList = ref([]);
+const loading = ref(false);
+const params = reactive({
+  page_no: 1,
+  page_size: 10,
+  user_state: 0,
+  keyword: '',
+});
+const tableData = ref<any>({});
+const adminForm = reactive({
+  founder: 0,
+  real_name: '',
+  password: '',
+  id: '',
+  role_id: '',
+  face: '',
+});
+const adminFormRef = ref();
+const advancedForm = reactive({
+  user_state: 0,
+});
+const adminRules = reactive({
+  username: [
+    { required: true, message: '请输入人员名称!' },
+    { min: 2, max: 20, message: '长度在 2 到 20 个字符' },
+    {
+      validator: (rule, value, callback) => {
+        if (value && !RegExp.userName.test(value)) {
+          callback(new Error('只支持汉字、字母、数字、“-”、“_”的组合!'));
+        } else {
+          callback();
+        }
+      },
+    },
+  ],
+  password: [
+    { required: true, message: '请输入人员密码!', trigger: 'blur' },
+    {
+      validator: (rule, value, callback) => {
+        if (value && !RegExp.password.test(value)) {
+          callback(new Error('密码应为6-20位数字、英文字母,或者特殊字符!'));
+        } else {
+          callback();
+        }
+      },
+    },
+  ],
+  department: [{ required: true, message: '请输入所属部门!' }],
+  role_id: [{ required: true, message: '请选择所属角色!', trigger: 'change' }],
+  face: [{ required: true, message: '请上传人员头像!' }],
+});
+const dialogVisible = ref(false);
+const rolesOptions = ref([]);
+const fileList = ref([]);
+
+watch(
+  () => adminForm.id,
+  (newValue) => {
+    adminRules.username[0].required = !newValue;
+    adminRules.password[0].required = !newValue;
+  }
+);
+
+watch(
+  () => adminForm.founder,
+  (newValue) => {
+    adminRules.role_id[0].required = newValue === 0;
+    if (newValue === 1) adminForm.role_id = '超级人员';
+  }
+);
+
+watch(dialogVisible, (newVal) => {
+  if (!newVal) {
+    fileList.value = [];
+  }
+});
+
+onMounted(() => {
+  GET_AdministratorList();
+  API_Auth.getRoleList({ page_size: 10000 }).then((response) => {
+    rolesOptions.value = response.data;
+  });
+  GET_DeptList();
+  GET_StationAllList();
+});
+
+const advancedSearchEvent = () => {
+  Object.assign(params, advancedForm);
+  delete params.start_time;
+  delete params.end_time;
+  if (advancedForm.Contract_time_range) {
+    params.start_time = parseInt(Number(advancedForm.Contract_time_range[0]) / 1000);
+    params.end_time = parseInt(Number(advancedForm.Contract_time_range[1]) / 1000);
+  }
+  delete params.Contract_time_range;
+  params.page_no = 1;
+  GET_AdministratorList();
+};
+
+const GET_DeptList = () => {
+  API_Setting.getDeptList().then((response) => {
+    deptList.value = Foundation.buildTree(response, '0');
+  });
+};
+
+const GET_StationAllList = () => {
+  API_Setting.getStationAllList().then((response) => {
+    postList.value = response;
+  });
+};
+
+const uploadSuccess = (res) => {
+  adminForm.face = res.url;
+};
+
+const delGiftImg = () => {
+  fileList.value = [];
+  adminForm.face = '';
+};
+
+const handlePageSizeChange = (size) => {
+  params.page_size = size;
+  GET_AdministratorList();
+};
+
+const handlePageCurrentChange = (page) => {
+  params.page_no = page;
+  GET_AdministratorList();
+};
+
+const handleAddAdmin = () => {
+  Object.assign(adminForm, {
+    founder: 0,
+    real_name: '',
+    password: '',
+    username:"",
+    nickname:"",
+    role_id:"",
+    
+  });
+  fileList.value = [];
+  dialogVisible.value = true;
+};
+
+const handleEditAdmin = (index, row) => {
+  Object.assign(adminForm, row);
+  adminForm.role_id = parseInt(adminForm.role_id) === 0 ? '超级人员' : adminForm.role_id;
+  fileList.value = adminForm.face ? [{ name: 'face', url: adminForm.face }] : [];
+  dialogVisible.value = true;
+};
+
+const handleRestoreAdmin = (index, row) => {
+  API_Auth.restoreAdministrator(row.id).then(() => {
+    ElMessage.success('恢复成功!');
+    GET_AdministratorList();
+  });
+};
+
+const handleDeleteAdmin = (index, row) => {
+  ElMessageBox.confirm('确定要删除这个人员吗?', '提示', { type: 'warning' })
+    .then(() => {
+      API_Auth.deleteAdministrator(row.id).then(() => {
+        ElMessage.success('删除成功!');
+        GET_AdministratorList();
+      });
+    })
+    .catch(() => {});
+};
+
+const handleDialogOpen = () => {
+  setTimeout(() => {
+    adminFormRef.value.clearValidate();
+  });
+};
+
+const submitAdminForm = () => {
+  adminFormRef.value.validate((valid) => {
+    if (valid) {
+      const { id } = adminForm;
+      const params = { ...adminForm };
+      if (params.password) params.password = md5(params.password);
+      if (!params.real_name) delete params.real_name;
+      params.role_id === '超级人员' ? (params.role_id = 0) : null;
+      if (id) {
+        API_Auth.editAdministrator(id, params).then(() => {
+          dialogVisible.value = false;
+          ElMessage.success('修改成功!');
+          GET_AdministratorList();
+        });
+      } else {
+        API_Auth.addAdministrator(params).then(() => {
+          dialogVisible.value = false;
+          ElMessage.success('添加成功!');
+          GET_AdministratorList();
+        });
+      }
+    } else {
+      ElMessage.error('表单填写有误,请核对!');
+      return false;
+    }
+  });
+};
+
+const searchEvent = (data) => {
+  params.keyword = data;
+  params.page_no = 1;
+  GET_AdministratorList();
+};
+
+const GET_AdministratorList = () => {
+  loading.value = true;
+  API_Auth.getAdministratorList(params)
+    .then((response) => {
+      loading.value = false;
+      tableData.value = response;
+    })
+    .catch(() => {
+      loading.value = false;
+    });
+};
+</script>
+  
+  <style scoped >
+.face-image {
+  display: block;
+  width: 50px;
+  height: 50px;
+  margin: 0 auto;
+}
+
+::v-deep .el-dialog__body {
+  padding: 10px 20px;
+}
+</style>

+ 296 - 0
src/views/companyMent/branch.vue

@@ -0,0 +1,296 @@
+<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="dialogVisible" width="500px" :close-on-click-modal="false" :close-on-press-escape="false">
+        <el-form :model="deptForm" ref="deptFormRef" 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="deptFormRef" 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';
+
+const defaultRegions = ref<any>(null);
+const deptList = ref([]);
+const deptType = ref([]);
+const deptForm = ref<any>({
+  sort: 1,
+  name: '',
+  sn: '',
+  parent_id: '',
+  type: '',
+  remark: '',
+});
+const dialogVisible = ref(false);
+const dialogEditVisible = ref(false);
+const deptFormRef=ref()
+
+const checkPermission = (permissions: string[]): boolean => {
+  return true;
+};
+
+const GET_DictDataInfo = () => {
+  API_Setting.getDictDataInfo('erp_dept_type').then((response) => {
+    deptType.value = response;
+  });
+};
+
+/* 处理树形数据 */
+let buildTree = function buildTree(list, parent_id) {
+  const tree = [];
+  for (let i = 0; i < list.length; i++) {
+    if (list[i].parent_id === parent_id) {
+      const node = {
+        id: list[i].id,
+        parent_id: list[i].parent_id,
+        name: list[i].name,
+        create_time: list[i].create_time,
+        remark: list[i].remark,
+        sn: list[i].sn,
+        sort: list[i].sort,
+        type: list[i].type,
+        region_ids: list[i].region_ids,
+        children: buildTree(list, list[i].id),
+      };
+      tree.push(node);
+    }
+  }
+  return tree;
+};
+const GET_DeptList = () => {
+  API_Setting.getDeptList().then((response) => {
+    deptList.value = buildTree(response, '0');
+    console.log(deptList.value);
+  });
+};
+
+const handleAddBranch = () => {
+  deptForm.value = {
+    sort: 1,
+    name: '',
+    sn: '',
+    parent_id: '',
+    remark: '',
+    type: deptType.value.find(sc => sc.default_flag === true)?.value || 'MANAGER'
+  };
+  defaultRegions.value = [];
+  dialogEditVisible.value = false;
+  dialogVisible.value = true;
+};
+
+const handleEditBranch = (row: any) => {
+  deptForm.value = JSON.parse(JSON.stringify(row)); // 创建深拷贝
+  defaultRegions.value = row.region_ids ? [...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>
+  <style scoped>
+:deep(.w-e-text-container) {
+  min-height: 200px !important;
+}
+
+:deep(.w-e-full-screen-container) {
+  z-index: 3000;
+}
+
+.toolbar {
+  display: flex;
+  align-items: center;
+  height: 44px;
+  border-bottom: 1px solid #e6ebf5;
+  background-color: #fff;
+}
+
+.inner-toolbar {
+  padding: 0 20px;
+}
+
+.inner-toolbar .categary {
+  padding-right: 10px;
+  font-size: 14px;
+}
+
+.face-image {
+  display: block;
+  width: 50px;
+  height: 50px;
+  margin: 0 auto;
+}
+
+:deep(.el-dialog__body) {
+  padding: 10px 20px;
+}
+
+.basicsetting-container {
+  background-color: #fff;
+  flex: 2;
+  margin: 10px 0 0 10px;
+}
+
+.basicsetting-container .form {
+  padding-top: 20px;
+  width: 500px;
+}
+
+.menus-container {
+  flex: 1;
+  margin: 10px 0 0 10px;
+}
+
+.menus-container :deep(.el-tree) {
+  width: 500px;
+}
+
+.menus-container :deep(.el-icon-minus) {
+  color: #f56c6c;
+}
+
+.custom-tree-node {
+  flex: 1;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  font-size: 14px;
+  padding-right: 8px;
+}
+</style>

+ 237 - 0
src/views/companyMent/companyInfo.vue

@@ -0,0 +1,237 @@
+<template>
+  <div class="container">
+    <el-form :model="companyForm" :rules="stockRules" ref="companyFormRef" label-width="140px">
+      <el-row>
+        <el-col :span="6">
+          <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>
+        <!-- user_num -->
+        <el-col :span="5">
+          <el-form-item label="套餐到期时间:" prop="name">
+           {{companyForm.end_use_time}}
+          </el-form-item>
+        </el-col>
+        <el-col :span="5">
+          <el-form-item label="可绑定账户数:" prop="name">
+           {{companyForm.user_num}}
+          </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>
+    <el-button type="primary" @click="submitWarehouseForm">保 存</el-button>
+  </div>
+</template>
+  
+<script lang="ts" setup>
+import { ref, onMounted } from 'vue';
+import * as API_Company from '@/api/company';
+import * as API_Package from '@/api/package';
+import { provinceAndCityData, pcTextArr, regionData, pcaTextArr, codeToText } from 'element-china-area-data';
+import { ElMessageBox, FormInstance, ElMessage } from 'element-plus';
+import type { UploadProps } from 'element-plus';
+const tableData = ref<any>({});
+const companyForm = ref<any>({
+  role_id: '',
+  name: '',
+  money: '',
+  day: '',
+  use_able: false,
+  remark: '',
+  address: '',
+  license: '',
+  company_package_id: '',
+});
+const options = ref<any[]>(regionData);
+const selectedOptions = ref<any[]>([]);
+const loading = ref(false);
+
+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 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;
+};
+const companyFormRef = ref();
+const submitWarehouseForm = () => {
+  companyFormRef.value.validate((valid: boolean) => {
+    if (valid) {
+      const params = { ...companyForm.value };
+      params.province = selectedOptions.value.join(',');
+      API_Company.editCompany(companyForm.value.id, params).then(() => {
+        ElMessage.success('修改成功!');
+        GET_CompanyList();
+      });
+    } else {
+      ElMessage.error('表单填写有误,请核对!');
+      /*    store.dispatch('showMessage', { message: '表单填写有误,请核对!', type: 'error' }); */
+      return false;
+    }
+  });
+};
+
+const GET_CompanyList = () => {
+  loading.value = true;
+  API_Company.getOne('1811346086001057794')
+    .then((response) => {
+      loading.value = false;
+      selectedOptions.value = response.province.split(',');
+      /*  response.records.map((el: any) => {
+        if (el.province !== undefined) {
+          el.license = el.license.replace('blob:', '');
+          el.province = el.province.split(',');
+        }
+        return el;
+      }); */
+      companyForm.value = response;
+      console.log(companyForm.value);
+    })
+    .catch(() => {
+      loading.value = false;
+    });
+};
+const packageList = ref([]);
+const GET_PackageList = () => {
+  API_Package.packageList({ page_no: 1, page_size: 1000, use_able: true }).then((res) => {
+    packageList.value = res.records;
+  });
+};
+
+onMounted(() => {
+  GET_CompanyList();
+  GET_PackageList();
+});
+</script>
+  
+  <style scoped >
+.container {
+  background-color: #fff;
+  padding: 10px;
+  position: relative;
+}
+
+.site-tag {
+  margin: 0 3px;
+}
+
+.site-new-tag-input {
+  width: 100px;
+}
+
+::v-deep .site-logo .el-upload {
+  border: 1px dashed #d9d9d9;
+  border-radius: 6px;
+  cursor: pointer;
+  position: relative;
+  overflow: hidden;
+
+  /* &:hover {
+    border-color: #409eff;
+  } */
+}
+
+.logo-uploader-icon {
+  font-size: 20px;
+  color: #8c939d;
+  width: 120px;
+  height: 120px;
+  line-height: 120px;
+  text-align: center;
+}
+
+.site-logo-img {
+  width: 120px;
+  height: 120px;
+  display: block;
+}
+
+.image-pane .el-input-group {
+  width: 200px;
+}
+
+.point-pane .el-input-group {
+  width: 200px;
+}
+</style>

+ 370 - 0
src/views/goods/SKUList.vue

@@ -0,0 +1,370 @@
+<template>
+  <div>
+    <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" 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="brand" label="品牌" />
+        <el-table-column prop="bar_code" label="条码" />
+        <el-table-column prop="sku" label="sku" />
+        <el-table-column prop="weight" label="重量" />
+        <el-table-column prop="unit" label="单位" />
+        <el-table-column prop="image" label="产品图片">
+          <template #default="scope">
+            <img :src="scope.row.image" alt="" />
+          </template>
+        </el-table-column>
+
+        <el-table-column label="操作">
+          <template #default="scope">
+            <el-button size="mini" v-if="checkPermission(['warehouse:update'])" @click="handleView(scope.row)">查看</el-button>
+            <el-button size="mini" v-if="checkPermission(['warehouse:update'])" @click="handleEditWarehouse(scope.$index, scope.row)">编辑</el-button>
+            <el-button type="success" size="mini" v-if="checkPermission(['removeAreaType:delete'])" @click="handleDelGoods(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="skuForm.id ? '编辑商品' : '新建商品'"
+      v-model="dialogVisible"
+      @open="handleDialogOpen"
+      :modal-append-to-body="false"
+      :close-on-click-modal="false"
+      :close-on-press-escape="false"
+    >
+      <el-form :model="skuForm" :rules="stockRules" ref="skuFormRef" label-width="140px">
+        <el-row>
+          <!--     <el-col :span="10" v-if="!skuForm.id">
+              <el-form-item label="套餐:" prop="company_package_id">
+                <el-select v-model="skuForm.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="skuForm.name" :minlength="2" :maxlength="20" :disabled="disabled" clearable placeholder="请输入产品名称"></el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :span="10">
+            <el-form-item label="品牌:" prop="brand">
+              <el-input v-model="skuForm.brand" :minlength="2" :maxlength="20" :disabled="disabled" clearable placeholder="请输入品牌"></el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :span="10">
+            <el-form-item label="条码:" prop="bar_code">
+              <el-input v-model="skuForm.bar_code" :minlength="2" :maxlength="20" :disabled="disabled" clearable placeholder="请输入条码"></el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :span="10">
+            <el-form-item label="sku:" prop="sku">
+              <el-input v-model="skuForm.sku" :minlength="2" :maxlength="20" :disabled="disabled" clearable placeholder="请输入sku"></el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :span="10">
+            <el-form-item label="重量:" prop="weight">
+              <el-input v-model="skuForm.weight" :minlength="2" :maxlength="20" :disabled="disabled" clearable placeholder="请输入重量"></el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :span="10">
+            <el-form-item label="单位:" prop="unit">
+              <el-input v-model="skuForm.unit" :minlength="2" :maxlength="20" :disabled="disabled" clearable placeholder="请输入单位"></el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :span="10">
+            <el-form-item label="产品图片:" prop="image">
+              <el-upload
+                class="avatar-uploader"
+                action="http://192.168.8.101:9998/uploaders"
+                :show-file-list="false"
+                :disabled="disabled"
+                :on-success="handleAvatarSuccess"
+                :before-upload="beforeAvatarUpload"
+              >
+                <img v-if="skuForm.image" :src="skuForm.image" 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" width="450px" :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_Goods from '@/api/goods';
+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);
+  skuForm.value.image = 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 skuForm = ref<any>({
+  name: '',
+  brand: '',
+  snCode: '',
+  bar_code: '',
+  sku: '',
+  weight: '',
+  image: '',
+  unit: '',
+});
+const stockRules = ref<any>({
+  name: [{ required: true, message: '请输入产品名称!', trigger: 'blur' }],
+  brand: [{ required: true, message: '请输入品牌!', trigger: 'blur' }],
+  snCode: [{ required: true, message: '请输入商品序列号!', trigger: 'blur' }],
+  bar_code: [{ required: true, message: '请输入条码!', trigger: 'blur' }],
+  sku: [{ required: true, message: '请输入sku!', trigger: 'blur' }],
+  weight: [{ required: true, message: '请输入重量!', trigger: 'blur' }],
+  postal_code: [{ required: true, message: '请输入单位!', trigger: 'blur' }],
+  image: [{ required: true, 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 disabled = ref(false);
+
+const renewVisible = ref(false);
+/* 套餐 */
+const packageForm = ref<any>({
+  company_package_id: '',
+  this_money: '',
+});
+let packageId = ref('');
+let companyId = ref('');
+const packageFormRef = ref();
+
+const handleBranch = (index: number, row: any) => {
+  router.push({ name: 'branch' });
+};
+
+/* 续费 */
+const handleRenew = (index: number, row: any) => {
+  packageForm.value.company_package_id = row.company_package_id;
+  companyId.value = row.id;
+  packageId.value = row.company_package_id;
+  API_Company.companyPackage(row.company_package_id).then((res) => {
+    packageForm.value.this_money = res.money;
+    renewVisible.value = true;
+  });
+};
+const skuFormRef = ref();
+const checkPermission = (permissions: string[]): boolean => {
+  // Implement your permission check logic here
+  return true;
+};
+
+const handleAddWarehouse = () => {
+  selectedOptions.value = [];
+  skuForm.value = {};
+  disabled.value = false;
+  dialogVisible.value = true;
+};
+
+const handleEditWarehouse = (index: number, row: any) => {
+  skuForm.value = { ...row };
+  disabled.value = false;
+  selectedOptions.value = row.province;
+  dialogVisible.value = true;
+};
+
+const searchEvent = (keyword: string) => {
+  params.value = {
+    ...params.value,
+    keyword: keyword,
+  };
+  params.value.page_no = 1;
+  GET_GoodsList();
+};
+
+const handlePageSizeChange = (size: number) => {
+  params.value.page_size = size;
+  GET_GoodsList();
+};
+
+const handlePageCurrentChange = (page: number) => {
+  params.value.page_no = page;
+  GET_GoodsList();
+};
+
+const handleDialogOpen = () => {
+  setTimeout(() => {
+    skuFormRef.value.clearValidate();
+  });
+};
+
+const submitWarehouseForm = () => {
+  skuFormRef.value.validate((valid: boolean) => {
+    if (valid) {
+      const { id } = skuForm.value;
+      const params = { ...skuForm.value };
+      if (id) {
+        API_Goods.editProduct(id, params).then(() => {
+          dialogVisible.value = false;
+          ElMessage.success('修改成功!');
+          GET_GoodsList();
+        });
+      } else {
+        API_Goods.addProduct(params).then(() => {
+          dialogVisible.value = false;
+          ElMessage.success('添加成功!');
+          GET_GoodsList();
+        });
+      }
+    } else {
+      ElMessage.error('表单填写有误,请核对!');
+      /*    store.dispatch('showMessage', { message: '表单填写有误,请核对!', type: 'error' }); */
+      return false;
+    }
+  });
+};
+
+const GET_GoodsList = () => {
+  loading.value = true;
+  API_Goods.productList(params.value)
+    .then((response) => {
+      loading.value = false;
+      tableData.value = response;
+      console.log(tableData.value);
+    })
+    .catch(() => {
+      loading.value = false;
+    });
+};
+
+/* 删除 */
+const handleDelGoods = (row) => {
+  ElMessageBox.confirm('确定要删除吗?', '提示', { type: 'warning' })
+    .then(() => {
+      API_Goods.delProduct(row.id).then(() => {
+        ElMessage.success('删除成功!');
+        GET_GoodsList();
+      });
+    })
+    .catch(() => {});
+};
+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;
+};
+
+/* 切换菜单 */
+
+const handleView = (row) => {
+  skuForm.value = { ...row };
+  disabled.value = true;
+  dialogVisible.value = true;
+};
+
+onMounted(() => {
+  GET_GoodsList();
+  /*  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>
+  

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

@@ -1,207 +0,0 @@
-<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';
-  
-  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;
-    });
-  };
-  
-    /* 处理树形数据 */
-    let buildTree = function buildTree(list, parent_id) {
-    const tree = [];
-    for (let i = 0; i < list.length; i++) {
-      if (list[i].parent_id === parent_id) {
-        const node = {
-          id: list[i].id,
-          parent_id: list[i].parent_id,
-          name: list[i].name,
-          create_time: list[i].create_time,
-          remark: list[i].remark,
-          sn: list[i].sn,
-          sort: list[i].sort,
-          type: list[i].type,
-          region_ids: list[i].region_ids,
-          children: buildTree(list, list[i].id),
-        };
-        tree.push(node);
-      }
-    }
-    return tree;
-  };
-  const GET_DeptList = () => {
-    API_Setting.getDeptList().then(response => {
-      deptList.value = 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>

+ 7 - 1
src/views/setting/company.vue → src/views/setting/companyList.vue

@@ -26,9 +26,10 @@
 
         <el-table-column label="操作">
           <template #default="scope">
-            <el-button size="mini" v-if="checkPermission(['warehouse:update'])" @click="getBranch('super')">查看</el-button>
+            <el-button size="mini" v-if="checkPermission(['warehouse:update'])" @click="getBranch('compan')">查看</el-button>
             <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>
+            <el-button size="mini" class="mt-1" v-if="checkPermission(['warehouse:update'])" @click="handleBranch(scope.$index, scope.row)">部门管理</el-button>
           </template>
         </el-table-column>
       </template>
@@ -235,6 +236,11 @@ const packageForm = ref<any>({
 let packageId = ref('');
 let companyId = ref('');
 const packageFormRef = ref();
+
+const handleBranch=(index: number, row: any)=>{
+  router.push({ name: 'branch' });
+}
+
 /* 续费 */
 const handleRenew = (index: number, row: any) => {
   packageForm.value.company_package_id = row.company_package_id;

+ 416 - 0
src/views/setting/dicData.vue

@@ -0,0 +1,416 @@
+<template>
+  <div>
+    <div style="display: flex">
+      <div class="dict-type">
+        <en-table-layout :tableData="tableDataType.data" :loading="loading" @cell-click="tableClick" style="cursor: pointer">
+          <template #toolbar>
+            <div class="inner-toolbar">
+              <div class="toolbar-btns">
+                <span style="font-size: 15px; padding-right: 10px">字典分类列表</span>
+                <el-button size="small" v-if="checkPermission(['dictDataType:add'])" type="primary" @click="handleAddType">新增</el-button>
+              </div>
+              <div class="toolbar-search"></div>
+            </div>
+          </template>
+
+          <template #default>
+            <el-table-column prop="id" label="字典编号" />
+            <el-table-column prop="name" label="字典名称" />
+            <el-table-column prop="type" label="字典类型" />
+            <el-table-column label="操作" width="150px">
+              <template #default="scope">
+                <el-button size="small" v-if="checkPermission(['dictDataType:edit'])" @click="handleEditType(scope.$index, scope.row)">编辑</el-button>
+                <el-button size="small" v-if="checkPermission(['dictDataType:del'])" type="danger" @click="handleDeleteType(scope.$index, scope.row)"
+                  >删除</el-button
+                >
+              </template>
+            </el-table-column>
+          </template>
+
+          <template #pagination>
+            <el-pagination
+              v-if="tableDataType"
+              @size-change="handlePageSizeChangeType"
+              @current-change="handlePageCurrentChangeType"
+              :current-page="tableDataType.page_no"
+              :page-sizes="[10, 20, 50, 100]"
+              :page-size="tableDataType.page_size"
+              layout="total, sizes, prev, pager, next, jumper"
+              :total="tableDataType.data_total"
+            >
+            </el-pagination>
+          </template>
+        </en-table-layout>
+      </div>
+      <div class="dict-data">
+        <en-table-layout :tableData="tableData.data" :loading="loading">
+          <template #toolbar>
+            <div class="inner-toolbar">
+              <div class="toolbar-btns">
+                <span style="font-size: 15px; padding-right: 10px">字典数据列表</span>
+                <el-button size="small" v-if="checkPermission(['dictData:add']) && params.dict_type" type="primary" @click="handleAddData">新增</el-button>
+              </div>
+              <div class="toolbar-search"></div>
+            </div>
+          </template>
+
+          <template #default>
+            <el-table-column prop="id" label="字典编码" />
+            <el-table-column prop="label" label="字典标签" />
+            <el-table-column prop="value" label="字典键值" />
+            <el-table-column prop="sort" label="字典排序" />
+            <el-table-column label="是否默认选中">
+              <template #default="{ row }">{{ row.default_flag === true ? '是' : '否' }}</template>
+            </el-table-column>
+            <el-table-column label="操作" width="150px">
+              <template #default="scope">
+                <el-button size="small" v-if="checkPermission(['dictData:edit'])" @click="handleEditData(scope.$index, scope.row)">编辑</el-button>
+                <el-button size="small" v-if="checkPermission(['dictData:del'])" type="danger" @click="handleDeleteData(scope.$index, scope.row)"
+                  >删除</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>
+      </div>
+    </div>
+    <el-dialog :title="dictTypeForm.id ? '编辑' : '添加'" v-model="dialogTypeVisible" width="500px">
+      <el-form :model="dictTypeForm" :rules="dictTypeRules" ref="dictTypeFormRef" label-width="120px">
+        <el-form-item label="字典名称" prop="name">
+          <el-input v-model="dictTypeForm.name" :minlength="2" :maxlength="20" clearable placeholder="请输入字典名称"></el-input>
+        </el-form-item>
+        <el-form-item label="字典类型" prop="type">
+          <el-input v-model="dictTypeForm.type" :minlength="2" :maxlength="50" clearable placeholder="请输入字典类型"></el-input>
+        </el-form-item>
+        <el-form-item label="备注" prop="remark">
+          <el-input v-model="dictTypeForm.remark" type="textarea" :autosize="{ minRows: 3, maxRows: 4 }" placeholder="请输入备注"></el-input>
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="dialogTypeVisible = false">取 消</el-button>
+          <el-button type="primary" @click="submitTypeForm">确 定</el-button>
+        </span>
+      </template>
+    </el-dialog>
+    <el-dialog :title="dictDataForm.id ? '编辑' : '添加'" v-model="dialogVisible" width="500px">
+      <el-form :model="dictDataForm" :rules="dictDataRules" ref="dictDataFormRef" label-width="120px">
+        <el-form-item label="字典类型" prop="dict_type">
+          <el-input v-model="dictDataForm.dict_type" :disabled="true" placeholder="请输入字典类型"></el-input>
+        </el-form-item>
+        <el-form-item label="是否默认选中" prop="是否默认选中">
+          <el-radio-group v-model="dictDataForm.default_flag">
+            <el-radio :label="true">是</el-radio>
+            <el-radio :label="false">否</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="字典标签" prop="label">
+          <el-input v-model="dictDataForm.label" :minlength="1" :maxlength="20" clearable placeholder="请输入字典标签"></el-input>
+        </el-form-item>
+        <el-form-item label="字典值" prop="value">
+          <el-input v-model="dictDataForm.value" :minlength="2" :maxlength="20" clearable placeholder="请输入字典值"></el-input>
+        </el-form-item>
+        <el-form-item label="顺序号" prop="sort">
+          <el-input v-model="dictDataForm.sort" :minlength="2" :maxlength="20" clearable placeholder="请输入顺序号"></el-input>
+        </el-form-item>
+        <el-form-item label="备注" prop="remark">
+          <el-input v-model="dictDataForm.remark" type="textarea" :autosize="{ minRows: 3, maxRows: 4 }" placeholder="请输入备注"></el-input>
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="dialogVisible = false">取 消</el-button>
+          <el-button type="primary" @click="submitDataForm">确 定</el-button>
+        </span>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+  
+  <script setup>
+import { ref, onMounted, reactive } from 'vue';
+import { ElMessage, ElMessageBox } from 'element-plus';
+import * as API_Setting from '@/api/setting';
+import { RegExp } from '../../composables/ui-utils/RegExp';
+
+// 列表loading状态
+const loading = ref(false);
+
+// 列表参数
+const params = reactive({
+  page_no: 1,
+  page_size: 10,
+  dict_type: '',
+});
+
+const paramsType = reactive({
+  page_no: 1,
+  page_size: 10,
+});
+
+// 列表数据
+const tableData = ref({});
+const tableDataType = ref({});
+
+// 字典表单
+const dictDataForm = ref({});
+const dictTypeForm = ref({});
+
+// 字典表单 规则
+const dictTypeRules = {
+  name: [
+    { required: true, message: '请输入字典名称!', trigger: 'blur' },
+    { min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' },
+    {
+      validator: (rule, value, callback) => {
+        if (value && !RegExp.userName.test(value)) {
+          callback(new Error('只支持汉字、字母、数字、"-"、"_"的组合!'));
+        } else {
+          callback();
+        }
+      },
+      trigger: 'blur',
+    },
+  ],
+  type: [
+    { required: true, message: '请输入字典类型!', trigger: 'blur' },
+    { min: 2, max: 50, message: '长度在 2 到 50 个字符', trigger: 'blur' },
+    {
+      validator: (rule, value, callback) => {
+        if (value && !RegExp.userName.test(value)) {
+          callback(new Error('只支持汉字、字母、数字、"-"、"_"的组合!'));
+        } else {
+          callback();
+        }
+      },
+      trigger: 'blur',
+    },
+  ],
+};
+
+const dictDataRules = {
+  dict_type: [
+    { required: true, message: '请输入字典类型!', trigger: 'blur' },
+    { min: 2, max: 50, message: '长度在 2 到 50 个字符', trigger: 'blur' },
+    {
+      validator: (rule, value, callback) => {
+        if (value && !RegExp.userName.test(value)) {
+          callback(new Error('只支持汉字、字母、数字、"-"、"_"的组合!'));
+        } else {
+          callback();
+        }
+      },
+      trigger: 'blur',
+    },
+  ],
+  label: [
+    { required: true, message: '请输入字典标签!', trigger: 'blur' },
+    { min: 1, max: 20, message: '长度在 1 到 20 个字符', trigger: 'blur' },
+  ],
+  value: [{ required: true, message: '请输入字典值!', trigger: 'blur' }],
+  sort: [{ required: true, message: '显示顺序不能为空!', trigger: 'blur' }],
+};
+
+// 字典表单 dialog
+const dialogVisible = ref(false);
+const dialogTypeVisible = ref(false);
+
+const dictTypeFormRef = ref(null);
+const dictDataFormRef = ref(null);
+
+onMounted(() => {
+  GET_DictDataList();
+  GET_DictTypeList();
+});
+
+const tableClick = (row) => {
+  params.dict_type = row.type;
+  GET_DictDataList();
+};
+
+const handlePageSizeChangeType = (size) => {
+  paramsType.page_size = size;
+  GET_DictTypeList();
+};
+
+const handlePageCurrentChangeType = (page) => {
+  paramsType.page_no = page;
+  GET_DictTypeList();
+};
+
+const handlePageSizeChange = (size) => {
+  params.page_size = size;
+  GET_DictDataList();
+};
+
+const handlePageCurrentChange = (page) => {
+  params.page_no = page;
+  GET_DictDataList();
+};
+
+const handleAddType = () => {
+  dictTypeForm.value = {
+    status: 0,
+  };
+  dialogTypeVisible.value = true;
+};
+
+const handleAddData = () => {
+  dictDataForm.value = {
+    status: 0,
+    sort: 0,
+    dict_type: params.dict_type,
+    default_flag: false,
+  };
+  dialogVisible.value = true;
+};
+
+const handleEditType = (index, row) => {
+  dictTypeForm.value = JSON.parse(JSON.stringify(row));
+  dialogTypeVisible.value = true;
+};
+
+const handleEditData = (index, row) => {
+  dictDataForm.value = JSON.parse(JSON.stringify(row));
+  dialogVisible.value = true;
+};
+
+const handleDeleteType = (index, row) => {
+  ElMessageBox.confirm('确定要删除这个字典分类吗?', '提示', { type: 'warning' })
+    .then(() => {
+      API_Setting.deleteDictType({ id: row.id }).then(() => {
+        ElMessage.success('删除成功!');
+        GET_DictTypeList();
+      });
+    })
+    .catch(() => {});
+};
+
+const handleDeleteData = (index, row) => {
+  ElMessageBox.confirm('确定要删除这个字典吗?', '提示', { type: 'warning' })
+    .then(() => {
+      API_Setting.deleteDictData({ id: row.id }).then(() => {
+        ElMessage.success('删除成功!');
+        GET_DictDataList();
+      });
+    })
+    .catch(() => {});
+};
+
+const submitTypeForm = () => {
+  dictTypeFormRef.value.validate((valid) => {
+    if (valid) {
+      const { id } = dictTypeForm.value;
+      const params = JSON.parse(JSON.stringify(dictTypeForm.value));
+      params.id = id;
+      if (id) {
+        API_Setting.editDictType(params).then((response) => {
+          dialogTypeVisible.value = false;
+          ElMessage.success('修改成功!');
+          GET_DictTypeList();
+        });
+      } else {
+        API_Setting.addDictType(params).then(() => {
+          dialogTypeVisible.value = false;
+          ElMessage.success('添加成功!');
+          GET_DictTypeList();
+        });
+      }
+    } else {
+      ElMessage.error('表单填写有误,请核对!');
+      return false;
+    }
+  });
+};
+
+const submitDataForm = () => {
+  dictDataFormRef.value.validate((valid) => {
+    if (valid) {
+      const { id } = dictDataForm.value;
+      const params = JSON.parse(JSON.stringify(dictDataForm.value));
+      params.id = id;
+      if (id) {
+        API_Setting.editDictData(params).then((response) => {
+          dialogVisible.value = false;
+          ElMessage.success('修改成功!');
+          GET_DictDataList();
+        });
+      } else {
+        API_Setting.addDictData(params).then(() => {
+          dialogVisible.value = false;
+          ElMessage.success('添加成功!');
+          GET_DictDataList();
+        });
+      }
+    } else {
+      ElMessage.error('表单填写有误,请核对!');
+      return false;
+    }
+  });
+};
+
+const GET_DictTypeList = () => {
+  loading.value = true;
+  API_Setting.getDictTypeList(paramsType)
+    .then((response) => {
+      loading.value = false;
+      console.log(response);
+      tableDataType.value = response;
+    })
+    .catch(() => {
+      loading.value = false;
+    });
+};
+
+const GET_DictDataList = () => {
+  loading.value = true;
+  API_Setting.getDictDataList(params)
+    .then((response) => {
+      loading.value = false;
+      tableData.value = response;
+    })
+    .catch(() => {
+      loading.value = false;
+    });
+};
+
+const checkPermission = (permissions) => {
+  // 实现权限检查逻辑
+  return true;
+};
+</script>
+  
+  <style scoped>
+.dict-type {
+  width: 50%;
+}
+
+.dict-data {
+  width: 50%;
+}
+
+.face-image {
+  display: block;
+  width: 50px;
+  height: 50px;
+  margin: 0 auto;
+}
+
+.el-dialog__body {
+  padding: 10px 20px;
+}
+</style>

+ 201 - 0
src/views/setting/menuManage.vue

@@ -0,0 +1,201 @@
+<template>
+    <div class="menus-container">
+      <el-tree :data="menus" node-key="identifier" :expand-on-click-node="true">
+        <template #default="{ node, data }">
+          <span class="custom-tree-node">
+            <span>{{ data.title }}</span>
+            <span>
+              <el-button type="primary" text :disabled="node.level === 4" :icon="Plus"
+                :title="node.level === 4 ? '菜单最多添加三级' : '增加'" v-if="checkPermission(['menuManage:add'])"
+                @click.stop="handleAddMenu(data)" />
+              <el-button type="danger" text :icon="Minus" title="删除" v-if="checkPermission(['menuManage:del'])"
+                @click.stop="handleDeleteMenu(node, data)" />
+              <el-button type="primary" text :icon="Edit" title="编辑" v-if="checkPermission(['menuManage:edit'])"
+                @click.stop="handleEditMenu(data)" />
+            </span>
+          </span>
+        </template>
+      </el-tree>
+      <el-button v-if="menus" text style="margin-top: 10px; margin-left: 20px"
+        @click="handleAddMenu('')">添加顶级菜单</el-button>
+      <el-dialog :title="menuForm.menu_name ? '编辑菜单' : '添加菜单'" v-model="dialogMenuVisible" width="500px"
+        :close-on-click-modal="false" :close-on-press-escape="false" @close="closeMenuForm">
+        <el-form :model="menuForm" :rules="menuRules" ref="menuFormRef" label-width="100px">
+          <el-form-item label="菜单标题" prop="title">
+            <el-input v-model="menuForm.title"></el-input>
+          </el-form-item>
+          <el-form-item label="菜单标识" prop="identifier">
+            <el-input v-model="menuForm.identifier"></el-input>
+          </el-form-item>
+          <el-form-item label="菜单权限" prop="auth_regular">
+            <el-input v-model="menuForm.auth_regular" type="textarea"></el-input>
+          </el-form-item>
+          <el-form-item label="菜单图标" prop="icon" v-if="iconShow!=='0'">
+            <el-upload class="site-logo" :action="MixinUploadApi" :show-file-list="false"
+              :on-success="(res) => { menuForm.icon = res.url }" :multiple="false">
+              <img v-if="menuForm.icon" :src="menuForm.icon" class="site-logo-img">
+              <el-icon v-else><Plus /></el-icon>
+            </el-upload>
+          </el-form-item>
+          <el-form-item label="首页显示" prop="show_home" v-if="iconShow !== '0' && checkPermission(['UserMenu:get'])">
+            <el-switch v-model="menuForm.show_home" :active-value="1" :inactive-value="2" @change="switchVal"></el-switch>
+          </el-form-item>
+        </el-form>
+        <template #footer>
+          <span class="dialog-footer">
+            <el-button @click="closeMenuForm">取 消</el-button>
+            <el-button type="primary" @click="submitMenuForm('menuFormRef')">确 定</el-button>
+          </span>
+        </template>
+      </el-dialog>
+    </div>
+  </template>
+  
+  <script lang="ts" setup>
+  import { ref, reactive, onMounted } from 'vue'
+  import { ElMessage, ElMessageBox } from 'element-plus'
+  import { Plus, Minus, Edit } from '@element-plus/icons-vue'
+  import * as API_Menus from '@/api/menus'
+  
+  const menus = ref([])
+  const menuForm = reactive({})
+  const menuRules = reactive({
+    title: [
+      { required: true, message: '请输入菜单标题!', trigger: 'blur' }
+    ],
+    identifier: [
+      { required: true, message: '请输入菜单标识!', trigger: 'blur' }
+    ],
+    auth_regular: [
+      { required: true, message: '请输入菜单权限!', trigger: 'blur' }
+    ],
+  })
+  
+  const dialogMenuVisible = ref(false)
+  const currentMenu = ref('')
+  const iconShow = ref('')
+  const itemMenu = ref({})
+  const menuFormRef = ref(null)
+  
+  const closeMenuForm = () => {
+    dialogMenuVisible.value = false
+    itemMenu.value = {}
+  }
+  
+  const switchVal = (val) => {
+    if (menuForm.menu_name === "编辑菜单") {
+      if (val === 1) {
+        // API_Menus.editHomeMenu({menu_id:''})
+      } else if (val === 2) {
+        // API_Menus.deleteHomeMenu({})
+      }
+    }
+  }
+  
+  const handleAddMenu = (menu) => {
+    if (menu === "") {
+      iconShow.value = '0'
+    }
+    Object.assign(menuForm, {
+      parent_id: menu ? menu.id : 0
+    })
+    currentMenu.value = menu || {}
+    dialogMenuVisible.value = true
+  }
+  
+  const handleDeleteMenu = (node, menu) => {
+    ElMessageBox.confirm('确定要删除这个菜单吗?如果有子菜单,子菜单也会被一并删除!', '提示', { type: 'warning' })
+      .then(() => {
+        API_Menus.deleteMenu(menu.id).then(() => {
+          const parent = node.parent
+          const children = parent.data.children || parent.data
+          const index = children.findIndex(d => d.id === menu.id)
+          children.splice(index, 1)
+        })
+      })
+      .catch(() => { })
+  }
+  
+  const handleEditMenu = (menu) => {
+    Object.assign(menuForm, JSON.parse(JSON.stringify(menu)))
+    itemMenu.value = menuForm
+    iconShow.value = menu.parent_id
+    currentMenu.value = menu
+    menuForm.menu_name = '编辑菜单'
+    dialogMenuVisible.value = true
+  }
+  
+  const submitMenuForm = (formName) => {
+    menuFormRef.value.validate((valid) => {
+      if (valid) {
+        const { id } = menuForm
+        if (id) {
+          API_Menus.editMenu(id, menuForm).then(response => {
+            dialogMenuVisible.value = false
+            ElMessage.success('保存成功!')
+            GET_Memus()
+          })
+        } else {
+          API_Menus.addMenu(menuForm).then(response => {
+            const data = currentMenu.value
+            if (menuForm.parent_id === 0) {
+              menus.value.push(response)
+            } else {
+              if (!data.children) data.children = []
+              data.children.push(response)
+            }
+            dialogMenuVisible.value = false
+            ElMessage.success('保存成功!')
+          })
+        }
+      } else {
+        ElMessage.error('表单填写有误,请检查!')
+        return false
+      }
+    })
+  }
+  
+  const GET_Memus = (parent_id) => {
+    API_Menus.getMenusChildren(parent_id).then(response => {
+      menus.value = response
+    })
+  }
+  
+  const checkPermission = (permissions) => {
+    // 实现权限检查逻辑
+    return true
+  }
+  
+  onMounted(() => {
+    GET_Memus()
+  })
+  </script>
+  
+  <style scoped>
+  .menus-container {
+    padding: 10px;
+    background-color: #fff;
+  }
+  
+  .menus-container :deep(.el-tree) {
+    width: 500px;
+  }
+  
+  .menus-container :deep(.el-icon-minus) {
+    color: #F56C6C;
+  }
+  
+  .custom-tree-node {
+    flex: 1;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    font-size: 14px;
+    padding-right: 8px;
+  }
+  
+  .site-logo-img {
+    width: 50px;
+    height: 50px;
+  }
+  </style>

+ 3 - 2
src/views/setting/roleManage.vue

@@ -3,7 +3,7 @@
     <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 size="mini" type="primary" :icon="CirclePlus" v-if="checkPermission(['roleManage:add'])" @click="handleAddRole"
             >添加</el-button
           >
         </div>
@@ -44,6 +44,7 @@
 import { ref, onMounted } from 'vue';
 import { useRouter } from 'vue-router';
 import { ElMessage, ElMessageBox } from 'element-plus';
+import { Delete, Edit, Search, Share, Upload, CirclePlus } from '@element-plus/icons-vue';
 import * as API_Auth from '@/api/auth';
 
 const router = useRouter();
@@ -74,7 +75,7 @@ const handleAddRole = () => {
 };
 
 const handleEditRole = (row: any) => {
-  //router.push({ name: 'roleManageEdit', params: { id: row.role_id, callback: GET_RoleList } });
+  router.push({ name: 'roleManageAdd', query: { id: row.role_id } });
 };
 
 const handleDeleteRole = (row: any) => {

+ 507 - 499
src/views/setting/roleManageAdd.vue

@@ -1,525 +1,533 @@
 <template>
-    <div class="permission-container">
-      <el-form :model="permissionForm" :rules="permissionRules" ref="permissionFormRef" label-width="200px" class="demo-ruleForm">
-        <el-form-item label="角色名称" prop="role_name">
-          <el-input v-model="permissionForm.role_name"></el-input>
-        </el-form-item>
-        <el-form-item label="数据范围" prop="dept_ids">
-          <el-cascader
-            @visible-change="visibleChange"
-            ref="cascader"
-            :clearable="true"
-            v-model="permissionForm.dept_ids"
-            :options="deptList"
-            @change="selectHandle"
-            :props="props"
-          ></el-cascader>
-        </el-form-item>
-        <el-form-item label="角色描述" prop="role_describe">
-          <el-input v-model="permissionForm.role_describe"></el-input>
-        </el-form-item>
-        <el-form-item label="角色权限" prop="permission">
-          <el-checkbox :indeterminate="allIndeterminate" v-model="allCheck" @change="handleCheckAll">全部选择</el-checkbox>
-          <div v-for="(item, index) in permissions" :key="item.identifier" class="level_1">
-            <el-row :gutter="20">
-              <el-col :span="4">
-                <el-checkbox
-                  v-model="item.checked"
-                  :label="item.title"
-                  :indeterminate="checkIndeterminate(item.children)"
-                  @change="handleCheckboxChanged(item)"
-                ></el-checkbox>
-              </el-col>
-              <el-col :span="20">
-                <div v-for="(_item, _index) in item.children" :key="_item.identifier" class="checkbox-dropdown">
-                  <el-popover placement="bottom" trigger="hover" :open-delay="300" :disabled="!hasChildren(_item)" :append-to-body="false">
-                    <template #reference>
-                      <el-checkbox
-                        v-model="_item.checked"
-                        :indeterminate="checkIndeterminate(_item.children)"
-                        @click.stop
-                        @change="handleCheckboxChanged(_item, item)"
-                      >
-                        {{ _item.title }}
-                        <template v-if="hasChildren(_item)">
-                          <i class="el-icon-arrow-down"></i>
+  <div class="permission-container">
+    <el-form :model="permissionForm" :rules="permissionRules" ref="permissionFormRef" label-width="200px" class="demo-ruleForm">
+      <el-form-item label="角色名称" prop="role_name">
+        <el-input v-model="permissionForm.role_name"></el-input>
+      </el-form-item>
+      <el-form-item label="数据范围" prop="dept_ids">
+        <el-cascader
+          @visible-change="visibleChange"
+          ref="cascader"
+          :clearable="true"
+          v-model="permissionForm.dept_ids"
+          :options="deptList"
+          @change="selectHandle"
+          :props="props"
+        ></el-cascader>
+      </el-form-item>
+      <el-form-item label="角色描述" prop="role_describe">
+        <el-input v-model="permissionForm.role_describe"></el-input>
+      </el-form-item>
+      <el-form-item label="角色权限" prop="permission">
+        <el-checkbox :indeterminate="allIndeterminate" v-model="allCheck" @change="handleCheckAll">全部选择</el-checkbox>
+        <div v-for="(item, index) in permissions" :key="item.identifier" class="level_1">
+          <el-row :gutter="20">
+            <el-col :span="4">
+              <el-checkbox
+                v-model="item.checked"
+                :label="item.title"
+                :indeterminate="checkIndeterminate(item.children)"
+                @change="handleCheckboxChanged(item)"
+              ></el-checkbox>
+            </el-col>
+            <el-col :span="20">
+              <div v-for="(_item, _index) in item.children" :key="_item.identifier" class="checkbox-dropdown">
+                <el-popover placement="bottom" trigger="hover" :open-delay="300" :disabled="!hasChildren(_item)" :append-to-body="false">
+                  <template #reference>
+                    <el-checkbox
+                      v-model="_item.checked"
+                      :indeterminate="checkIndeterminate(_item.children)"
+                      @click.stop
+                      @change="handleCheckboxChanged(_item, item)"
+                    >
+                      {{ _item.title }}
+                      <template v-if="hasChildren(_item)">
+                        <i class="el-icon-arrow-down"></i>
+                      </template>
+                    </el-checkbox>
+                  </template>
+                  <div class="role-dropdown__list" v-if="hasChildren(_item)">
+                    <div class="role-dropdown" v-for="(__item, __index) in _item.children" :key="__item.identifier">
+                      <el-popover placement="right" trigger="hover" :open-delay="200" :disabled="!hasChildren(__item)" :append-to-body="false">
+                        <template #reference>
+                          <div class="role-dropdown__label">
+                            <el-checkbox
+                              v-model="__item.checked"
+                              :indeterminate="checkIndeterminate(__item.children)"
+                              @click.stop
+                              @change="handleCheckboxChanged(__item, _item)"
+                            >
+                              {{ __item.title }}
+                              <template v-if="hasChildren(__item)">
+                                <i class="el-icon-arrow-right"></i>
+                              </template>
+                            </el-checkbox>
+                          </div>
                         </template>
-                      </el-checkbox>
-                    </template>
-                    <div class="role-dropdown__list" v-if="hasChildren(_item)">
-                      <div class="role-dropdown" v-for="(__item, __index) in _item.children" :key="__item.identifier">
-                        <el-popover placement="right" trigger="hover" :open-delay="200" :disabled="!hasChildren(__item)" :append-to-body="false">
-                          <template #reference>
-                            <div class="role-dropdown__label">
-                              <el-checkbox
-                                v-model="__item.checked"
-                                :indeterminate="checkIndeterminate(__item.children)"
-                                @click.stop
-                                @change="handleCheckboxChanged(__item, _item)"
-                              >
-                                {{ __item.title }}
-                                <template v-if="hasChildren(__item)">
-                                  <i class="el-icon-arrow-right"></i>
-                                </template>
-                              </el-checkbox>
-                            </div>
-                          </template>
-                          <div class="role-dropdown__list" v-if="hasChildren(__item)">
-                            <div class="role-dropdown" v-for="(___item, __index) in __item.children" :key="___item.identifier">
-                              <el-popover placement="right" trigger="hover" :open-delay="200" :disabled="!hasChildren(___item)" :append-to-body="false">
-                                <template #reference>
-                                  <div class="role-dropdown__label">
-                                    <el-checkbox
-                                      v-model="___item.checked"
-                                      :indeterminate="checkIndeterminate(___item.children)"
-                                      @click.stop
-                                      @change="handleCheckboxChanged(___item, __item)"
-                                      >{{ ___item.title }}</el-checkbox
-                                    >
-                                  </div>
-                                </template>
-                              </el-popover>
-                            </div>
+                        <div class="role-dropdown__list" v-if="hasChildren(__item)">
+                          <div class="role-dropdown" v-for="(___item, __index) in __item.children" :key="___item.identifier">
+                            <el-popover placement="right" trigger="hover" :open-delay="200" :disabled="!hasChildren(___item)" :append-to-body="false">
+                              <template #reference>
+                                <div class="role-dropdown__label">
+                                  <el-checkbox
+                                    v-model="___item.checked"
+                                    :indeterminate="checkIndeterminate(___item.children)"
+                                    @click.stop
+                                    @change="handleCheckboxChanged(___item, __item)"
+                                    >{{ ___item.title }}</el-checkbox
+                                  >
+                                </div>
+                              </template>
+                            </el-popover>
                           </div>
-                        </el-popover>
-                      </div>
+                        </div>
+                      </el-popover>
                     </div>
-                  </el-popover>
-                </div>
-              </el-col>
-            </el-row>
-          </div>
-        </el-form-item>
-        <el-form-item label="">
-          <el-button type="primary" size="small" style="margin-top: 15px" @click="saveRolePermission">保存设置</el-button>
-        </el-form-item>
-      </el-form>
-    </div>
-  </template>
+                  </div>
+                </el-popover>
+              </div>
+            </el-col>
+          </el-row>
+        </div>
+      </el-form-item>
+      <el-form-item label="">
+        <el-button type="primary" size="small" style="margin-top: 15px" @click="saveRolePermission">保存设置</el-button>
+      </el-form-item>
+    </el-form>
+  </div>
+</template>
     
     <script lang="ts" setup>
-  import { ref, reactive, onMounted, watch, nextTick } from 'vue';
-  import { useRoute, useRouter } from 'vue-router';
-  import { ElMessage } from 'element-plus';
-  import * as API_Auth from '@/api/auth';
-  import * as API_Menus from '@/api/menus';
-  import * as API_Setting from '@/api/setting';
-  /*   import { Foundation } from '../../composables/ui-utils'; */
-  
-  const route = useRoute();
-  const router = useRouter();
-  
-  const props = {
-    label: 'name',
-    value: 'id',
-    emitPath: false,
-    multiple: true,
-    checkStrictly: true,
-  };
-  
-  const checked = ref(true);
-  const deptList = ref<any>([]);
-  const oneDimensionalList = ref([]); // 源数据平铺成一级节点
-  
-  /** 权限 表单 */
-  const permissionForm = reactive({
-    data_scope: 'ALL',
-    role_name: '',
-    dept_ids:"",
-    role_describe: '',
-  });
-  
-  /** 权限 表单规则 */
-  const permissionRules = reactive({
-    role_name: [
-      { required: true, message: '请输入角色名称!', trigger: 'blur' },
-      { min: 1, max: 10, message: '长度在 1 到 10 个字符', trigger: 'blur' },
-    ],
+import { ref, reactive, onMounted, watch, nextTick } from 'vue';
+import { useRoute, useRouter } from 'vue-router';
+import { ElMessage } from 'element-plus';
+import * as API_Auth from '@/api/auth';
+import * as API_Menus from '@/api/menus';
+import * as API_Setting from '@/api/setting';
+/*   import { Foundation } from '../../composables/ui-utils'; */
+
+const route = useRoute();
+const router = useRouter();
+
+const props = {
+  label: 'name',
+  value: 'id',
+  emitPath: false,
+  multiple: true,
+  checkStrictly: true,
+};
+
+const checked = ref(true);
+const deptList = ref<any>([]);
+const oneDimensionalList = ref([]); // 源数据平铺成一级节点
+
+/** 权限 表单 */
+const permissionForm = reactive({
+  data_scope: 'ALL',
+  role_name: '',
+  dept_ids: '',
+  role_describe: '',
+});
+
+/** 权限 表单规则 */
+const permissionRules = reactive({
+  role_name: [
+    { required: true, message: '请输入角色名称!', trigger: 'blur' },
+    { min: 1, max: 10, message: '长度在 1 到 10 个字符', trigger: 'blur' },
+  ],
+});
+
+const permissions = ref([]);
+const allCheck = ref(false);
+const allIndeterminate = ref(false);
+const isAllSelected = ref(false);
+
+const role_id = ref<any>(route.query.id);
+console.log('role_id:', route.query);
+const searchSelectAllNodeIndex = () => {
+  let selectAllOptionIndex = -1;
+  permissionForm.dept_ids.forEach((res, index) => {
+    if (res.length === 1) {
+      selectAllOptionIndex = index;
+    }
   });
-  
-  const permissions = ref([]);
-  const allCheck = ref(false);
-  const allIndeterminate = ref(false);
-  const isAllSelected = ref(false);
-  
-  const role_id = ref<any>(route.query.id);
-  console.log('role_id:', route.query);
-  const searchSelectAllNodeIndex = () => {
-    let selectAllOptionIndex = -1;
-    permissionForm.dept_ids.forEach((res, index) => {
-      if (res.length === 1) {
-        selectAllOptionIndex = index;
-      }
+  return selectAllOptionIndex;
+};
+
+const visibleChange = (visible) => {
+  if (visible) {
+    deptList.value.unshift({
+      id: '全选',
+      name: '全选',
+      value: '全选',
     });
-    return selectAllOptionIndex;
-  };
-  
-  const visibleChange = (visible) => {
-    if (visible) {
-      deptList.value.unshift({
-        id: '全选',
-        name: '全选',
-        value: '全选',
+  } else {
+    deptList.value.shift();
+  }
+};
+
+// 选择级联选择器
+const selectHandle = async (e = []) => {
+  const selectLength = e.filter((v) => v !== '全选').length;
+  const has_all_option = e.some((v) => v === '全选');
+  let dept_ids = [...permissionForm.dept_ids];
+
+  if (has_all_option && isAllSelected.value) {
+    // 移除了选项中除全部外的某个节点
+    dept_ids = dept_ids.filter((v) => v !== '全选');
+    dept_ids.splice(searchSelectAllNodeIndex(), 1);
+    permissionForm.dept_ids = dept_ids;
+    isAllSelected.value = false;
+  } else if (has_all_option && !isAllSelected.value) {
+    // 选中全部节点
+    isAllSelected.value = true;
+    const deptListData = deptList.value;
+    const dept_ids = [];
+    const loopSelectData = (list) => {
+      list.forEach((e) => {
+        dept_ids.push(e.id);
+        if (e.children && e.children.length > 0) {
+          loopSelectData(e.children);
+        }
       });
-    } else {
-      deptList.value.shift();
-    }
-  };
-  
-  // 选择级联选择器
-  const selectHandle = async (e = []) => {
-    const selectLength = e.filter((v) => v !== '全选').length;
-    const has_all_option = e.some((v) => v === '全选');
-    let dept_ids = [...permissionForm.dept_ids];
-  
-    if (has_all_option && isAllSelected.value) {
-      // 移除了选项中除全部外的某个节点
-      dept_ids = dept_ids.filter((v) => v !== '全选');
-      dept_ids.splice(searchSelectAllNodeIndex(), 1);
-      permissionForm.dept_ids = dept_ids;
-      isAllSelected.value = false;
-    } else if (has_all_option && !isAllSelected.value) {
-      // 选中全部节点
-      isAllSelected.value = true;
-      const deptListData = deptList.value;
-      const dept_ids = [];
-      const loopSelectData = (list) => {
-        list.forEach((e) => {
-          dept_ids.push(e.id);
-          if (e.children && e.children.length > 0) {
-            loopSelectData(e.children);
-          }
-        });
+    };
+    loopSelectData(deptListData);
+    permissionForm.dept_ids = dept_ids;
+  } else if (!has_all_option && isAllSelected.value) {
+    // 取消选中全部节点
+    isAllSelected.value = false;
+    permissionForm.dept_ids = [];
+  } else if (selectLength === oneDimensionalList.value.length) {
+    isAllSelected.value = true;
+    dept_ids.unshift('全选');
+    permissionForm.dept_ids = dept_ids;
+  }
+  nextTick(() => {
+    // 强制更新
+  });
+};
+/* 处理树形数据 */
+let buildTree = function buildTree(list, parent_id) {
+  const tree = [];
+  for (let i = 0; i < list.length; i++) {
+    if (list[i].parent_id === parent_id) {
+      const node = {
+        id: list[i].id,
+        parent_id: list[i].parent_id,
+        name: list[i].name,
+        create_time: list[i].create_time,
+        remark: list[i].remark,
+        sn: list[i].sn,
+        sort: list[i].sort,
+        type: list[i].type,
+        region_ids: list[i].region_ids,
+        children: buildTree(list, list[i].id),
       };
-      loopSelectData(deptListData);
-      permissionForm.dept_ids = dept_ids;
-    } else if (!has_all_option && isAllSelected.value) {
-      // 取消选中全部节点
-      isAllSelected.value = false;
-      permissionForm.dept_ids = [];
-    } else if (selectLength === oneDimensionalList.value.length) {
+      tree.push(node);
+    }
+  }
+  return tree;
+};
+/** 获取部门 */
+const GET_DeptList = () => {
+  API_Setting.getDeptList().then((response) => {
+    deptList.value = buildTree(response, '0');
+    const oneDimensionalListData = [];
+    const loopData = (list) => {
+      list.forEach((e) => {
+        oneDimensionalListData.push(e);
+        if (e.children && e.children.length > 0) {
+          loopData(e.children);
+        }
+      });
+    };
+    loopData(response);
+    oneDimensionalList.value = oneDimensionalListData;
+    if (permissionForm.dept_ids.length === oneDimensionalListData.length) {
+      // 设置数据范围全选
+      permissionForm.dept_ids.push('全选');
       isAllSelected.value = true;
-      dept_ids.unshift('全选');
-      permissionForm.dept_ids = dept_ids;
     }
-    nextTick(() => {
-      // 强制更新
-    });
-  };
-  /* 处理树形数据 */
-  let buildTree = function buildTree(list, parent_id) {
-    const tree = [];
-    for (let i = 0; i < list.length; i++) {
-      if (list[i].parent_id === parent_id) {
-        const node = {
-          id: list[i].id,
-          parent_id: list[i].parent_id,
-          name: list[i].name,
-          create_time: list[i].create_time,
-          remark: list[i].remark,
-          sn: list[i].sn,
-          sort: list[i].sort,
-          type: list[i].type,
-          region_ids: list[i].region_ids,
-          children: buildTree(list, list[i].id),
-        };
-        tree.push(node);
-      }
+  });
+};
+
+/** 判断是否还有子集 */
+const hasChildren = (item) => {
+  return Array.isArray(item.children) && item.children.length !== 0;
+};
+
+/** 全选 */
+const handleCheckAll = (checked) => {
+  allIndeterminate.value = false;
+  permissions.value = setPermissionsCheck(permissions.value, checked);
+};
+
+/** 选择 */
+const handleCheckboxChanged = (item, parent) => {
+  if (item.children && item.children.length) {
+    item.children = setPermissionsCheck(item.children, item.checked);
+  }
+  countAllPermissions(permissions.value);
+  countParentChecked(permissions.value);
+};
+
+/** 设置权限状态 */
+const setPermissionsCheck = (permissions, checked) => {
+  const perm = [...permissions];
+  perm.map((item) => {
+    item.checked = checked;
+    if (item.children && item.children.length) {
+      item.children = setPermissionsCheck(item.children, checked);
     }
-    return tree;
-  };
-  /** 获取部门 */
-  const GET_DeptList = () => {
-    API_Setting.getDeptList().then((response) => {
-      deptList.value = buildTree(response, '0');
-      const oneDimensionalListData = [];
-      const loopData = (list) => {
-        list.forEach((e) => {
-          oneDimensionalListData.push(e);
-          if (e.children && e.children.length > 0) {
-            loopData(e.children);
-          }
-        });
-      };
-      loopData(response);
-      oneDimensionalList.value = oneDimensionalListData;
-      if (permissionForm.dept_ids.length === oneDimensionalListData.length) {
-        // 设置数据范围全选
-        permissionForm.dept_ids.push('全选');
-        isAllSelected.value = true;
-      }
-    });
-  };
-  
-  /** 判断是否还有子集 */
-  const hasChildren = (item) => {
-    return Array.isArray(item.children) && item.children.length !== 0;
-  };
-  
-  /** 全选 */
-  const handleCheckAll = (checked) => {
-    allIndeterminate.value = false;
-    permissions.value = setPermissionsCheck(permissions.value, checked);
-  };
-  
-  /** 选择 */
-  const handleCheckboxChanged = (item, parent) => {
+  });
+  return perm;
+};
+
+/** 检测是否有不确定性 */
+const checkIndeterminate = (permissions) => {
+  if (!Array.isArray(permissions)) return false;
+  const _len = permissions.length;
+  const __len = permissions.filter((item) => item.checked).length;
+  return __len !== 0 && _len !== __len;
+};
+
+/** 获取所有权限展开后的长度、被选中的长度 */
+const countAllPermissions = (perms?: any[]) => {
+  const currentPermissions = perms || permissions.value;
+  if (!Array.isArray(currentPermissions)) {
+    console.warn('permissions is not an array:', currentPermissions);
+    return [];
+  }
+  const _list = [];
+  currentPermissions.forEach((item) => {
+    _list.push(item);
+    if (item.children) _list.push(...countAllPermissions(item.children));
+  });
+  const length = _list.length;
+  const length_checked = _list.filter((_item) => _item.checked).length;
+  allCheck.value = length === length_checked;
+  allIndeterminate.value = length_checked !== 0 && length !== length_checked;
+  return _list;
+};
+
+/** 计算所有父辈的选中状态 */
+const countParentChecked = (perms?: any[]) => {
+  const currentPermissions = perms || permissions.value;
+  currentPermissions.forEach((item) => {
     if (item.children && item.children.length) {
-      item.children = setPermissionsCheck(item.children, item.checked);
+      countParentChecked(item.children);
+      const length = item.children.length;
+      const checked_length = item.children.filter((_item) => _item.checked).length;
+      item.checked = !!checked_length;
     }
-    countAllPermissions();
-    countParentChecked();
-  };
-  
-  /** 设置权限状态 */
-  const setPermissionsCheck = (permissions, checked) => {
-    const perm = [...permissions];
-    perm.map((item) => {
-      item.checked = checked;
-      if (item.children && item.children.length) {
-        item.children = setPermissionsCheck(item.children, checked);
-      }
-    });
-    return perm;
-  };
-  
-  /** 检测是否有不确定性 */
-  const checkIndeterminate = (permissions) => {
-    if (!Array.isArray(permissions)) return false;
-    const _len = permissions.length;
-    const __len = permissions.filter((item) => item.checked).length;
-    return __len !== 0 && _len !== __len;
-  };
-  
-  /** 获取所有权限展开后的长度、被选中的长度 */
-  const countAllPermissions = (permissions) => {
-    permissions = permissions || permissions.value;
-    const _list = [];
-    permissions.forEach((item) => {
-      _list.push(item);
-      if (item.children) _list.push(...countAllPermissions(item.children));
-    });
-    const length = _list.length;
-    const length_checked = _list.filter((_item) => _item.checked).length;
-    allCheck.value = length === _list.filter((_item) => _item.checked).length;
-    allIndeterminate.value = length_checked !== 0 && length !== length_checked;
-    return _list;
-  };
-  
-  /** 计算所有父辈的选中状态 */
-  const countParentChecked = (permissions) => {
-    permissions = permissions || permissions.value;
-    permissions.forEach((item) => {
-      if (item.children && item.children.length) {
-        countParentChecked(item.children);
-        const length = item.children.length;
-        const checked_length = item.children.filter((_item) => _item.checked).length;
-        item.checked = !!checked_length;
+  });
+};
+
+/** 保存角色权限 */
+const permissionFormRef = ref();
+const saveRolePermission = () => {
+  permissionFormRef.value.validate((valid) => {
+    if (valid) {
+      let params = {
+        ...permissionForm,
+        menus: permissions.value,
+      };
+      if (!params.dept_ids) {
+        ElMessage.error('请选择数据范围!');
+        return;
       }
-    });
-  };
-  
-  /** 保存角色权限 */
-  const permissionFormRef = ref();
-  const saveRolePermission = () => {
-    permissionFormRef.value.validate((valid) => {
-      if (valid) {
-        let params = {
-          ...permissionForm,
-          menus: permissions.value,
-        };
-        if (!params.dept_ids) {
-          ElMessage.error('请选择数据范围!');
-          return;
-        }
-        params.dept_ids = params.dept_ids.filter((v) => v !== '全选');
-        if (params.dept_ids) {
-          if (isAllSelected.value) {
-            params.data_scope = 'ALL';
-            params.dept_ids = params.dept_ids.join(',');
-          } else {
-            params.data_scope = 'DEPT_CUSTOM';
-            params.dept_ids = params.dept_ids.join(',');
-          }
+      params.dept_ids = params.dept_ids.filter((v) => v !== '全选');
+      if (params.dept_ids) {
+        if (isAllSelected.value) {
+          params.data_scope = 'ALL';
+          params.dept_ids = params.dept_ids.join(',');
+        } else {
+          params.data_scope = 'DEPT_CUSTOM';
+          params.dept_ids = params.dept_ids.join(',');
         }
-        const saveSuccess = () => {
-          ElMessage.success('保存成功!');
-          router.push({ name: 'roleManage' });
-        };
-        role_id.value === "0" ? API_Auth.addRole(params).then(() => saveSuccess()) : API_Auth.editRole(role_id.value, params).then(() => saveSuccess());
-      } else {
-        ElMessage.error('表单填写有误,请检查!');
-        return false;
       }
-    });
-  };
-  
-  /** 获取权限菜单树 */
-  const GET_RolePermission = () => {
-    API_Menus.getMenusChildren().then((res) => {
-      if (role_id.value !== '0') {
-        API_Auth.getRolePermission(role_id.value).then((response) => {
-          role_id.value = response.role_id;
-          permissionForm.data_scope = response.data_scope;
-          permissionForm.role_name = response.role_name;
-          permissionForm.role_describe = response.role_describe;
-          permissionForm.dept_ids = response.dept_ids.split(',');
-          const checkedIds = expandRouters(response.menus);
-          permissions.value = filterRoleRouter(res, checkedIds);
+      const saveSuccess = () => {
+        ElMessage.success('保存成功!');
+        router.push({ name: 'roleManage' });
+      };
+      role_id.value === '0' ? API_Auth.addRole(params).then(() => saveSuccess()) : API_Auth.editRole(role_id.value, params).then(() => saveSuccess());
+    } else {
+      ElMessage.error('表单填写有误,请检查!');
+      return false;
+    }
+  });
+};
+
+/** 获取权限菜单树 */
+const GET_RolePermission = () => {
+  API_Menus.getMenusChildren().then((res) => {
+    if (role_id.value !== '0') {
+      API_Auth.getRolePermission(role_id.value).then((response) => {
+        // ... existing code ...
+        const checkedIds = expandRouters(response.menus);
+        permissions.value = filterRoleRouter(res, checkedIds);
+        if (Array.isArray(permissions.value)) {
           countAllPermissions();
-          GET_DeptList();
-        });
-      } else {
-        permissions.value = res;
-        countAllPermissions();
+        } else {
+          console.warn('Invalid permissions data:', permissions.value);
+        }
         GET_DeptList();
-      }
-    });
-  };
-  
-  /** 展开路由的identifier */
-  const expandRouters = (menus) => {
-    const routers = [];
-    menus.forEach((item) => {
-      item.checked && routers.push(item.identifier);
-      if (item.children && item.children.length) {
-        routers.push(...expandRouters(item.children));
-      }
-    });
-    return routers;
-  };
-  
-  /** 递归筛选被选中的路由 */
-  const filterRoleRouter = (routers, ids) => {
-    const _routers = [];
-    routers.forEach((item) => {
-      if (ids.includes(item.identifier) || item.hidden) {
-        item.checked = true;
+      });
+    } else {
+      permissions.value = res;
+      if (Array.isArray(permissions.value)) {
+        countAllPermissions();
       } else {
-        item.checked = false;
-      }
-      if (item.children) {
-        item.children = filterRoleRouter(item.children, ids);
+        console.warn('Invalid permissions data:', permissions.value);
       }
-      _routers.push(item);
-    });
-    return _routers;
-  };
-  
-  onMounted(() => {
-    GET_RolePermission();
+      GET_DeptList();
+    }
+  });
+};
+
+/** 展开路由的identifier */
+const expandRouters = (menus) => {
+  const routers = [];
+  menus.forEach((item) => {
+    item.checked && routers.push(item.identifier);
+    if (item.children && item.children.length) {
+      routers.push(...expandRouters(item.children));
+    }
   });
-  
-  watch(
-    () => route.query.id,
-    () => {
-      role_id.value = route.query.id;
-      GET_RolePermission();
+  return routers;
+};
+
+/** 递归筛选被选中的路由 */
+const filterRoleRouter = (routers, ids) => {
+  const _routers = [];
+  routers.forEach((item) => {
+    if (ids.includes(item.identifier) || item.hidden) {
+      item.checked = true;
+    } else {
+      item.checked = false;
+    }
+    if (item.children) {
+      item.children = filterRoleRouter(item.children, ids);
     }
-  );
-  </script>
+    _routers.push(item);
+  });
+  return _routers;
+};
+
+onMounted(() => {
+  GET_RolePermission();
+});
+
+watch(
+  () => route.query.id,
+  () => {
+    role_id.value = route.query.id;
+    GET_RolePermission();
+  }
+);
+</script>
     
   <style scoped>
-  .permission-container {
-    padding: 10px;
-    background-color: #fff;
-    padding-bottom: 150px;
-  }
-  
-  .level_1 {
-    padding: 15px 0;
-    border-bottom: 1px dashed #e7e7e7;
-  }
-  
-  .level_1:last-child {
-    border-bottom: none;
-  }
-  
-  ::v-deep .el-form-item__label {
-    padding-top: 15px;
-  }
-  
-  ::v-deep .el-form-item__content {
-    display: block;
-    border-left: 1px solid #e7e7e7;
-    padding-left: 20px;
-    padding-top: 15px;
-  }
-  
-  ::v-deep .el-form-item__content .el-form-item__error {
-    padding-left: 20px;
-  }
-  
-  ::v-deep .el-form-item:last-child .el-form-item__content {
-    padding-top: 0;
-  }
-  
-  ::v-deep .el-form-item:not(:first-child) {
-    border-top: 1px solid #e7e7e7;
-    position: relative;
-  }
-  
-  ::v-deep .el-form-item:not(:first-child)::after {
-    content: ' ';
-    width: 1px;
-    height: 22px;
-    background-color: #e7e7e7;
-    position: absolute;
-    top: -22px;
-    left: 200px;
-  }
-  
-  ::v-deep .el-button-group {
-    display: inline-block;
-  }
-  
-  ::v-deep .el-button-group .el-button {
-    display: inline-block;
-    padding: 0;
-    border: none;
-  }
-  
-  ::v-deep .el-button-group .el-button:focus,
-  ::v-deep .el-button-group .el-button:hover {
-    color: #606266;
-    border-color: #fff;
-    background-color: #fff;
-  }
-  
-  .checkbox-dropdown {
-    display: inline-block;
-    min-width: 130px;
-    cursor: pointer;
-  }
-  
-  .checkbox-dropdown .checked {
-    color: #409eff;
-  }
-  
-  .role-dropdown {
-    display: flex;
-    align-items: center;
-    width: 25%;
-    height: 30px;
-    margin-bottom: 10px;
-  }
-  
-  .role-dropdown:last-child {
-    margin-bottom: 0;
-  }
-  
-  .role-dropdown__label {
-    display: flex;
-    align-items: center;
-    width: 100%;
-  }
-  
-  ::v-deep .role-dropdown__label .el-checkbox__label {
-    color: inherit;
-  }
-  
-  .role-dropdown__list {
-    max-height: 320px;
-    overflow-y: auto;
-  }
-  </style>
+.permission-container {
+  padding: 10px;
+  background-color: #fff;
+  padding-bottom: 150px;
+}
+
+.level_1 {
+  padding: 15px 0;
+  border-bottom: 1px dashed #e7e7e7;
+}
+
+.level_1:last-child {
+  border-bottom: none;
+}
+
+::v-deep .el-form-item__label {
+  padding-top: 15px;
+}
+
+::v-deep .el-form-item__content {
+  display: block;
+  border-left: 1px solid #e7e7e7;
+  padding-left: 20px;
+  padding-top: 15px;
+}
+
+::v-deep .el-form-item__content .el-form-item__error {
+  padding-left: 20px;
+}
+
+::v-deep .el-form-item:last-child .el-form-item__content {
+  padding-top: 0;
+}
+
+::v-deep .el-form-item:not(:first-child) {
+  border-top: 1px solid #e7e7e7;
+  position: relative;
+}
+
+::v-deep .el-form-item:not(:first-child)::after {
+  content: ' ';
+  width: 1px;
+  height: 22px;
+  background-color: #e7e7e7;
+  position: absolute;
+  top: -22px;
+  left: 200px;
+}
+
+::v-deep .el-button-group {
+  display: inline-block;
+}
+
+::v-deep .el-button-group .el-button {
+  display: inline-block;
+  padding: 0;
+  border: none;
+}
+
+::v-deep .el-button-group .el-button:focus,
+::v-deep .el-button-group .el-button:hover {
+  color: #606266;
+  border-color: #fff;
+  background-color: #fff;
+}
+
+.checkbox-dropdown {
+  display: inline-block;
+  min-width: 130px;
+  cursor: pointer;
+}
+
+.checkbox-dropdown .checked {
+  color: #409eff;
+}
+
+.role-dropdown {
+  display: flex;
+  align-items: center;
+  width: 25%;
+  height: 30px;
+  margin-bottom: 10px;
+}
+
+.role-dropdown:last-child {
+  margin-bottom: 0;
+}
+
+.role-dropdown__label {
+  display: flex;
+  align-items: center;
+  width: 100%;
+}
+
+::v-deep .role-dropdown__label .el-checkbox__label {
+  color: inherit;
+}
+
+.role-dropdown__list {
+  max-height: 320px;
+  overflow-y: auto;
+}
+</style>

+ 224 - 0
src/views/setting/systemLog.vue

@@ -0,0 +1,224 @@
+<template>
+  <div>
+    <en-table-layout :tableData="tableData.data" :loading="loading">
+      <template #toolbar>
+        <div class="inner-toolbar inner-toolbar2">
+          <div class="toolbar-btns"></div>
+          <en-table-search @search="searchEvent" @advancedSearch="advancedSearchEvent" advanced advancedWidth="465" placeholder="账号/操作内容/IP地址">
+            <template #advanced-content>
+              <el-form ref="advancedFormRef" :model="advancedForm" label-width="80px">
+                <el-form-item label="日志级别">
+                  <el-select v-model="advancedForm.level" placeholder="请选择" clearable>
+                    <el-option label="一般" value="normal" />
+                    <el-option label="重要" value="important" />
+                  </el-select>
+                </el-form-item>
+                <el-form-item label="操作时间">
+                  <el-date-picker
+                    v-model="advancedForm.order_time_range"
+                    type="daterange"
+                    align="center"
+                    :editable="false"
+                    unlink-panels
+                    :default-time="['00:00:00', '23:59:59']"
+                    range-separator="-"
+                    start-placeholder="开始日期"
+                    end-placeholder="结束日期"
+                    value-format="x"
+                    :disabled-date="disabledDate"
+                  >
+                  </el-date-picker>
+                </el-form-item>
+              </el-form>
+            </template>
+          </en-table-search>
+        </div>
+      </template>
+
+      <template #table-columns>
+        <el-table-column prop="operator_name" label="账号" />
+        <el-table-column prop="operate_detail" label="操作描述" />
+        <el-table-column label="操作时间" align="center">
+          <template #default="scope">{{ scope.row.operate_time }}</template>
+        </el-table-column>
+        <el-table-column prop="operate_ip" label="IP地址" />
+        <el-table-column label="日志级别" align="center">
+          <template #default="scope">{{ scope.row.level === 'important' ? '重要' : '一般' }}</template>
+        </el-table-column>
+        <el-table-column label="操作">
+          <template #default="scope">
+            <el-button size="small" type="primary" @click="handleShowDetail(scope.row)" v-if="checkPermission(['systemLogs:detail'])">查看</el-button>
+          </template>
+        </el-table-column>
+      </template>
+      <template #pagination>
+        <el-pagination
+          v-if="tableData.data_total"
+          @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="日志详情" v-model="showLogDetail" width="40%">
+      <div class="logInfo">账号:{{ currDetailLog.operator_name }}</div>
+      <div class="logInfo">操作描述:{{ currDetailLog.operate_detail }}</div>
+      <div class="logInfo">请求方法:{{ currDetailLog.method }}</div>
+      <div class="logInfo">请求参数:{{ currDetailLog.params }}</div>
+      <div class="logInfo">ip地址:{{ currDetailLog.operate_ip }}</div>
+      <div class="logInfo">日志级别:{{ currDetailLog.level === 'important' ? '重要' : '一般' }}</div>
+      <div class="logInfo">操作时间:{{ unixToDate(currDetailLog.operate_time) }}</div>
+    </el-dialog>
+  </div>
+</template>
+  
+  <script lang="ts"setup>
+import { ref, reactive, onMounted } from 'vue';
+import * as API_Setting from '@/api/setting';
+
+const loading = ref(false);
+const params = reactive({
+  page_no: 1,
+  page_size: 10,
+});
+const tableData = ref<any>({});
+const searchTime = ref('');
+const showLogDetail = ref(false);
+const currDetailLog = ref<any>({});
+const advancedForm = reactive({});
+const advancedFormRef = ref(null);
+
+const handlePageSizeChange = (size) => {
+  params.page_size = size;
+  GET_SystemLogs();
+};
+
+const handlePageCurrentChange = (page) => {
+  params.page_no = page;
+  GET_SystemLogs();
+};
+
+const searchEvent = (data) => {
+  Object.assign(params, { keyword: data });
+  Object.keys(advancedForm).forEach((key) => delete params[key]);
+  params.page_no = 1;
+  GET_SystemLogs();
+};
+
+const advancedSearchEvent = () => {
+  Object.assign(params, advancedForm);
+  delete params.start_time;
+  delete params.end_time;
+  if (advancedForm.order_time_range) {
+    params.start_time = advancedForm.order_time_range[0] / 1000;
+    params.end_time = advancedForm.order_time_range[1] / 1000;
+  }
+  delete params.keyword;
+  delete params.order_time_range;
+  params.page_no = 1;
+  GET_SystemLogs();
+};
+
+const GET_SystemLogs = () => {
+  loading.value = true;
+  const clonedParams = JSON.parse(JSON.stringify(params));
+  Object.keys(clonedParams).forEach((key) => {
+    if (!clonedParams[key] && clonedParams[key] !== 0) {
+      delete clonedParams[key];
+    }
+  });
+  API_Setting.getSystemLogs(clonedParams)
+    .then((response) => {
+      loading.value = false;
+      tableData.value = response;
+      params.page_no = response.page_no;
+      params.page_size = response.page_size;
+    })
+    .catch(() => {
+      loading.value = false;
+    });
+};
+
+const handleShowDetail = (row) => {
+  showLogDetail.value = true;
+  currDetailLog.value = row;
+};
+
+const checkPermission = (permissions) => {
+  // 实现权限检查逻辑
+  return true;
+};
+
+const unixToDate = (unix) => {
+  // 实现 unix 时间戳转日期的逻辑
+  return new Date(unix * 1000).toLocaleString();
+};
+
+const disabledDate = (time) => {
+  return time.getTime() > Date.now();
+};
+
+onMounted(() => {
+  GET_SystemLogs();
+});
+</script>
+  
+  <style>
+.inner-toolbar2 {
+  justify-content: flex-end !important;
+}
+
+.inner-toolbar2 .dateSearch {
+  margin-right: 20px;
+}
+
+.toolbar-search {
+  display: flex;
+  width: 400px;
+}
+
+.toolbar-search .el-button {
+  margin-left: 18px;
+}
+
+.nc-container {
+  height: 32px !important;
+  overflow: hidden;
+}
+
+.face-image {
+  display: block;
+  width: 50px;
+  height: 50px;
+  margin: 0 auto;
+}
+
+.el-dialog__body {
+  padding: 10px 20px;
+}
+
+.img-code {
+  width: 80%;
+}
+
+.img-code .el-input-group__append {
+  padding: 0;
+  margin: 0;
+}
+
+.img-code .el-input-group__append img {
+  display: block;
+  height: 30px;
+  cursor: pointer;
+}
+
+.logInfo {
+  margin-bottom: 20px;
+}
+</style>

+ 20 - 14
src/views/stock/goodsLend.vue

@@ -78,8 +78,8 @@
         <el-table-column prop="bind_num" label="已绑定SKU数" />
         <el-table-column label="操作" width="250">
           <template #default="scope">
-            <el-button size="mini" v-if="checkPermission(['inventoryItem'])" @click="introGoodsLend(scope.row)">查看</el-button>
-            <el-button size="mini" v-if="checkPermission(['bindAreaStock:update'])" @click="AddGoodsLend(scope.row)">绑定商品</el-button>
+            <!-- <el-button size="mini" v-if="checkPermission(['inventoryItem'])" @click="introGoodsLend(scope.row)">查看</el-button>
+            <el-button size="mini" v-if="checkPermission(['bindAreaStock:update'])" @click="AddGoodsLend(scope.row)">绑定商品</el-button> -->
             <el-button size="mini" v-if="checkPermission(['goodsLend:edit'])" @click="handleEditGoodsLend(scope.row)">编辑</el-button>
             <el-button type="success" size="mini" style="margin-top: 5px" v-if="checkPermission(['goodsLend:del'])" @click="handleReturnGoodsLend(scope.row)"
               >删除</el-button
@@ -114,14 +114,15 @@
         </div>
       </div>
     </el-dialog>
-    <el-dialog :title="title" v-model="dialogVisibleForm" width="940px" center @close="addHandleCancle" style="width: 30%;border-radius: 10px">
-      <el-form :model="addLendForm" ref="addLendFormRef"  :rules="rules" label-width="90px">
+    <el-dialog :title="title" v-model="dialogVisibleForm" width="940px" center @close="addHandleCancle" style="width: 30%; border-radius: 10px">
+      <el-form :model="addLendForm" ref="addLendFormRef" :rules="rules" label-width="90px">
         <el-form-item label="仓库" prop="warehouse_id">
           <el-select v-model="addLendForm.warehouse_id" clearable @change="selWarehouse">
             <el-option v-for="item in warehouseList" :key="item.id" :label="item.name" :value="item.id" />
           </el-select>
           <span style="color: red; margin-left: 20px; cursor: pointer; text-decoration: underline" v-if="warehouseList.length == 0" @click="gowareHouse"
-            >新增仓库</span>
+            >新增仓库</span
+          >
         </el-form-item>
         <el-form-item label="库位区域" prop="type">
           <el-select v-model="addLendForm.type" clearable @change="selAreaTableData">
@@ -152,9 +153,9 @@ import Print from 'print-js';
 import * as API_Setting from '@/api/setting';
 import { useRouter, useRoute } from 'vue-router';
 import QRCode from 'qrcode';
-import EnTableLayout from '@/components/TableLayout/index.vue';
-import EnTableSearch from '@/components/TableSearch/index.vue';
-import { ElMessageBox } from 'element-plus';
+/* import EnTableLayout from '@/components/TableLayout/index.vue';
+import EnTableSearch from '@/components/TableSearch/index.vue'; */
+import { ElMessageBox, ElMessage } from 'element-plus';
 
 const router = useRouter();
 const route = useRoute();
@@ -178,7 +179,7 @@ const addLendForm = reactive({
   warehouse_id: '',
   type: '',
   code: '',
-  intro: ''
+  intro: '',
 });
 
 const rules = reactive({
@@ -253,9 +254,11 @@ const selAreaTableData = (val) => {
 };
 
 const lendCommit = () => {
-  addLendForm.value.code = warehouseSn.value + addLendForm.value.type + '-' + addLendForm.value.code;
+  console.log(addLendForm);
+  addLendForm.code = warehouseSn.value +'-'+ addLendForm.type + '-' + addLendForm.code;
+
   if (title.value === '新增') {
-    API_GoodsLend.create(addLendForm.value).then(() => {
+    API_GoodsLend.create(addLendForm).then(() => {
       ElMessage.success('添加成功!');
       dialogVisibleForm.value = false;
       warehouseSn.value = '';
@@ -263,7 +266,7 @@ const lendCommit = () => {
       GET_GoodsLendList();
     });
   } else if (title.value === '编辑') {
-    API_GoodsLend.update(addLendForm.value).then(() => {
+    API_GoodsLend.update(addLendForm).then(() => {
       ElMessage.success('修改成功!');
       dialogVisibleForm.value = false;
       addLendForm.value = {};
@@ -303,8 +306,11 @@ const handleEditGoodsLend = (row) => {
   const str = row.code;
   warehouseSn.value = str.split('-')[0];
   AreaTab.value = str.split('-')[1];
-  addLendForm.value = { ...row };
-  addLendForm.value.code = str.split('-').pop();
+  // 使用 Object.assign 或者手动赋值来更新 reactive 对象的属性
+  Object.assign(addLendForm, row);
+  console.log(warehouseSn.value,AreaTab.value);
+  addLendForm.code = str.split('-').pop();
+
   dialogVisibleForm.value = true;
 };
 

+ 5 - 3
src/views/stock/warehouseList.vue

@@ -186,13 +186,15 @@ const submitWarehouseForm = () => {
       if (id) {
         API_BasicSetting.editWarehouse(id, params).then(() => {
           dialogVisible.value = false;
-          store.dispatch('showMessage', { message: '修改成功!', type: 'success' });
+          ElMessage.success('修改成功!')
+          //store.dispatch('showMessage', { message: '修改成功!', type: 'success' });
           GET_WarehouseList();
         });
       } else {
         API_BasicSetting.addWarehouse(params).then(() => {
           dialogVisible.value = false;
-          store.dispatch('showMessage', { message: '添加成功!', type: 'success' });
+          ElMessage.success('添加成功!')
+     /*      store.dispatch('showMessage', { message: '添加成功!', type: 'success' }); */
           GET_WarehouseList();
         });
       }
@@ -241,6 +243,6 @@ const adminName = (val: string[]) => {
 
 onMounted(() => {
   GET_WarehouseList();
-  GET_MarketingList();
+  //GET_MarketingList();
 });
 </script>

+ 322 - 0
src/views/stock/warehouseLocation.vue

@@ -0,0 +1,322 @@
+<template>
+  <div>
+    <en-table-layout :tableData="tableData" :loading="loading" @selection-change="handleSelectionChange">
+      <template #toolbar>
+        <div class="inner-toolbar">
+          <div class="toolbar-btns">
+            <el-button size="mini" v-if="checkPermission(['createAreaType:add'])" type="primary" @click="handleAddGoodsLend">新增</el-button>
+          </div>
+          <div class="toolbar-search"></div>
+        </div>
+      </template>
+
+      <template #table-columns>
+        <el-table-column type="selection" width="55" />
+        <el-table-column prop="code" label="区域编号" />
+        <el-table-column prop="name" label="名称" />
+        <el-table-column prop="remark" label="备注" />
+        <el-table-column label="操作" width="250">
+          <template #default="scope">
+            <el-button size="mini" v-if="checkPermission(['updateAreaType:update'])" @click="handleEditGoodsLend(scope.row)">编辑</el-button>
+            <el-button type="success" size="mini" v-if="checkPermission(['removeAreaType:delete'])" @click="handleReturnGoodsLend(scope.row)">删除</el-button>
+          </template>
+        </el-table-column>
+      </template>
+    </en-table-layout>
+    <el-dialog :title="title" v-model="dialogVisibleForm" width="30%" center @close="addHandleCancle">
+      <el-form ref="addLendFormRef" :model="addLendForm" :rules="rules" label-width="90px">
+        <el-form-item label="编号" prop="code">
+          <el-input placeholder="请输入编号" v-model="addLendForm.code" />
+        </el-form-item>
+        <el-form-item label="名称" prop="name">
+          <el-input placeholder="请输入名称" v-model="addLendForm.name" />
+        </el-form-item>
+        <el-form-item label="备注">
+          <el-input type="textarea" :autosize="{ minRows: 2, maxRows: 4 }" placeholder="请输入内容" v-model="addLendForm.remark" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="addHandleCancle">取 消</el-button>
+        <el-button type="primary" @click="lendCommit">确 定</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+  
+  <script lang="ts" setup>
+import { ref, reactive, onMounted, watch, nextTick, getCurrentInstance } from 'vue';
+import { ElMessageBox, FormInstance, ElMessage } from 'element-plus';
+import * as API_GoodsLend from '@/api/goodsLend';
+import * as API_Setting from '@/api/setting';
+/* import Print from 'print-js';
+import QRCode from 'qrcodejs2'; */
+
+const { proxy } = getCurrentInstance();
+
+const checkPermission = (value: string[]): boolean => {
+  return proxy.checkPermission(value);
+};
+
+const warehouseSn = ref('');
+const warehouseList = ref([]);
+const loading = ref(false);
+const params = reactive({
+  page_no: 1,
+  page_size: 10,
+});
+const advancedForm = reactive({});
+const tableData = ref([]);
+const dialogVisible = ref(false);
+const multipleSelection = ref([]);
+const goodsLendForm = ref<any>({});
+const codeList = ref<any>([]);
+const title = ref('');
+const dialogVisibleForm = ref(false);
+let addLendForm = ref<any>({
+  name: '',
+  remark: '',
+  code: '',
+});
+const rules = reactive({
+  name: [{ required: true, message: '请选择输入名称', trigger: 'blur' }],
+  code: [{ required: true, message: '请选择输入编号', trigger: 'blur' }],
+});
+const typeList = ref([]);
+const id = ref('');
+const addLendFormRef = ref();
+
+const lendCommit = () => {
+  addLendFormRef.value.validate((valid) => {
+    if (valid) {
+      if (title.value === '新增库位区域') {
+       // addLendForm.value.code = warehouseSn.value + '-' + addLendForm.value.code;
+        API_GoodsLend.addCreateAreaType(addLendForm.value).then(() => {
+          ElMessage.success('添加成功!');
+          dialogVisibleForm.value = false;
+          warehouseSn.value = '';
+          addLendForm.value.name = '';
+          addLendForm.value.remark = '';
+          GET_GoodsLendList();
+        });
+      } else if (title.value === '编辑库位区域') {
+        API_GoodsLend.editUpdateAreaType(id.value, addLendForm.value).then(() => {
+          ElMessage.success('修改成功!');
+          dialogVisibleForm.value = false;
+          id.value = '';
+          addLendForm.value.name = '';
+          addLendForm.value.remark = '';
+          GET_GoodsLendList();
+        });
+      }
+    } else {
+      ElMessage.error('提交失败');
+      return false;
+    }
+  });
+};
+
+const addHandleCancle = () => {
+  dialogVisibleForm.value = false;
+  addLendForm.value.name = '';
+  addLendForm.value.remark = '';
+};
+
+const handleCancle = () => {
+  dialogVisible.value = false;
+  codeList.value = [];
+};
+
+/* const creatQrCode = () => {
+  nextTick(() => {
+    codeList.value.forEach((item, index) => {
+      const codeItem = document.querySelectorAll('.code-item')[index];
+      codeItem.innerHTML = '';
+      new QRCode(codeItem, {
+        text: item,
+        width: 600,
+        height: 660,
+        render: 'table',
+        colorDark: '#333333',
+        colorLight: '#ffffff',
+        correctLevel: QRCode.CorrectLevel.H,
+      });
+    });
+  });
+}; */
+
+const handleEditGoodsLend = (row) => {
+  id.value = row.id;
+  title.value = '编辑库位区域';
+  addLendForm.value.code = row.code;
+  addLendForm.value.name = row.name;
+  addLendForm.value.remark = row.remark;
+  dialogVisibleForm.value = true;
+};
+
+const handleReturnGoodsLend = (row) => {
+  ElMessageBox.confirm('确定要删除吗?', '提示', { type: 'warning' })
+    .then(() => {
+      API_GoodsLend.removeAreaType(row.id).then(() => {
+        ElMessage.success('删除成功!');
+        GET_GoodsLendList();
+      });
+    })
+    .catch(() => {});
+};
+
+const statusFilter = (val) => {
+  const obj = typeList.value.filter((el) => el.value === val);
+  return obj.length !== 0 ? obj[0].label : '';
+};
+
+const handleConfirmGoodsLend = () => {
+  if (!multipleSelection.value.length) return ElMessage.error('请先选择!');
+  const ids = multipleSelection.value.map((item: any) => item.code);
+  codeList.value = ids;
+  dialogVisible.value = true;
+  /*   creatQrCode(); */
+};
+
+const GET_DeptList = () => {
+  API_Setting.getListAll().then((response) => {
+    warehouseList.value = response;
+  });
+  API_Setting.getPage({ dictType: 'Warehouse_location_type', page_no: 1, page_size: 50 }).then((res) => {
+    typeList.value = res.data;
+  });
+};
+
+const handlePrintGoodsLend = () => {
+  /*  Print({
+    printable: 'deliverySheet',
+    type: 'html',
+    targetStyles: ['*'],
+    ignoreElements: ['no-logs', 'goods-image', 'no-btn', 'elementToIgnore'],
+  }); */
+};
+
+const handleDeleteGoodsLend = () => {
+  if (!multipleSelection.value.length) return ElMessage.error('请先选择!');
+  ElMessageBox.confirm('确定要删除这些借出单吗?', '提示', { type: 'warning' })
+    .then(() => {
+      const ids = multipleSelection.value.map((item) => item.id);
+      API_GoodsLend.deleteGoodsLend(ids).then(() => {
+        ElMessage.success('删除成功!');
+        GET_GoodsLendList();
+      });
+    })
+    .catch(() => {});
+};
+
+const handleSelectionChange = (val) => {
+  multipleSelection.value = val;
+};
+
+const handlePageSizeChange = (size) => {
+  params.page_size = size;
+  GET_GoodsLendList();
+};
+
+const handlePageCurrentChange = (page) => {
+  params.page_no = page;
+  GET_GoodsLendList();
+};
+
+const handleAddGoodsLend = () => {
+  title.value = '新增库位区域';
+  addLendForm.value = {};
+  dialogVisibleForm.value = true;
+};
+
+const handlePrintGoodsLendDialog = (row) => {
+  dialogVisible.value = true;
+  codeList.value.push(row.code);
+  /*   creatQrCode(); */
+};
+
+const GET_GoodsLendDetail = (id) => {
+  API_GoodsLend.getGoodsLendInfo(id).then((response) => {
+    goodsLendForm.value = response;
+    if (response.product_list && response.product_list.length) {
+      let str = '';
+      response.product_list.forEach((i) => {
+        str += i.product_name + '、';
+      });
+      goodsLendForm.value.illustrate = str;
+    }
+  });
+};
+
+const searchEvent = (data) => {
+  params.sn = data;
+  Object.keys(advancedForm).forEach((key) => delete params[key]);
+  params.page_no = 1;
+  GET_GoodsLendList();
+};
+
+const advancedSearchEvent = () => {
+  Object.assign(params, advancedForm);
+  delete params.start_time;
+  delete params.end_time;
+  if (advancedForm.goodsLend_time_range) {
+    params.start_time = parseInt(Number(advancedForm.goodsLend_time_range[0]) / 1000);
+    params.end_time = parseInt(Number(advancedForm.goodsLend_time_range[1]) / 1000);
+  }
+  delete params.goodsLend_time_range;
+  params.page_no = 1;
+  GET_GoodsLendList();
+};
+
+const GET_GoodsLendList = () => {
+  loading.value = true;
+  API_GoodsLend.getListAreaType()
+    .then((response) => {
+      loading.value = false;
+      tableData.value = response;
+    })
+    .catch(() => {
+      loading.value = false;
+    });
+};
+onMounted(() => {
+  GET_GoodsLendList();
+  GET_DeptList();
+});
+</script>
+  
+  <style scoped>
+.print {
+  margin-left: 790px;
+}
+
+.tips-t {
+  display: flex;
+  padding: 10px 0;
+}
+.tips-t div {
+  flex: 1;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  flex-direction: column;
+}
+
+.tips-f {
+  padding: 20px 0;
+}
+
+.face-image {
+  display: block;
+  width: 50px;
+  height: 50px;
+  margin: 0 auto;
+}
+
+::v-deep .el-dialog__body {
+  padding: 10px 20px;
+}
+
+::v-deep .el-form .el-form-item__content {
+  display: flex;
+}
+</style>