Преглед на файлове

feat: 用户群组及其资源管理

lvkun преди 2 години
родител
ревизия
bc07d79b29

+ 0 - 1
config/proxy.ts

@@ -20,6 +20,5 @@ module.exports = {
       changeOrigin: true,
       pathRewrite: { '^/iot': '' }
     }
-
   }
 }

+ 1 - 1
src/api/user/index.ts

@@ -53,7 +53,7 @@ export const updatePassword = (data: {id: string, sourcePassword: string, newPas
   })
 }
 
-export const resource = (params: USER.Params.Query & {resourceLabel: string}) => {
+export const resourcePage = (params: USER.Params.Query & {resourceLabel: string}) => {
   return request<USER.Resource.Detail[]>({
     url: '/resource/page',
     method: 'GET',

+ 5 - 1
src/controller/user/index.ts

@@ -1,4 +1,4 @@
-import { addUser, list, login, page, updatePassword } from '@/api/user'
+import { addUser, list, login, page, resourcePage, updatePassword } from '@/api/user'
 import { message } from 'ant-design-vue'
 
 export class UserController {
@@ -27,4 +27,8 @@ export class UserController {
     await updatePassword(data)
     message.success('修改成功')
   }
+
+  static async resourcePage (params: USER.Params.Query & {resourceLabel: string}) {
+    return await resourcePage(params)
+  }
 }

+ 3 - 1
src/hooks/effect.ts

@@ -57,8 +57,10 @@ export const usePort = (title: string) => {
     setBaseUrl('/iot')
   } else if (title === '视联网') {
     setBaseUrl('/rts')
-  } else {
+  } else if (title === '用户群组') {
     setBaseUrl('/user')
+  } else {
+    setBaseUrl('/')
   }
 }
 

+ 38 - 3
src/pages/login/index.vue

@@ -1,13 +1,48 @@
 <template>
-<a-card>
-</a-card>
+  <div class="login" >
+    <div class="app-name" >蛟龙云</div>
+    <a-row :gutter="[16, 16]" style="margin-top: 40px;">
+      <a-col span="24" >
+        <a-input v-model:value="state.userAccount" style="height: 40px;" placeholder="请输入账号" ></a-input>
+      </a-col>
+      <a-col span="24">
+        <a-input-password v-model:value="state.password" style="height: 40px;" placeholder="请输入密码" ></a-input-password>
+      </a-col>
+      <a-col span="24">
+        <a-button style="width: 100%;" type="primary" @click="login"> 登陆</a-button>
+      </a-col>
+    </a-row>
+  </div>
 </template>
 <script lang='ts' setup >
-
 import { useRoute } from 'vue-router'
+import { useUserStore } from '@/store/index'
+import { reactive } from 'vue'
 
 // const { redirct_url } = useRoute().query
 
+const userStore = useUserStore()
+
+const state = reactive({
+  userAccount: '',
+  password: ''
+})
+
+const login = () => {
+  userStore.login(state)
+}
+
 </script>
 <style lang='less' scoped >
+.login {
+  position: absolute;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%, -50%);
+  .app-name {
+    font: 80px 'Italiana', sans-serif;
+    text-transform: lowercase;
+    text-align: center;
+  }
+}
 </style>

+ 78 - 52
src/pages/user/manage/index.vue

@@ -1,12 +1,18 @@
 <template>
   <a-card>
-    <a-row justify="end" >
+    <a-row justify="space-between" >
+      <a-col>
+        <a-space>
+          <a-input placeholder="根据用户名称搜索" allowClear v-model:value="queryState.userLabel" ></a-input>
+          <a-button type="primary" @click="getUserList">搜索</a-button>
+        </a-space>
+      </a-col>
       <a-col>
         <a-button type="primary" @click="openModal('add')">添加</a-button>
       </a-col>
     </a-row>
     <a-table
-      style="margin-top: 10px;"
+      style="margin-top: 20px;"
       :columns="columns"
       :data-source="state.dataSource"
       :loading="state.loading"
@@ -14,23 +20,10 @@
       @change="changePage"
     >
       <template #bodyCell="{column, record}">
-        <template v-if="column.key === 'deviceStatus'" >
-          <a-tag :color="record.deviceStatus.color" >{{record.deviceStatus.name}}</a-tag>
-        </template>
-        <template v-if="column.key === 'deviceNodeType'" >
-          <a-tag>{{record.deviceNodeType == 'GATEWAY'?'直连类型':'非直连类型'}}</a-tag>
-        </template>
+
         <template v-if="column.key === 'action'">
             <a-space>
-              <a href="#" @click="openModal('update', record)" >编辑</a>
-              <!-- <a-popconfirm
-                  title="确实要删除吗?"
-                  ok-text="确定"
-                  cancel-text="取消"
-                  @confirm="delUser(record.id)"
-                >
-                  <a href="#">删除</a>
-              </a-popconfirm> -->
+              <a href="#" @click="openModal('update', record)" >修改密码</a>
             </a-space>
         </template>
       </template>
@@ -45,47 +38,69 @@
     style="width: 700px;"
   >
   <a-form :label-col="{span: 4}" :wrapper-col="{span: 14}">
-    <a-form-item label="姓名" v-bind="validateInfos.name">
-      <a-input allowClear v-model:value="modelRef.name"  />
+    <a-form-item label="用户名称" v-if="state.opraState === 'add'">
+      <a-input allowClear v-model:value="modelRef.label"  />
+    </a-form-item>
+    <a-form-item name="account" label="用户账号" v-bind="validateInfos.account" v-if="state.opraState === 'add'">
+      <a-input allowClear v-model:value="modelRef.account"  />
+    </a-form-item>
+    <a-form-item name="sourcePassword" label="原始密码" v-bind="validateInfos.password" v-if="state.opraState === 'update'">
+      <a-input-password  v-model:value="modelRef.sourcePassword"  />
+    </a-form-item>
+    <a-form-item name="password" :label="state.opraState === 'add' ? '用户密码' : '新密码'" v-bind="validateInfos.password">
+      <a-input-password  v-model:value="modelRef.password"  />
+    </a-form-item>
+    <a-form-item name="email" label="用户邮箱" v-bind="validateInfos.email" v-if="state.opraState === 'add'">
+      <a-input allowClear v-model:value="modelRef.email"  />
     </a-form-item>
    </a-form>
   </modal-pro>
 </template>
 
 <script lang="ts" setup >
-import { computed, onMounted, reactive } from 'vue'
+import { computed, nextTick, onMounted, reactive } from 'vue'
 import { UserController } from '@/controller'
 import { Form, useCacheToken } from 'ant-design-vue'
 
 const columns = [
   {
-    title: '设备ID',
-    dataIndex: 'id',
-    key: 'id'
+    title: '用户id',
+    dataIndex: 'id'
   },
   {
-    title: '设备名称',
-    dataIndex: 'deviceLabel'
+    title: '用户名称',
+    dataIndex: 'label',
+    key: 'label'
   },
   {
-    title: '设备标识码',
-    dataIndex: 'deviceCode'
+    title: '用户邮箱',
+    dataIndex: 'email'
   },
   {
-    title: 'action',
-    dataIndex: 'dataIndex',
-    key: 'dataIndex'
+    title: '用户账号',
+    dataIndex: 'account',
+    key: 'account'
+  },
+  {
+    title: '用户密码',
+    dataIndex: 'password'
+  },
+  {
+    title: '操作',
+    dataIndex: 'action',
+    key: 'action'
   }
 ]
 
-const modalTitle = computed(() => state.opraState === 'add' ? '新增用户' : '编辑用户')
+const modalTitle = computed(() => state.opraState === 'add' ? '新增用户' : '修改密码')
 
 const useForm = Form.useForm
 
 const queryState = reactive({
   page: 1,
   pageSize: 10,
-  total: 0
+  total: 0,
+  userLabel: ''
 })
 
 const state = reactive<{
@@ -103,38 +118,49 @@ const state = reactive<{
 })
 
 const modelRef = reactive({
-  name: ''
+  id: '',
+  account: '', // 用户账号 登陆使用 必需
+  email: '', // 用户邮箱
+  password: '', // 用户密码  必需,在输入的密码上 做一次base64 encode
+  label: '', // 用户名称 非必需
+  sourcePassword: ''
 })
 
 const closeModal = () => {
   state.visible = false
 }
 
-const openModal = (key: 'add' | 'update', record: USER.User.Detail = {}) => {
+const openModal = (key: 'add' | 'update', record: Partial<USER.User.Detail> = {}) => {
   state.opraState = key
   state.visible = true
-  if (key === 'update') {
-    state.detail = record
-  }
+  modelRef.id = record.id!
 }
 
-const { resetFields, validate, validateInfos } = useForm(modelRef, {
-  name: [{ required: true, message: '请填写标题' }]
-})
-
-const submit = () => {
-  validate().then(async () => {
-    state.opraState === 'add' ? await UserController.add(modelRef) : UserController.update(modelRef)
-    closeModal()
-    getUserList()
-  })
+const { resetFields, validate, validateInfos } = useForm(modelRef, reactive({
+  account: [{ required: true, message: '请填写用户账户' }],
+  email: [{ required: true, message: '请填写用邮箱', name: 'email' }],
+  password: [{ required: true, message: '请填写用户密码', name: 'password' }],
+  sourcePassword: [{ required: true, message: '请填写原始密码' }]
+}))
+
+const submit = async () => {
+  if (state.opraState === 'add') {
+    validate().then(async () => {
+      await UserController.add(modelRef)
+      closeModal()
+      getUserList()
+      resetFields()
+    })
+  } else {
+    if (modelRef.id && modelRef.password && modelRef.sourcePassword) {
+      await UserController.updatePassword({ id: modelRef.id, sourcePassword: modelRef.sourcePassword, newPassword: modelRef.password })
+      closeModal()
+      getUserList()
+      resetFields()
+    }
+  }
 }
 
-// const delUser = async (id: string) => {
-//   await UserController.del(id)
-//   getUserList()
-// }
-
 const changePage = ({ current }) => {
   queryState.page = current
   getUserList()

+ 70 - 2
src/pages/user/resource/index.vue

@@ -1,8 +1,76 @@
 <template>
-<a-card title="资源管理" >
-</a-card>
+  <a-card title="资源管理" >
+    <a-row justify="start" >
+      <a-col>
+        <a-space>
+          <a-input placeholder="根据用户名称搜索" allowClear v-model:value="queryState.userLabel" ></a-input>
+          <a-button type="primary" @click="getUserList">搜索</a-button>
+        </a-space>
+      </a-col>
+    </a-row>
+    <a-table
+      style="margin-top: 20px;"
+      :columns="columns"
+      :data-source="state.dataSource"
+      :loading="state.loading"
+      :pagination="queryState"
+      @change="changePage"
+    >
+    </a-table>
+  </a-card>
 </template>
 <script lang='ts' setup >
+import { UserController } from '@/controller'
+import { reactive, onMounted } from 'vue'
+
+const columns = [
+  {
+    title: '资源id',
+    dataIndex: 'id'
+  },
+  {
+    title: '资源名称',
+    dataIndex: 'label',
+    key: 'label'
+  },
+  {
+    title: '资源描述',
+    dataIndex: 'description'
+  },
+  {
+    title: '资源路径',
+    dataIndex: 'path'
+  }
+]
+
+const queryState = reactive({
+  page: 1,
+  pageSize: 10,
+  total: 0,
+  resourceLabel: ''
+})
+
+const state = reactive({
+  loading: false,
+  dataSource: []
+})
+
+const changePage = ({ current }) => {
+  queryState.page = current
+  getResourcPage()
+}
+
+const getResourcPage = async () => {
+  state.loading = true
+  const { data, sum } = await UserController.resourcePage(queryState)
+  state.dataSource = data
+  state.loading = false
+  queryState.total = sum
+}
+onMounted(() => {
+  getResourcPage()
+})
+
 </script>
 <style lang='less' scoped >
 </style>

+ 10 - 1
src/router/index.ts

@@ -312,7 +312,16 @@ const user = {
   ]
 }
 
-const _routes = [iot, rts, schedule, view, lowcode, user] as any
+const login = {
+  path: '/login',
+  name: '登录',
+  meta: {
+    title: '登录'
+  },
+  component: () => import('@/pages/login/index.vue')
+}
+
+const _routes = [iot, rts, schedule, view, lowcode, user, login] as any
 
 if (_routes[0].link) {
   window.open(_routes[0].path)

+ 3 - 2
src/service/request.ts

@@ -17,10 +17,10 @@ const catchErr = (response: AxiosResponse) => {
   const { data } = response
   if (data.code === 500) {
     message.error(data.msg)
-    throw new Error('')
+    throw new Error(data.msg)
   } else if (data.code === 400) {
     message.error(data.msg)
-    throw new Error('')
+    throw new Error(data.msg)
   }
 }
 
@@ -39,6 +39,7 @@ instance.interceptors.request.use(config => {
 
 instance.interceptors.response.use(function (response) {
   catchErr(response)
+
   if (response.status === 403) {
     redirectUrl()
   }

+ 8 - 2
src/store/modules/user/index.ts

@@ -16,7 +16,10 @@ export const useUserStore = defineStore('userStore', () => {
 
   const login = async (data: USER.Params.Login) => {
     // 存储用户信息 跳转页面
-    await UserController.login(data)
+    await UserController.login({
+      ...data,
+      password: encryptPassWord(data.password)
+    })
   }
 
   const openUserInfoModal = () => {
@@ -46,11 +49,14 @@ export const useUserStore = defineStore('userStore', () => {
     })
   }
 
+  const encryptPassWord = (password: string) => encodeURI(btoa(password))
+
   return {
     userInfo,
     login,
     logout,
     clearUserInfo,
-    openUserInfoModal
+    openUserInfoModal,
+    encryptPassWord
   }
 })