ソースを参照

perf(views): 初步编写"创建角色"步骤页面

wangshun 3 週間 前
コミット
522203bf90
1 ファイル変更294 行追加0 行削除
  1. 294 0
      src/views/create-customer/CreateCharacter.vue

+ 294 - 0
src/views/create-customer/CreateCharacter.vue

@@ -0,0 +1,294 @@
+<script setup lang="ts">
+import { onMounted, ref, useTemplateRef, watch } from 'vue';
+
+import ConfirmModal from '@/components/ConfirmModal.vue';
+import SvgIcon from '@/components/SvgIcon.vue';
+import { useRequest } from '@/hooks/request';
+import { t } from '@/i18n';
+import { getSubPermList } from '@/api';
+
+import type { TreeProps } from 'ant-design-vue';
+import type { Rule } from 'ant-design-vue/es/form';
+import type { DataNode } from 'ant-design-vue/es/tree';
+import type { CharacterItem, TreeStructure } from '@/types';
+
+const rules: Record<string, Rule[]> = {
+  name: [{ required: true, message: t('common.cannotEmpty'), trigger: 'change' }],
+};
+
+const { handleRequest } = useRequest();
+const modalComponentRef = useTemplateRef('modalComponent');
+const characterList = ref<CharacterItem[]>([]);
+const characterOpen = ref<boolean>(false);
+const checked = ref<boolean>(false);
+const checkedAll = ref<number[]>([]);
+const indeterminate = ref<boolean>(false);
+const characterIndex = ref<number>(0);
+const expandedKeys = ref<number[]>([]);
+const selectedKeys = ref<number[]>([]);
+const checkedKeys = ref<number[]>([]);
+const fieldNames: TreeProps['fieldNames'] = {
+  children: 'subPermissions',
+  title: 'menuName',
+  key: 'id',
+};
+const treeStructure = ref<DataNode[]>([]);
+const characterForm = ref({
+  name: '',
+});
+const addCharacter = () => {
+  characterOpen.value = true;
+};
+const characterSave = () => {
+  characterList.value.push({
+    name: '',
+    id: undefined,
+  });
+  characterOpen.value = false;
+};
+const cancelSave = () => {
+  checkedKeys.value = [];
+  characterOpen.value = false;
+};
+const confirm = () => {
+  modalComponentRef.value?.hideView();
+  characterList.value.splice(characterIndex.value, 1);
+};
+const characterEditor = () => {};
+const characterDelete = (index: number) => {
+  characterIndex.value = index;
+  modalComponentRef.value?.showView();
+};
+const addMenu = () => {
+  console.log(checkedKeys.value);
+  console.log(checkedAll.value);
+  if (checkedAll.value.length !== checkedKeys.value.length) {
+    indeterminate.value = true;
+  } else {
+    indeterminate.value = false;
+  }
+};
+const selectAll = () => {
+  if (checked.value) {
+    checkedKeys.value = getAllKeys(treeStructure.value);
+    if (checkedKeys.value.length) {
+      checkedAll.value = checkedKeys.value;
+    }
+  } else {
+    checkedKeys.value = [];
+  }
+};
+
+// 获取所有节点 key 的递归方法
+const getAllKeys = (data: DataNode[]) => {
+  let keys: number[] = [];
+  data.forEach((item) => {
+    keys.push(item.id);
+    if (item.subPermissions) {
+      keys = keys.concat(getAllKeys(item.subPermissions));
+    }
+  });
+  return keys;
+};
+
+const transformTreeData = (data: TreeStructure[]): DataNode[] => {
+  return data.map((item) => ({
+    ...item,
+    key: item.id, // 关键:将 id 映射到 key
+    title: item.menuName,
+    children: item.subPermissions ? transformTreeData(item.subPermissions) : undefined,
+  }));
+};
+
+watch(
+  () => checkedAll.value,
+  (count) => {
+    if (count) {
+      console.log(count);
+    }
+  },
+);
+
+onMounted(() => {
+  handleRequest(async () => {
+    const data = await getSubPermList(0);
+    const treeData = transformTreeData(data);
+    treeStructure.value = treeData[0].subPermissions;
+  });
+});
+</script>
+
+<template>
+  <div>
+    <div class="character"><span class="character-text">*</span>创建角色</div>
+    <AFlex :vertical="true" :gap="16">
+      <AFlex align="center">
+        <AFlex class="input-style" align="center"> 管理员 </AFlex>
+        <div class="input-style-text">默认角色</div>
+      </AFlex>
+      <AFlex align="center">
+        <AFlex class="input-style" align="center"> 运维人员 </AFlex>
+        <div class="input-style-text">默认角色</div>
+      </AFlex>
+      <AFlex align="center" v-for="(item, index) in characterList" :key="index">
+        <AFlex class="input-style input-background" align="center"> 角色 </AFlex>
+        <div @click="characterEditor">
+          <AFlex class="editorial-role" align="center" justify="center">
+            <SvgIcon name="edit-o" />
+          </AFlex>
+        </div>
+        <div @click="characterDelete(index)">
+          <AFlex class="editorial-role" align="center" justify="center">
+            <SvgIcon class="icon-color" name="delete" />
+          </AFlex>
+        </div>
+      </AFlex>
+    </AFlex>
+    <AButton type="primary" ghost class="icon-button button-style" @click="addCharacter">
+      <AFlex align="center">
+        <SvgIcon name="plus" />
+        <span> 增加角色 </span>
+      </AFlex>
+    </AButton>
+    <AModal v-model:open="characterOpen" title="添加角色" width="920px" :mask-closable="false" :footer="null">
+      <AForm
+        ref="formRef"
+        class="alarm-modal"
+        :model="characterForm"
+        label-align="left"
+        :rules="rules"
+        :label-col="{ span: 3 }"
+      >
+        <AFormItem label="角色名称" name="name">
+          <AInput class="input-width" v-model:value="characterForm.name" placeholder="请输入角色名称" />
+        </AFormItem>
+        <AFormItem label="角色描述">
+          <ATextarea
+            class="input-width"
+            v-model:value="characterForm.name"
+            :placeholder="t('common.pleaseEnter')"
+            :auto-size="{ minRows: 4 }"
+          />
+        </AFormItem>
+        <AFormItem label="菜单权限配置">
+          <div class="permission-configuration">
+            <ACheckbox class="select-all" v-model:checked="checked" @change="selectAll">全选</ACheckbox>
+            <ATree
+              v-model:expanded-keys="expandedKeys"
+              v-model:selected-keys="selectedKeys"
+              v-model:checked-keys="checkedKeys"
+              :tree-data="treeStructure"
+              checkable
+              :field-names="fieldNames"
+              @check="addMenu"
+            />
+          </div>
+        </AFormItem>
+      </AForm>
+      <AFlex justify="flex-end" class="footer">
+        <AButton class="button-right" type="primary" ghost @click="cancelSave">{{ $t('common.cancel') }}</AButton>
+        <AButton type="primary" @click="characterSave">保存</AButton>
+      </AFlex>
+    </AModal>
+    <ConfirmModal
+      ref="modalComponent"
+      :title="t('common.deleteConfirmation')"
+      :description-text="t('common.confirmDeletion')"
+      :icon="{ name: 'delete' }"
+      :icon-bg-color="'#F56C6C'"
+      @confirm="confirm"
+    />
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.select-all {
+  margin-bottom: 12px;
+  margin-left: 10px;
+  font-size: 14px;
+  font-style: normal;
+  font-weight: 400;
+  line-height: 24px;
+  color: #000;
+  text-align: left;
+}
+
+.permission-configuration {
+  width: 772px;
+  height: 294px;
+  padding: 16px 14px 24px;
+  overflow: auto;
+  background: #fff;
+  border: 1px solid #d9d9d9;
+  border-radius: 4px;
+}
+
+.input-width {
+  width: 328px;
+}
+
+.button-right {
+  margin-right: 16px;
+}
+
+.button-style {
+  margin-top: 24px;
+}
+
+.icon-color {
+  color: #f67f7f;
+}
+
+.editorial-role {
+  width: 32px;
+  height: 32px;
+  margin-right: 12px;
+  cursor: pointer;
+  background: #fff;
+  border: 1px solid #d9d9d9;
+  border-radius: 4px;
+}
+
+.character {
+  margin-bottom: 16px;
+  color: rgb(0 0 0 / 85%);
+}
+
+.input-style-text {
+  font-size: 12px;
+  font-style: normal;
+  font-weight: 400;
+  line-height: 22px;
+  color: #666;
+  text-align: left;
+}
+
+.input-style {
+  width: 256px;
+  height: 32px;
+  padding-left: 12px;
+  margin-right: 16px;
+  font-size: 14px;
+  font-style: normal;
+  font-weight: 400;
+  line-height: 22px;
+  color: #333;
+  text-align: left;
+  background: #f5f7fa;
+  border: 1px solid rgb(0 0 0 / 15%);
+  border-radius: 4px;
+}
+
+.input-background {
+  background: #fff;
+}
+
+.character-text {
+  font-size: 14px;
+  font-style: normal;
+  font-weight: 400;
+  line-height: 22px;
+  color: #e02020;
+  text-align: left;
+}
+</style>