Преглед изворни кода

Merge branch 'master' of http://gitlab.smartai.com/zhupei/smartai2dian0

zhupei@smartai.com пре 1 година
родитељ
комит
6fbf83d997

+ 35 - 0
src/modules/permission/Modal.vue

@@ -0,0 +1,35 @@
+<template>
+  <a-modal v-model:visible="visible" :title="title" @ok="ok" @cancel="editHandleCancel" width="40%">
+    <slot></slot>
+  </a-modal>
+</template>
+
+<script setup>
+import { toRefs } from 'vue';
+
+// 定义组件的 props(根据实际需要进行修改)
+const props = defineProps({
+  visible: Boolean,
+  title: {
+    type: String,
+    required: true,
+    default: '标题'
+  }
+});
+
+// 事件发射器
+const emit = defineEmits(['ok', 'cancel']);
+
+const { visible, title } = toRefs(props);
+
+// 处理确认事件
+const ok = () => {
+  // 进行表单验证和提交逻辑
+  // 根据需要增加表单验证逻辑
+  emit('ok');
+}
+
+const editHandleCancel = () => {
+  emit('cancel');
+}
+</script>

+ 51 - 16
src/modules/permission/TreeParts.vue

@@ -3,21 +3,21 @@
     <template #title>
       <span>机构</span>
       <a-tooltip content="点击后可通过拖拽更改机构结构,再次点击来关闭" background-color="#165DFF">
-        <a-button type="outline" style="float: right;" @click="toggleDraggable" :style="{ color: buttonColor }"
+        <a-button :type="is_lock ? 'secondary' : 'outline'" style="float: right;" @click="toggleDraggable"
           v-has-permi="'/base/syorganization!update'">
-          <component :is="lock_icon" />
+          <component :is="is_lock ? 'IconUnlock' : 'IconLock'" />
         </a-button>
       </a-tooltip>
     </template>
-    <a-tree class="tree-demo" :draggable="draggable" blockNode :data="treeData" :show-line="true" :fieldNames="{
+    <a-tree :draggable="is_lock" blockNode :data="treeData" :show-line="true" :fieldNames="{
       key: 'deptId',
       title: 'deptName',
       children: 'children',
-    }" @drop="onDrop" @select="showDetail">
+    }" @drop="onDrop" @select="nodeClick">
       <template #extra="nodeData">
-        <IconPlus class="icon-plus" @click="() => onIconClick(nodeData)" />
+        <IconPlus class="icon-plus" @click="() => onAddBtnClick(nodeData)" />
         <a-popconfirm v-if="nodeData.deptName != 'root'" content="请确认是否删除?" type="success"
-          @ok="() => onIconClickDelete(nodeData)">
+          @ok="() => onDeleteBtnClickDelete(nodeData)">
           <IconDelete class="icon-delete" />
         </a-popconfirm>
       </template>
@@ -36,24 +36,59 @@ const props = defineProps({
 
 const { treeData } = toRefs(props);
 
+const emit = defineEmits(['nodeClick', 'onAddBtnClick', 'onDeleteBtnClickDelete']);
 
-let draggable = ref(false);
-let lock_icon = ref("IconLock");
+let is_lock = ref(false);
 
 const toggleDraggable = () => {
-  draggable.value = !draggable.value;
-  buttonColor.value = draggable.value ? '#575757' : '';
-  lock_icon.value = draggable.value ? 'IconUnlock' : 'IconLock';
+  is_lock.value = !is_lock.value;
 };
 
-const showDetail = (id) => {
-  OrganizationById(id).then((res) => {
-    deptform.value = { ...res.data };
-  });
+const nodeClick = (id) => {
+  emit('nodeClick', id);
+
+};
+const onAddBtnClick = (nodeData) => {
+  emit('addBtnClick', nodeData);
+}
+const onDeleteBtnClickDelete = (nodeData) => {
+  emit('deleteBtnClickDelete', nodeData);
 };
 
-const onIconClickDelete = (nodeData) => {
+const onDrop = ({ dragNode, dropNode, dropPosition }) => {
+  const data = treeData.value;
+  OrganizationUpdate({
+    orderNum: '0',
+    parentId: dropNode.deptId,
+    deptId: dragNode.deptId,
+  });
+  const loop = (data, key, callback) => {
+    data.some((item, index, arr) => {
+      if (item.deptId === key) {
+        callback(item, index, arr);
+        return true;
+      }
+      if (item.children) {
+        return loop(item.children, key, callback);
+      }
+      return false;
+    });
+  };
+
+  loop(data, dragNode.deptId, (_, index, arr) => {
+    arr.splice(index, 1);
+  });
 
+  if (dropPosition === 0) {
+    loop(data, dropNode.deptId, (item) => {
+      item.children = item.children || [];
+      item.children.push(dragNode);
+    });
+  } else {
+    loop(data, dropNode.deptId, (_, index, arr) => {
+      arr.splice(dropPosition < 0 ? index : index + 1, 0, dragNode);
+    });
+  }
 };
 </script>
 

+ 71 - 0
src/modules/permission/account/AccountForm.vue

@@ -0,0 +1,71 @@
+<template>
+  <a-form :model="edit_form" auto-label-width>
+    <a-row :gutter="24">
+      <a-col :span="12">
+        <a-form-item field="loginName" label="用户名" :rules="[
+          { required: true, message: '用户名必填' },
+          { maxLength: 50, message: '长度不超过50' },
+        ]">
+          <a-input v-model="edit_form.loginName" />
+        </a-form-item>
+      </a-col>
+      <a-col :span="12">
+        <a-form-item field="userName" label="姓名">
+          <a-input v-model="edit_form.userName" />
+        </a-form-item>
+      </a-col>
+    </a-row>
+    <a-row :gutter="24">
+      <a-col :span="12">
+        <a-form-item field="phoneNumber" label="手机号">
+          <a-input v-model="edit_form.phoneNumber" />
+        </a-form-item>
+      </a-col>
+      <a-col :span="12">
+        <a-form-item required field="email" label="邮箱" :rules="[
+          { required: true, message: '邮箱必填' },
+          { maxLength: 50, message: '长度不超过50' },
+        ]">
+          <a-input v-model="edit_form.email" />
+        </a-form-item>
+      </a-col>
+    </a-row>
+    <a-row :gutter="24">
+      <a-col :span="12">
+        <a-form-item field="password" label="密码">
+          <a-input v-model="edit_form.password" type="password" />
+        </a-form-item>
+      </a-col>
+      <a-col :span="12">
+        <a-form-item field="role" label="角色">
+          <a-select multiple v-model="edit_form.role" :options="roles" @change="roleChange">
+          </a-select>
+        </a-form-item>
+      </a-col>
+    </a-row>
+  </a-form>
+</template>
+
+<script setup>
+import { toRefs } from 'vue';
+
+const props = defineProps({
+  edit_form: Object,
+  roles: Array,
+});
+
+// 事件发射器
+const emit = defineEmits(['ok', 'cancel']);
+
+const { edit_form, roles } = toRefs(props);
+
+// 角色变化处理
+const roleChange = (selectedRoles) => {
+
+  edit_form.role = selectedRoles;
+}
+</script>
+
+<style lang="css" scoped>
+/* 这里可以添加自定义样式 */
+</style>

+ 1 - 1
src/modules/permission/account/DeptConfigModal.vue

@@ -40,7 +40,7 @@ const props = defineProps({
   },
   record: {
     type: Object,
-    default: false
+    default: () => { }
   }
 });
 

+ 0 - 89
src/modules/permission/account/EditModal.vue

@@ -1,89 +0,0 @@
-<template>
-  <a-modal v-model:visible="visible" :title="title" @ok="ok" @cancel="editHandleCancel" width="40%">
-    <a-form ref="formRef" :model="edit_form" auto-label-width>
-      <a-row :gutter="24">
-        <a-col :span="12">
-          <a-form-item field="loginName" label="用户名" :rules="[
-            { required: true, message: '用户名必填' },
-            { maxLength: 50, message: '长度不超过50' },
-          ]">
-            <a-input v-model="edit_form.loginName" />
-          </a-form-item>
-        </a-col>
-        <a-col :span="12">
-          <a-form-item field="userName" label="姓名">
-            <a-input v-model="edit_form.userName" />
-          </a-form-item>
-        </a-col>
-      </a-row>
-      <a-row :gutter="24">
-        <a-col :span="12">
-          <a-form-item field="phoneNumber" label="手机号">
-            <a-input v-model="edit_form.phoneNumber" />
-          </a-form-item>
-        </a-col>
-        <a-col :span="12">
-          <a-form-item required field="email" label="邮箱" :rules="[
-            { required: true, message: '邮箱必填' },
-            { maxLength: 50, message: '长度不超过50' },
-          ]">
-            <a-input v-model="edit_form.email" />
-          </a-form-item>
-        </a-col>
-      </a-row>
-      <a-row :gutter="24">
-        <a-col :span="12">
-          <a-form-item field="password" label="密码">
-            <a-input v-model="edit_form.password" type="password" />
-          </a-form-item>
-        </a-col>
-        <a-col :span="12">
-          <a-form-item field="role" label="角色">
-            <a-select multiple v-model="edit_form.role" :options="roles" :field-names="field_names"
-              @change="roleChange">
-            </a-select>
-          </a-form-item>
-        </a-col>
-      </a-row>
-    </a-form>
-  </a-modal>
-</template>
-
-<script setup>
-import { toRefs } from 'vue';
-
-// 定义组件的 props(根据实际需要进行修改)
-const props = defineProps({
-  edit_form: Object,
-  visible: Boolean,
-  title: String,
-  roles: Array,
-  field_names: Object
-});
-
-// 事件发射器
-const emit = defineEmits(['ok', 'cancel']);
-
-const { edit_form, visible, title, roles, field_names } = toRefs(props);
-
-// 处理确认事件
-const ok = () => {
-  // 进行表单验证和提交逻辑
-  // 根据需要增加表单验证逻辑
-  emit('ok', edit_form);
-}
-
-const editHandleCancel = () => {
-  emit('cancel');
-  visible.value = false;
-}
-
-// 角色变化处理
-const roleChange = (selectedRoles) => {
-  edit_form.role = selectedRoles;
-}
-</script>
-
-<style lang="css" scoped>
-/* 这里可以添加自定义样式 */
-</style>

+ 7 - 5
src/modules/permission/account/index.vue

@@ -21,8 +21,9 @@
     </DataTable>
   </a-card>
 
-  <EditModal :visible.sync="edit_visible" :edit_form="edit_form" @ok="editModalSave" @cancel="handleEdit(null, false)"
-    :title="'编辑'" />
+  <Modal :visible.sync="edit_visible" @ok="editModalSave" @cancel="handleEdit(null, false)" :title="'编辑'">
+    <AccountForm :edit_form="edit_form" />
+  </Modal>
 
   <ViewPermission :visible.sync="view_permission_visible" @cancel="handleView(null, false)"
     @close="handleView(null, false)" :view_form="view_form" :menu_permissions_list="menu_permissions_list"
@@ -36,10 +37,11 @@
 
 <script setup>
 import { ref, reactive } from 'vue';
-import { Modal } from '@arco-design/web-vue';
+import { Modal as ArcoModal } from '@arco-design/web-vue';
 import DataTable from '../../../views/permission/DataTable.vue';
 import Action from '../../../views/permission/Action.vue';
-import EditModal from './EditModal.vue';
+import Modal from '../Modal.vue';
+import AccountForm from './AccountForm.vue';
 import ViewPermission from './ViewPermission.vue';
 import DeptConfigModal from './DeptConfigModal.vue';
 
@@ -243,7 +245,7 @@ const refreshClick = () => {
 }
 
 const handleResetPassword = (record) => {
-  Modal.success({
+  ArcoModal.success({
     title: '重置密码',
     content: '该用户密码重置为000000',
   });

+ 10 - 23
src/modules/permission/org/OrgForm.vue

@@ -1,30 +1,29 @@
 <template>
-  <a-form :model="dept_form" layout="horizontal" ref="formRef">
+  <a-form :model="org_form" layout="horizontal" ref="formRef">
     <a-form-item field="parentName" label="上级机构" disabled>
-      <a-input v-model="dept_form.parentName" />
+      <a-input v-model="org_form.parentName" />
     </a-form-item>
     <a-form-item field="status" label="机构状态">
-      <a-switch checked-value="0" unchecked-value="1" v-model="dept_form.status"></a-switch>
+      <a-switch checked-value="0" unchecked-value="1" v-model="org_form.status"></a-switch>
     </a-form-item>
     <a-form-item field="deptName" label="机构名称"
       :rules="[{ required: true, message: '机构名称必填' }, { maxLength: 50, message: '长度不超过50' }]">
-      <a-input v-model="dept_form.deptName" />
+      <a-input v-model="org_form.deptName" />
     </a-form-item>
     <a-form-item field="leader" label="联系人"
       :rules="[{ required: true, message: '联系人必填' }, { maxLength: 50, message: '长度不超过50' }]">
-      <a-input v-model="dept_form.leader" />
+      <a-input v-model="org_form.leader" />
     </a-form-item>
     <a-form-item field="phone" label="联系电话"
       :rules="[{ required: true, message: '联系电话必填' }, { maxLength: 50, message: '长度不超过50' }]">
-      <a-input v-model="dept_form.phone" />
+      <a-input v-model="org_form.phone" />
     </a-form-item>
     <a-form-item field="address" label="机构地址">
-      <a-input v-model="dept_form.address" />
+      <a-input v-model="org_form.address" />
     </a-form-item>
     <a-form-item>
       <a-space>
-        <a-button @click="handleSubmit">保存</a-button>
-        <a-button @click="handleReset">重置</a-button>
+        <slot name="actions"></slot>
       </a-space>
     </a-form-item>
   </a-form>
@@ -35,7 +34,7 @@ import { toRefs } from 'vue';
 
 // 接收父组件传入的参数
 const props = defineProps({
-  dept_form: {
+  org_form: {
     type: Object,
     required: true,
     default: () => ({
@@ -50,20 +49,8 @@ const props = defineProps({
 });
 
 // 将 props 解构
-const { dept_form } = toRefs(props);
+const { org_form } = toRefs(props);
 
-// 定义要抛出的事件
-const emit = defineEmits(['save', 'reset']);
-
-// 处理保存逻辑
-const handleSubmit = () => {
-  emit('save', dept_form.value);
-};
-
-// 处理重置逻辑
-const handleReset = () => {
-  emit('reset');
-};
 </script>
 
 <style lang="css" scoped></style>

+ 33 - 9
src/modules/permission/org/index.vue

@@ -1,13 +1,23 @@
 <template>
   <div class="organization-container">
     <div class="left">
-      <TreeParts :treeData="org_tree_data" />
+      <TreeParts :treeData="org_tree_data" @nodeClick="onNodeClick" @addBtnClick="onAddBtnClick"
+        @deleteBtnClick="onDeleteBtnClick" />
     </div>
     <div class="right">
       <a-card title="详情" style="height: 100%;">
-        <OrgForm :dept_form="dept_form" @save="orgFromSave" @reset="orgFromReset"></OrgForm>
+        <OrgForm :org_form="org_form" @save="orgFromSave" @reset="orgFromReset">
+          <template #actions>
+            <a-button @click="orgFromSave">保存</a-button>
+            <a-button @click="orgFromReset">重置</a-button>
+          </template>
+        </OrgForm>
       </a-card>
     </div>
+
+    <Modal :visible.sync="org_visible" @ok="orgFromSave" @cancel="org_visible = false" :title="'新增'">
+      <OrgForm></OrgForm>
+    </Modal>
   </div>
 </template>
 
@@ -15,6 +25,9 @@
 import { ref, reactive, toRefs } from 'vue';
 import TreeParts from '../TreeParts.vue';
 import OrgForm from './OrgForm.vue';
+import Modal from '../Modal.vue';
+
+const org_visible = ref(false);
 
 const org_tree_data = ref([
   {
@@ -142,7 +155,7 @@ const org_tree_data = ref([
   }
 ]);
 
-const dept_form = reactive({
+const org_form = reactive({
   parentName: '',
   status: 0,
   deptName: '',
@@ -150,18 +163,29 @@ const dept_form = reactive({
   phone: '',
   address: ''
 });
+// 树节点操作相关
+const onNodeClick = (id) => {
+  console.log('id', id)
+}
+const onAddBtnClick = (nodeData) => {
+  console.log('nodeData', nodeData)
+  org_visible.value = true;
+}
+const onDeleteBtnClick = (nodeData) => {
+  console.log('nodeData', nodeData)
+}
 
 const orgFromSave = (data) => {
   console.log(data);
 }
 
 const orgFromReset = () => {
-  dept_form.parentName = '';
-  dept_form.status = 0;
-  dept_form.deptName = '';
-  dept_form.leader = '';
-  dept_form.phone = '';
-  dept_form.address = '';
+  org_form.parentName = '';
+  org_form.status = 0;
+  org_form.deptName = '';
+  org_form.leader = '';
+  org_form.phone = '';
+  org_form.address = '';
 }
 </script>
 

+ 82 - 0
src/modules/permission/resource/ResourceForm.vue

@@ -0,0 +1,82 @@
+<template>
+  <a-form :model="resource_form" layout="horizontal">
+    <a-form-item field="parentName" label="上级资源" disabled>
+      <a-input v-model="resource_form.parentName" />
+    </a-form-item>
+    <a-form-item field="status" label="资源状态">
+      <a-switch checked-value="0" unchecked-value="1" v-model="resource_form.status"></a-switch>
+    </a-form-item>
+    <a-form-item field="menuName" label="资源名称"
+      :rules="[{ required: true, message: '资源名称必填' }, { maxLength: 50, message: '长度不超过50' }]">
+      <a-input v-model="resource_form.menuName" />
+    </a-form-item>
+    <a-form-item field="menuName" label="资源图标">
+      <Upload :action="uploadAction" :limit="1" :url="resource_form.icon" @update:fileList="updateFileList"
+        @success="handleSuccess"></Upload>
+    </a-form-item>
+    <a-form-item field="menuType" label="资源类型" :rules="[{ required: true, message: '资源类型必填' }]">
+      <a-select v-model="resource_form.menuType" :options="options" :field-names="fieldNames"
+        :style="{ width: '320px' }" placeholder="请选择" />
+    </a-form-item>
+    <a-form-item field="description" label="提示词">
+      <a-input v-model="resource_form.description" placeholder="请输入提示词" />
+    </a-form-item>
+    <a-form-item field="perms" label="资源控制权限字符"
+      :rules="[{ required: true, message: '资源控制权限字符必填' }, { maxLength: 50, message: '长度不超过50' }]">
+      <a-input v-model="resource_form.perms" />
+    </a-form-item>
+    <a-form-item field="component" label="资源地址" style="align: start"
+      :rules="[{ required: true, message: '资源地址必填' }, { maxLength: 50, message: '长度不超过50' }]">
+      <a-input v-model="resource_form.component" />
+    </a-form-item>
+    <a-form-item>
+      <a-space>
+        <slot name="actions"></slot>
+      </a-space>
+    </a-form-item>
+  </a-form>
+</template>
+
+<script setup>
+import { toRefs } from 'vue';
+import Upload from './Upload.vue';
+
+const uploadAction = '/api/v1/llm/upload'; // 替换为你的上传API
+const fileList = ref([]);
+const imageUrls = ref([]);
+
+// 接收父组件传入的参数
+const props = defineProps({
+  resource_form: {
+    type: Object,
+    required: true,
+    default: () => ({
+      parentName: '',
+      status: 0,
+      deptName: '',
+      leader: '',
+      phone: '',
+      address: ''
+    })
+  }
+});
+
+// 将 props 解构
+const { resource_form } = toRefs(props);
+
+const updateFileList = (newFileList) => {
+  fileList.value = newFileList;
+};
+
+const handleSuccess = (urls) => {
+  uploadUrl.value = urls;
+
+  const urlsArr = urls.map((url) => {
+    return httpUrl + url;
+  });
+  imageUrls.value = urlsArr; // 拿到上传的图片地址
+};
+
+</script>
+
+<style lang="css" scoped></style>

+ 72 - 0
src/modules/permission/resource/Upload.vue

@@ -0,0 +1,72 @@
+<template>
+  <a-upload v-model:fileList="fileList" list-type="picture-card" :limit="limit" :action="action" @change="handleChange"
+    @before-remove="beforeRemove" image-preview />
+</template>
+
+<script setup>
+import { computed, ref, onMounted, watch, watchEffect } from 'vue';
+
+const props = defineProps({
+  limit: {
+    type: Number,
+    default: 1,
+  },
+  action: String, // 上传的服务器地址
+  url: String, //回显的文件地址
+});
+
+const emit = defineEmits(['fileListChange', 'fileListRemove', 'success']);
+const urls = computed(() => props.url);
+const fileList = ref([]);
+
+watch(
+  () => props.url,
+  (newVal) => {
+    if (newVal) {
+      fileList.value = newVal.split(',').map((item) => ({
+        uid: item,
+        name: item,
+        status: 'done',
+        url: item,
+      }));
+    }
+  },
+  {
+    deep: true, // 开启深度监听
+  }
+);
+
+onMounted(() => {
+  if (urls.value) {
+    fileList.value = urls.value.split(',').map((item) => ({
+      uid: item,
+      name: item,
+      status: 'done',
+      url: item,
+    }));
+  }
+
+  // console.log(
+  //   window.location.origin,
+  //   import.meta.env.VITE_API_BASE_URL,
+  //   8988
+  // );
+});
+
+// console.log(urls.value, 8988);
+const beforeRemove = (file) => {
+  emit('fileListRemove');
+  fileList.value = [];
+};
+
+const handleChange = (fileList) => {
+  emit('fileListChange', fileList);
+  const successFiles = fileList.filter((item) => item.status === 'done');
+  if (successFiles.length > 0) {
+    emit(
+      'success',
+      successFiles.map((item) => item.response.data)
+    );
+  }
+};
+</script>