CzRger 1 周之前
父節點
當前提交
8127c5954c

+ 5 - 0
src/api/modules/center/user.ts

@@ -8,3 +8,8 @@ export const userAdd = (params) => post(`/user`, params, {})
 export const userEdit = (params) => put(`/user`, params, {})
 // 用户管理-详情
 export const userDetail = (id) => get(`/user/${id}`, {}, {})
+// 用户管理-删除
+export const userDel = (id) => del(`/user/${id}`, {}, {})
+// 用户管理-生成邀请链接
+export const accountGenerateFriendlyCode = (params) =>
+  get(`/account/generateFriendlyCode`, params, {})

+ 5 - 0
src/api/modules/global/login.ts

@@ -5,3 +5,8 @@ export const loginCaptcha = () => get(`/login/captcha`, {}, {})
 export const loginSubmit = (params) => post(`/login/submit`, params, {})
 // 获取用户信息
 export const userInfo = () => get(`/user/info`, {}, {})
+// 切换租户
+export const loginChangeTenant = (id) =>
+  post(`/login/change/tenant/${id}`, {}, {})
+// 退出登录
+export const loginLogout = () => post(`/login/logout`, {}, {})

+ 76 - 4
src/layout/top-left/head/index.vue

@@ -19,11 +19,44 @@
         </div>
       </template>
     </div>
-    <div class="ml-auto">
+    <div class="mr-4 ml-auto">
       <el-button type="primary" @click="state.drawer = true"
         >临时字典</el-button
       >
     </div>
+    <el-dropdown :disabled="DictionaryStore.tenants.list.length < 2">
+      <div class="__hover flex items-center gap-2 text-[#303133]">
+        {{ AppStore.tenantInfo?.name }}
+        <SvgIcon name="czr_arrow" :rotate="90" size="10" color="#303133" />
+      </div>
+      <template #dropdown>
+        <el-dropdown-menu>
+          <template
+            v-for="item in DictionaryStore.tenants.list.filter(
+              (v) => v.id !== AppStore.tenantInfo.id,
+            )"
+          >
+            <el-dropdown-item @click="onChangeTenant(item)">{{
+              item.name
+            }}</el-dropdown-item>
+          </template>
+        </el-dropdown-menu>
+      </template>
+    </el-dropdown>
+    <el-dropdown>
+      <div
+        class="__hover mr-4 ml-4 flex items-center gap-2 text-sm text-[#303133]"
+      >
+        <img src="@/assets/images/avatar-default.png" class="size-8" />
+        {{ AppStore.userInfo?.name }}
+        <SvgIcon name="czr_arrow" :rotate="90" size="10" color="#303133" />
+      </div>
+      <template #dropdown>
+        <el-dropdown-menu>
+          <el-dropdown-item @click="logout">退出登录</el-dropdown-item>
+        </el-dropdown-menu>
+      </template>
+    </el-dropdown>
     <el-drawer v-model="state.drawer" title="临时字典管理">
       <div class="h-full w-full overflow-y-auto">
         <el-collapse v-model="state.activeNames">
@@ -60,11 +93,18 @@
 </template>
 
 <script setup lang="ts">
-import { computed, getCurrentInstance, reactive } from 'vue'
-import { useMenuStore } from '@/stores'
+import { computed, getCurrentInstance, onMounted, reactive } from 'vue'
+import {
+  useAppStore,
+  useDialogStore,
+  useDictionaryStore,
+  useMenuStore,
+} from '@/stores'
 import { useRoute, useRouter } from 'vue-router'
 
-const MenuStore = useMenuStore()
+const AppStore = useAppStore()
+const DictionaryStore = useDictionaryStore()
+const DialogStore = useDialogStore()
 const router = useRouter()
 const route = useRoute()
 const { proxy } = getCurrentInstance()
@@ -81,6 +121,7 @@ const menusCpt: any = computed(
 import { sysDict } from '@/api/modules/global/dictionary'
 import { copy } from '@/utils/czr-util'
 import { ElMessage } from 'element-plus'
+import { loginChangeTenant, loginLogout } from '@/api/modules/global/login'
 sysDict().then(({ data }: any) => {
   data.forEach((v) => {
     const d = state.dictMap.get(v.dictType)
@@ -103,6 +144,37 @@ const copyDict = (label, value) => {
     console.log(e)
   }
 }
+const onChangeTenant = (row) => {
+  DialogStore.confirm({
+    content: `请确认是否切换到租户 ${row.name} ?`,
+    onSubmit: () => {
+      loginChangeTenant(row.id)
+        .then(({ data }: any) => {
+          localStorage.setItem((import.meta as any).env.VITE_TOKEN, data)
+          window.location.reload()
+        })
+        .catch(() => {})
+        .finally(() => {})
+    },
+  })
+}
+const logout = () => {
+  DialogStore.confirm({
+    content: `请确认是否退出登录?`,
+    onSubmit: () => {
+      loginLogout()
+        .then(({ data }: any) => {
+          localStorage.clear()
+          window.location.reload()
+        })
+        .catch(() => {})
+        .finally(() => {})
+    },
+  })
+}
+onMounted(() => {
+  DictionaryStore.initTenants()
+})
 </script>
 
 <style lang="scss" scoped>

+ 1 - 1
src/stores/modules/regex.ts

@@ -13,7 +13,7 @@ export const useRegexStore = defineStore('regex', {
     password1: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,16}$/,
     //  密码级别高(2):请输入8~16位密码,要求同时包含大小写字母、数字、特殊字符
     password2:
-      /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[~!@#$%^&*()_+`\-={}[\]:;"'<>,.?/]).{8,16}$/,
+      /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[~!@#$%^&*()_+`\-={}[\]:;"'<>,.?/]).{8,}$/,
     // ip
     ip: /^((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})(\.((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})){3}$/,
   }),

+ 57 - 30
src/views/manage/center/user/detail.vue

@@ -20,20 +20,39 @@
           :min-length="2"
           :max-length="50"
         />
-        <CzrFormColumn
-          required
-          :span="12"
-          label="密码"
-          v-model:param="state.form.pass"
-          :rules="[
-            {
-              handle: (val) => RegexStore.password2.test(val),
-              message:
-                '请输入8~16位密码,要求同时包含大小写字母、数字、特殊字符',
-            },
-          ]"
-          :disabled="!!state.form.id"
-        />
+        <template v-if="state.editPassword">
+          <CzrFormColumn
+            required
+            :span="12"
+            label="密码"
+            v-model:param="state.form.pass"
+            :rules="[
+              {
+                handle: (val) => RegexStore.password2.test(val),
+                message:
+                  '请输入至少8位密码,要求同时包含大小写字母、数字、特殊字符',
+              },
+            ]"
+          />
+        </template>
+        <template v-else>
+          <CzrFormColumn
+            :span="12"
+            label="密码"
+            v-model:param="state.form.pass"
+            type="password"
+            :disabled="true"
+          >
+            <template #suffix>
+              <el-tooltip placement="top" content="重新设置密码">
+                <Edit
+                  class="w-4 cursor-pointer"
+                  @click="(state.form.pass, (state.editPassword = true))"
+                />
+              </el-tooltip>
+            </template>
+          </CzrFormColumn>
+        </template>
         <CzrFormColumn
           required
           :span="12"
@@ -87,9 +106,14 @@ import {
   useRegexStore,
 } from '@/stores'
 import { useRouter } from 'vue-router'
-import { CopyDocument } from '@element-plus/icons-vue'
+import { Edit } from '@element-plus/icons-vue'
 import { copy } from '@/utils/czr-util'
-import { userAdd, userDetail } from '@/api/modules/center/user'
+import {
+  accountGenerateFriendlyCode,
+  userAdd,
+  userDetail,
+  userEdit,
+} from '@/api/modules/center/user'
 
 const router = useRouter()
 const AppStore = useAppStore()
@@ -105,6 +129,7 @@ const props = defineProps({
 const state: any = reactive({
   loading: false,
   form: {},
+  editPassword: false,
 })
 const ref_form = ref()
 const titleCpt = computed(() => {
@@ -131,7 +156,9 @@ watch(
       state.form = {
         enabled: true,
       }
+      state.editPassword = true
       if (props.transfer.mode !== 'add') {
+        state.editPassword = false
         initData()
       }
       nextTick(() => {
@@ -172,20 +199,19 @@ const onSubmit = () => {
                 state.loading = false
               })
           } else {
-            // datasetsUpdate({
-            //   ...state.form,
-            //   ...ref_modelConfig.value.getData(),
-            //   tenantId: AppStore.tenantInfo?.id,
-            // })
-            //   .then(({ data }: any) => {
-            //     ElMessage.success(`${titleCpt.value}成功!`)
-            //     emit('update:show', false)
-            //     emit('refresh')
-            //   })
-            //   .catch(() => {})
-            //   .finally(() => {
-            //     state.loading = false
-            //   })
+            if (!state.editPassword) {
+              delete state.form.pass
+            }
+            userEdit(state.form)
+              .then(({ data }: any) => {
+                ElMessage.success(`${titleCpt.value}成功!`)
+                emit('update:show', false)
+                emit('refresh')
+              })
+              .catch(() => {})
+              .finally(() => {
+                state.loading = false
+              })
           }
         },
       })
@@ -199,6 +225,7 @@ const onSubmit = () => {
     })
 }
 const onInvite = () => {
+  // accountGenerateFriendlyCode()
   state.invite.url = 'https://cn.element-plus.org/zh-CN/component/input.html'
   state.invite.show = true
 }

+ 16 - 16
src/views/manage/center/user/index.vue

@@ -21,7 +21,7 @@
             placeholder="全部租戶"
           />
           <CzrFormColumn
-            width="19rem"
+            width="18rem"
             class="__czr-table-form-column"
             :span="24"
             label-width="0px"
@@ -31,7 +31,7 @@
             placeholder="最后登录时间"
           />
           <CzrFormColumn
-            width="19rem"
+            width="18rem"
             class="__czr-table-form-column"
             :span="24"
             label-width="0px"
@@ -154,7 +154,7 @@ import { debounce } from 'lodash'
 import { useAppStore, useDialogStore, useDictionaryStore } from '@/stores'
 import { ElMessage } from 'element-plus'
 import detailCom from './detail.vue'
-import { userPage } from '@/api/modules/center/user'
+import { userDel, userPage } from '@/api/modules/center/user'
 import { copy } from '@/utils/czr-util'
 
 const AppStore = useAppStore()
@@ -261,11 +261,11 @@ const onPage = (pageNum, pageSize) => {
   for (const [k, v] of Object.entries(state.query.formReal)) {
     if (proxy.$czrUtil.isValue(v)) {
       if (k === 'loginTime') {
-        params['loginTimeStart'] = v[0]
-        params['loginTimeEnd'] = v[1]
+        params['loginTimeStart'] = v[0] + ' 00:00:00'
+        params['loginTimeEnd'] = v[1] + ' 23:59:59'
       } else if (k === 'createTime') {
-        params['createTimeStart'] = v[0]
-        params['createTimeEnd'] = v[1]
+        params['createTimeStart'] = v[0] + ' 00:00:00'
+        params['createTimeEnd'] = v[1] + ' 23:59:59'
       } else {
         params[k] = v
       }
@@ -315,16 +315,16 @@ const onEdit = (row) => {
 const onDel = (row: any) => {
   DialogStore.confirm({
     title: '删除确认',
-    content: `请确认是否删除`,
+    content: `请确认是否删除 ${row.name} ?`,
     onSubmit: () => {
-      // appDel(row.id)
-      //   .then(() => {
-      //     ElMessage.success('删除成功!')
-      //   })
-      //   .catch(() => {})
-      //   .finally(() => {
-      //     onSearch()
-      //   })
+      userDel(row.id)
+        .then(() => {
+          ElMessage.success('删除成功!')
+        })
+        .catch(() => {})
+        .finally(() => {
+          onSearch()
+        })
     },
   })
 }