Browse Source

feat: 账号管理

刘洋 8 months ago
parent
commit
71fd49cc8b

+ 2 - 0
.eslintrc.js

@@ -73,5 +73,7 @@ module.exports = {
     'no-use-before-define': ['error', { functions: false }],
     'no-empty-function': 0,
     'no-loop-func': 0,
+    'guard-for-in': 0,
+    'no-restricted-syntax': 0,
   },
 };

+ 14 - 0
src/api/order.ts

@@ -186,3 +186,17 @@ export function saveReservation(data: any, params: any) {
 export function toggleSelfYYStatus(params: any) {
   return axios.post('/api/admin/teaching/selfApplyEnable', {}, { params });
 }
+
+export function getUserList(data: any): any {
+  return axios.post('/api/admin/user/page', data);
+}
+
+export function addUser(data: any): any {
+  return axios.post('/api/admin/user/save', data);
+}
+export function ableUser(params: any): any {
+  return axios.post('/api/admin/user/enable', {}, { params });
+}
+export function resetUserPwd(params: any): any {
+  return axios.post('/api/admin/user/reset/password', {}, { params });
+}

+ 2 - 0
src/components/index.ts

@@ -10,6 +10,7 @@ import SelectRangeTime from './select-range-time/index.vue';
 import SelectCity from './select-city/index.vue';
 import StatusTag from './status-tag/index.vue';
 import UploadButton from './upload-button/index.vue';
+import SelectRole from './select-role/index.vue';
 
 export default {
   install(Vue: App) {
@@ -22,5 +23,6 @@ export default {
     Vue.component('SelectCity', SelectCity);
     Vue.component('StatusTag', StatusTag);
     Vue.component('UploadButton', UploadButton);
+    Vue.component('SelectRole', SelectRole);
   },
 };

+ 65 - 0
src/components/select-role/index.vue

@@ -0,0 +1,65 @@
+<template>
+  <a-select
+    v-model="selected"
+    :placeholder="placeholder"
+    :allow-clear="clearable"
+    :disabled="disabled"
+    :options="optionList"
+    allow-search
+    popup-container="body"
+    v-bind="attrs"
+    @change="onChange"
+  >
+    <template v-if="prefix" #prefix>角色</template>
+  </a-select>
+</template>
+
+<script setup lang="ts">
+  import { ref, useAttrs, watch } from 'vue';
+  import useDictOption from '@/hooks/dict-option';
+
+  defineOptions({
+    name: 'SelectRole',
+  });
+  type ValueType = number | Array<number> | null;
+
+  const props = defineProps<{
+    modelValue: ValueType;
+    clearable?: boolean;
+    disabled?: boolean;
+    placeholder?: string;
+    multiple?: boolean;
+    prefix?: boolean;
+  }>();
+  const emit = defineEmits(['update:modelValue', 'change']);
+  const attrs = useAttrs();
+  const { optionList } = useDictOption('ROLE_TYPE');
+
+  interface OptionListItem {
+    value: number;
+    label: string;
+  }
+
+  const selected = ref<number | Array<number> | undefined>();
+
+  const onChange = () => {
+    const selectedData = props.multiple
+      ? optionList.value.filter(
+          (item: any) =>
+            selected.value && (selected.value as number[]).includes(item.value)
+        )
+      : optionList.value.filter((item) => selected.value === item.value);
+    emit('update:modelValue', selected.value || null);
+    emit('change', props.multiple ? selectedData : selectedData[0]);
+  };
+
+  watch(
+    () => props.modelValue,
+    (val) => {
+      selected.value = val || undefined;
+    },
+    {
+      immediate: true,
+    }
+  );
+</script>

+ 9 - 0
src/router/routes/modules/order.ts

@@ -54,6 +54,15 @@ const routes: AppRouteRecordRaw = {
         requiresAuth: true,
       },
     },
+    {
+      path: 'user-manage',
+      name: 'AccountManage',
+      component: () => import('@/views/order/user-manage/index.vue'),
+      meta: {
+        title: '账号管理',
+        requiresAuth: true,
+      },
+    },
   ],
 };
 

+ 19 - 0
src/store/modules/app/menuData.ts

@@ -107,4 +107,23 @@ export const menus = [
     enable: true,
     roles: ['ADMIN', 'TEACHING'],
   },
+  {
+    id: 17,
+    name: '系统设置',
+    url: 'base',
+    type: 'MENU',
+    parentId: -1,
+    sequence: 1,
+    enable: true,
+  },
+  {
+    id: 18,
+    name: '账号管理',
+    url: 'AccountManage',
+    type: 'MENU',
+    parentId: 17,
+    sequence: 1,
+    enable: true,
+    roles: ['ADMIN'],
+  },
 ];

+ 124 - 0
src/views/order/user-manage/addUserDialog.vue

@@ -0,0 +1,124 @@
+<template>
+  <a-modal
+    v-model:visible="visible"
+    :width="500"
+    title-align="start"
+    top="10vh"
+    :align-center="false"
+    :mask-closable="false"
+    :esc-to-close="false"
+    @close="emit('close')"
+  >
+    <template #title> {{ `${curRow ? '编辑' : '新增'}账号` }} </template>
+    <a-form ref="formRef" :model="formData" :rules="rules" auto-label-width>
+      <a-form-item field="loginName" label="账号">
+        <a-input v-model="formData.loginName" />
+      </a-form-item>
+      <a-form-item field="name" label="用户名">
+        <a-input v-model="formData.name" />
+      </a-form-item>
+      <a-form-item field="roleCode" label="角色">
+        <SelectRole
+          v-model="formData.roleCode"
+          placeholder="请选择"
+          allow-clear
+        />
+      </a-form-item>
+      <a-form-item field="categoryId" label="教学点">
+        <SelectTeaching
+          v-model="formData.categoryId"
+          placeholder="请选择"
+          allow-clear
+        />
+      </a-form-item>
+      <a-form-item field="mobile" label="联系方式">
+        <a-input v-model="formData.mobile" />
+      </a-form-item>
+    </a-form>
+
+    <template #footer>
+      <a-button @click="emit('close')">取消</a-button>
+      <a-button type="primary" :disabled="loading" @click="confirm"
+        >确认</a-button
+      >
+    </template>
+  </a-modal>
+</template>
+
+<script setup lang="ts">
+  import { nextTick, reactive, ref } from 'vue';
+  import { Message } from '@arco-design/web-vue';
+  import type { FormInstance } from '@arco-design/web-vue/es/form';
+  import { addUser } from '@/api/order';
+  import useLoading from '@/hooks/loading';
+
+  defineOptions({
+    name: 'AddUserDialog',
+  });
+  const props = defineProps<{ curRow: any }>();
+  const emit = defineEmits(['close', 'success']);
+  const visible = ref(true);
+  const formRef = ref<FormInstance>();
+  const formData = reactive<any>({
+    loginName: '',
+    name: '',
+    roleCode: '',
+    categoryId: '',
+    mobile: '',
+  });
+  if (props.curRow) {
+    for (const key in formData) {
+      formData[key] = props.curRow?.[key] || '';
+    }
+  }
+  const rules = {
+    loginName: [
+      {
+        required: true,
+        message: '请输入账号',
+      },
+    ],
+    name: [
+      {
+        required: true,
+        message: '请输入用户名',
+      },
+    ],
+    roleCode: [
+      {
+        required: true,
+        message: '请选择角色',
+      },
+    ],
+    categoryId: [
+      {
+        required: true,
+        message: '请选择教学点',
+      },
+    ],
+  };
+
+  /* confirm */
+  const { loading, setLoading } = useLoading();
+  async function confirm() {
+    const err = await formRef.value?.validate();
+    if (err) return;
+
+    setLoading(true);
+    const params: any = { ...formData };
+    if (props.curRow) {
+      params.id = props.curRow.id;
+    }
+    addUser(params).then(() => {
+      Message.success('操作成功!');
+      emit('success');
+    });
+    // const res = await downloadByApi(() => orderRecordPrint(datas)).catch(
+    //   (e) => {
+    //     Message.error(e || '下载失败,请重新尝试!');
+    //   }
+    // );
+    // setLoading(false);
+    // if (!res) return;
+  }
+</script>

+ 165 - 0
src/views/order/user-manage/index.vue

@@ -0,0 +1,165 @@
+<template>
+  <div class="part-box is-filter">
+    <a-space class="filter-line" :size="12" wrap>
+      <SelectRole
+        v-model="searchModel.roleCode"
+        placeholder="请选择"
+        allow-clear
+        prefix
+      />
+      <a-input
+        v-model.trim="searchModel.loginName"
+        placeholder="请输入"
+        allow-clear
+      >
+        <template #prefix>账号</template>
+      </a-input>
+      <a-input v-model.trim="searchModel.name" placeholder="请输入" allow-clear>
+        <template #prefix>用户名</template>
+      </a-input>
+      <a-button type="primary" @click="toPage(1)">查询</a-button>
+    </a-space>
+  </div>
+  <div class="part-box">
+    <a-space class="part-action" :size="12">
+      <a-button type="primary" @click="toAdd(null)">新增</a-button>
+    </a-space>
+
+    <a-table
+      class="page-table"
+      :columns="columns"
+      :data="dataList"
+      :pagination="false"
+      :scroll="{ x: 1200 }"
+      :bordered="false"
+    >
+      <template #role="{ record }">
+        {{ record.roleCode === 'ADMIN' ? '学校管理员' : '教学点管理员' }}
+      </template>
+      <template #action="{ record }">
+        <a-button type="text" class="btn-primary" @click="toAdd(record)"
+          >编辑</a-button
+        >
+        <a-button
+          :class="record.enable ? 'btn-danger' : 'btn-primary'"
+          type="text"
+          @click="toEnable(record)"
+          >{{ record.enable ? '禁用' : '启用' }}</a-button
+        >
+        <a-button type="text" class="btn-primary" @click="toReset(record)"
+          >重置密码</a-button
+        >
+      </template>
+    </a-table>
+
+    <AddUserDialog
+      v-if="showAddUserDialog"
+      :cur-row="curRow"
+      @close="showAddUserDialog = false"
+      @success="
+        () => {
+          toPage(1);
+          showAddUserDialog = false;
+        }
+      "
+    />
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { reactive, ref, computed, onMounted, watch } from 'vue';
+  import { Message, TableColumnData } from '@arco-design/web-vue';
+  import { getUserList, ableUser, resetUserPwd } from '@/api/order';
+  import { TaskItem } from '@/api/types/order';
+  import useTable from '@/hooks/table';
+  import { useAppStore } from '@/store';
+  import { modalConfirm } from '@/utils/arco';
+  import AddUserDialog from './addUserDialog.vue';
+
+  defineOptions({
+    name: 'AccountManage',
+  });
+
+  const showAddUserDialog = ref(false);
+  const appStore = useAppStore();
+  appStore.setInfo({ breadcrumbs: ['系统设置', '账号管理'] });
+  const curRow = ref<any>(null);
+  const searchModel = reactive({
+    roleCode: '',
+    name: '',
+    loginName: '',
+  });
+
+  const columns = computed(() => {
+    return [
+      // {
+      //   title: '任务ID',
+      //   dataIndex: 'id',
+      //   width: 80,
+      // },
+      {
+        title: '账号',
+        dataIndex: 'loginName',
+      },
+      {
+        title: '用户名',
+        dataIndex: 'name',
+      },
+      {
+        title: '角色',
+        dataIndex: 'roleCode',
+        slotName: 'role',
+      },
+      {
+        title: '所属教学点',
+        dataIndex: 'loginName',
+      },
+      {
+        title: '操作',
+        slotName: 'action',
+        width: 180,
+        fixed: 'right',
+        cellClass: 'action-column',
+      },
+    ];
+  });
+  const { dataList, pagination, toPage, getList } = useTable<{
+    id: string;
+    loginName: string;
+    name: string;
+    roleCode: string;
+    categoryName: string;
+    categoryId: string;
+    mobile: string;
+    enable: boolean;
+  }>(getUserList, searchModel, true);
+
+  const toAdd = (obj: any) => {
+    curRow.value = obj || null;
+    showAddUserDialog.value = true;
+  };
+
+  async function toEnable(row: any) {
+    const action = row.enable ? '禁用' : '启用';
+    const confirmRes = await modalConfirm(
+      '提示',
+      `确定要${action}用户【${row.name}】吗?`
+    ).catch(() => false);
+    if (confirmRes !== 'confirm') return;
+
+    await ableUser({ id: row.id, enable: !row.enable });
+    Message.success('操作成功!');
+    getList();
+  }
+
+  async function toReset(row: any) {
+    const confirmRes = await modalConfirm(
+      '提示',
+      `确定要重置账号【${row.loginName}】的密码吗?`
+    ).catch(() => false);
+    if (confirmRes !== 'confirm') return;
+    await resetUserPwd({ id: row.id });
+    Message.success('操作成功!');
+    getList();
+  }
+</script>