mirror of
				https://github.com/continew-org/continew-admin-ui.git
				synced 2025-10-31 22:57:15 +08:00 
			
		
		
		
	refactor: 优化系统管理相关代码
This commit is contained in:
		| @@ -6,7 +6,7 @@ export type * from './type' | ||||
| const BASE_URL = '/system/dict' | ||||
|  | ||||
| /** @desc 查询字典列表 */ | ||||
| export function listDict(query: T.DictQuery) { | ||||
| export function listDict(query?: T.DictQuery) { | ||||
|   return http.get<T.DictResp[]>(`${BASE_URL}/list`, query) | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,12 +1,12 @@ | ||||
| <template> | ||||
|   <span v-if="!dictItem"></span> | ||||
|   <span v-else-if="!dictItem.extend">{{ dictItem.label }}</span> | ||||
|   <a-tag v-else-if="dictItem.extend === 'primary'" color="arcoblue">{{ dictItem.label }}</a-tag> | ||||
|   <a-tag v-else-if="dictItem.extend === 'success'" color="green">{{ dictItem.label }}</a-tag> | ||||
|   <a-tag v-else-if="dictItem.extend === 'warning'" color="orangered">{{ dictItem.label }}</a-tag> | ||||
|   <a-tag v-else-if="dictItem.extend === 'error'" color="red">{{ dictItem.label }}</a-tag> | ||||
|   <a-tag v-else-if="dictItem.extend === 'default'" color="gray">{{ dictItem.label }}</a-tag> | ||||
|   <a-tag v-else :color="dictItem.extend">{{ dictItem.label }}</a-tag> | ||||
|   <span v-else-if="!dictItem.extra">{{ dictItem.label }}</span> | ||||
|   <a-tag v-else-if="dictItem.extra === 'primary'" color="arcoblue">{{ dictItem.label }}</a-tag> | ||||
|   <a-tag v-else-if="dictItem.extra === 'success'" color="green">{{ dictItem.label }}</a-tag> | ||||
|   <a-tag v-else-if="dictItem.extra === 'warning'" color="orangered">{{ dictItem.label }}</a-tag> | ||||
|   <a-tag v-else-if="dictItem.extra === 'error'" color="red">{{ dictItem.label }}</a-tag> | ||||
|   <a-tag v-else-if="dictItem.extra === 'default'" color="gray">{{ dictItem.label }}</a-tag> | ||||
|   <a-tag v-else :color="dictItem.extra">{{ dictItem.label }}</a-tag> | ||||
| </template> | ||||
|  | ||||
| <script setup lang="ts"> | ||||
|   | ||||
| @@ -1,14 +1,14 @@ | ||||
| <template> | ||||
|   <div class="container"> | ||||
|     <a-row :gutter="16"> | ||||
|       <a-col :span="24" :md="18"> | ||||
|       <a-col :span="24" :md="17"> | ||||
|         <GiTable | ||||
|           v-model:selectedKeys="selectedKeys" | ||||
|           row-key="id" | ||||
|           :data="dataList" | ||||
|           :columns="listColumns" | ||||
|           :loading="loading" | ||||
|           :scroll="{ x: '100%', y: '100%', minWidth: 900 }" | ||||
|           :scroll="{ x: '100%', y: '100%', minWidth: 500 }" | ||||
|           style="max-height: 600px" | ||||
|           :pagination="pagination" | ||||
|           :disabled-tools="['size', 'fullscreen', 'setting', 'refresh']" | ||||
| @@ -57,7 +57,7 @@ | ||||
|           </template> | ||||
|         </GiTable> | ||||
|       </a-col> | ||||
|       <a-col :span="24" :md="6" class="section"> | ||||
|       <a-col :span="24" :md="7" class="section"> | ||||
|         <a-card> | ||||
|           <template #title>已选择: {{ selectedKeys.length }}</template> | ||||
|           <a-table :columns="selectedColumns" :data="[...selectedData.values()]" :pagination="paginationOptions"> | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
|   "theme": "light", | ||||
|   "themeColor": "#165DFF", | ||||
|   "tab": true, | ||||
|   "tabMode": "card", | ||||
|   "tabMode": "card-gutter", | ||||
|   "animate": false, | ||||
|   "animateMode": "zoom-fade", | ||||
|   "menuCollapse": true, | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| <template> | ||||
|   <a-drawer v-model:visible="visible" title="应用详情" :width="width >= 580 ? 580 : '100%'" :footer="false"> | ||||
|   <a-drawer v-model:visible="visible" title="应用详情" :width="width >= 600 ? 600 : '100%'" :footer="false"> | ||||
|     <a-descriptions :column="1" size="large" class="general-description" bordered> | ||||
|       <a-descriptions-item label="应用名称">{{ dataDetail?.name }}</a-descriptions-item> | ||||
|       <a-descriptions-item label="APPKEY">{{ dataDetail?.appKey }}</a-descriptions-item> | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| <template> | ||||
|   <a-modal | ||||
|     v-model:visible="visible" :title="title" :mask-closable="false" :esc-to-close="false" | ||||
|     :width="width >= 500 ? 500 : '100%'" draggable @before-ok="save" @ok="saveAfter" @close="reset" | ||||
|     :width="width >= 600 ? 600 : '100%'" draggable @before-ok="save" @ok="saveAfter" @close="reset" | ||||
|   > | ||||
|     <GiForm ref="formRef" v-model="form" :options="options" :columns="columns"> | ||||
|       <template #captcha> | ||||
|   | ||||
| @@ -4,8 +4,7 @@ | ||||
|     :title="title" | ||||
|     :mask-closable="false" | ||||
|     :esc-to-close="false" | ||||
|     :modal-style="{ maxWidth: '520px' }" | ||||
|     width="90%" | ||||
|     :width="width >= 500 ? 500 : '100%'" | ||||
|     draggable | ||||
|     @before-ok="save" | ||||
|     @close="reset" | ||||
| @@ -16,22 +15,41 @@ | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| import { Message } from '@arco-design/web-vue' | ||||
| import { addDept, getDept, updateDept } from '@/apis/system' | ||||
| import { type Columns, GiForm } from '@/components/GiForm' | ||||
| import { useWindowSize } from '@vueuse/core' | ||||
| import { mapTree } from 'xe-utils' | ||||
| import { type DeptResp, addDept, getDept, updateDept } from '@/apis/system/dept' | ||||
| import { type Columns, GiForm, type Options } from '@/components/GiForm' | ||||
| import { useForm } from '@/hooks' | ||||
| import { useDept } from '@/hooks/app' | ||||
|  | ||||
| interface Props { | ||||
|   depts: DeptResp[] | ||||
| } | ||||
| const props = withDefaults(defineProps<Props>(), { | ||||
|   depts: () => [], | ||||
| }) | ||||
|  | ||||
| const emit = defineEmits<{ | ||||
|   (e: 'save-success'): void | ||||
| }>() | ||||
|  | ||||
| const { deptList, getDeptList } = useDept() | ||||
| const { width } = useWindowSize() | ||||
|  | ||||
| const dataId = ref('') | ||||
| const visible = ref(false) | ||||
| const isUpdate = computed(() => !!dataId.value) | ||||
| const title = computed(() => (isUpdate.value ? '修改部门' : '新增部门')) | ||||
| const formRef = ref<InstanceType<typeof GiForm>>() | ||||
|  | ||||
| // 转换为部门树 | ||||
| const deptSelectTree = computed(() => { | ||||
|   const data = JSON.parse(JSON.stringify(props.depts)) as DeptResp[] | ||||
|   return mapTree(data, (i) => ({ | ||||
|     key: i.id, | ||||
|     title: i.name, | ||||
|     children: i.children, | ||||
|   })) | ||||
| }) | ||||
|  | ||||
| const options: Options = { | ||||
|   form: { size: 'large' }, | ||||
|   btns: { hide: true }, | ||||
| @@ -42,7 +60,7 @@ const columns: Columns = reactive([ | ||||
|     label: '上级部门', | ||||
|     field: 'parentId', | ||||
|     type: 'tree-select', | ||||
|     data: deptList, | ||||
|     data: deptSelectTree, | ||||
|     hide: (form) => { | ||||
|       return form.parentId === 0 | ||||
|     }, | ||||
| @@ -111,30 +129,6 @@ const reset = () => { | ||||
|   resetForm() | ||||
| } | ||||
|  | ||||
| const visible = ref(false) | ||||
| // 新增 | ||||
| const onAdd = (id?: string) => { | ||||
|   if (!deptList.value.length) { | ||||
|     getDeptList() | ||||
|   } | ||||
|   reset() | ||||
|   form.parentId = id | ||||
|   dataId.value = '' | ||||
|   visible.value = true | ||||
| } | ||||
|  | ||||
| // 修改 | ||||
| const onUpdate = async (id: string) => { | ||||
|   if (!deptList.value.length) { | ||||
|     await getDeptList() | ||||
|   } | ||||
|   reset() | ||||
|   dataId.value = id | ||||
|   const res = await getDept(id) | ||||
|   Object.assign(form, res.data) | ||||
|   visible.value = true | ||||
| } | ||||
|  | ||||
| // 保存 | ||||
| const save = async () => { | ||||
|   try { | ||||
| @@ -147,8 +141,6 @@ const save = async () => { | ||||
|       await addDept(form) | ||||
|       Message.success('新增成功') | ||||
|     } | ||||
|     // 更新部门树 | ||||
|     await getDeptList() | ||||
|     emit('save-success') | ||||
|     return true | ||||
|   } catch (error) { | ||||
| @@ -156,5 +148,24 @@ const save = async () => { | ||||
|   } | ||||
| } | ||||
|  | ||||
| // 新增 | ||||
| const onAdd = (id?: string) => { | ||||
|   reset() | ||||
|   form.parentId = id | ||||
|   dataId.value = '' | ||||
|   visible.value = true | ||||
| } | ||||
|  | ||||
| // 修改 | ||||
| const onUpdate = async (id: string) => { | ||||
|   reset() | ||||
|   dataId.value = id | ||||
|   const { data } = await getDept(id) | ||||
|   Object.assign(form, data) | ||||
|   visible.value = true | ||||
| } | ||||
|  | ||||
| defineExpose({ onAdd, onUpdate }) | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped></style> | ||||
|   | ||||
| @@ -2,14 +2,13 @@ | ||||
|   <div class="table-page"> | ||||
|     <GiTable | ||||
|       ref="tableRef" | ||||
|       row-key="id" | ||||
|       title="部门管理" | ||||
|       row-key="id" | ||||
|       :data="dataList" | ||||
|       :columns="columns" | ||||
|       :loading="loading" | ||||
|       :scroll="{ x: '100%', y: '100%', minWidth: 1000 }" | ||||
|       :pagination="false" | ||||
|       :disabled-tools="['size']" | ||||
|       :disabled-column-keys="['name']" | ||||
|       @refresh="search" | ||||
|     > | ||||
| @@ -18,7 +17,7 @@ | ||||
|         <IconRight v-else /> | ||||
|       </template> | ||||
|       <template #toolbar-left> | ||||
|         <a-input v-model="name" placeholder="请输入名称" allow-clear @change="search"> | ||||
|         <a-input v-model="name" placeholder="请输入名称" allow-clear> | ||||
|           <template #prefix><icon-search /></template> | ||||
|         </a-input> | ||||
|         <a-button @click="reset"> | ||||
| @@ -45,38 +44,38 @@ | ||||
|       </template> | ||||
|       <template #action="{ record }"> | ||||
|         <a-space> | ||||
|           <a-link v-permission="['system:dept:update']" @click="onUpdate(record)">修改</a-link> | ||||
|           <a-link v-permission="['system:dept:add']" @click="onAdd(record.id)">新增</a-link> | ||||
|           <a-link v-permission="['system:dept:update']" title="修改" @click="onUpdate(record)">修改</a-link> | ||||
|           <a-link | ||||
|             v-permission="['system:dept:delete']" | ||||
|             status="danger" | ||||
|             :title="record.isSystem ? '系统内置数据不能删除' : undefined" | ||||
|             :disabled="record.isSystem" | ||||
|             :title="record.isSystem ? '系统内置数据不能删除' : '删除'" | ||||
|             @click="onDelete(record)" | ||||
|           > | ||||
|             删除 | ||||
|           </a-link> | ||||
|           <a-link v-permission="['system:dept:add']" title="新增" @click="onAdd(record.id)">新增</a-link> | ||||
|         </a-space> | ||||
|       </template> | ||||
|     </GiTable> | ||||
|  | ||||
|     <DeptAddModal ref="DeptAddModalRef" @save-success="search" /> | ||||
|     <DeptAddModal ref="DeptAddModalRef" :depts="dataList" @save-success="search" /> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| import DeptAddModal from './DeptAddModal.vue' | ||||
| import { type DeptQuery, type DeptResp, deleteDept, exportDept, listDept } from '@/apis/system' | ||||
| import type GiTable from '@/components/GiTable/index.vue' | ||||
| import { type DeptQuery, type DeptResp, deleteDept, exportDept, listDept } from '@/apis/system/dept' | ||||
| import type { TableInstanceColumns } from '@/components/GiTable/type' | ||||
| import type GiTable from '@/components/GiTable/index.vue' | ||||
| import { useDownload, useTable } from '@/hooks' | ||||
| import { isMobile } from '@/utils' | ||||
| import has from '@/utils/has' | ||||
|  | ||||
| defineOptions({ name: 'SystemDept' }) | ||||
|  | ||||
| const tableRef = ref<InstanceType<typeof GiTable>>() | ||||
| const queryForm = reactive<DeptQuery>({}) | ||||
| const tableRef = ref<InstanceType<typeof GiTable>>() | ||||
| const { | ||||
|   tableData, | ||||
|   loading, | ||||
| @@ -92,7 +91,6 @@ const { | ||||
| }) | ||||
|  | ||||
| // 过滤树 | ||||
| const name = ref('') | ||||
| const searchData = (name: string) => { | ||||
|   const loop = (data: DeptResp[]) => { | ||||
|     const result = [] as DeptResp[] | ||||
| @@ -114,6 +112,7 @@ const searchData = (name: string) => { | ||||
|   return loop(tableData.value) | ||||
| } | ||||
|  | ||||
| const name = ref('') | ||||
| const dataList = computed(() => { | ||||
|   if (!name.value) return tableData.value | ||||
|   return searchData(name.value) | ||||
| @@ -121,9 +120,9 @@ const dataList = computed(() => { | ||||
|  | ||||
| const columns: TableInstanceColumns[] = [ | ||||
|   { title: '名称', dataIndex: 'name', minWidth: 170, ellipsis: true, tooltip: true }, | ||||
|   { title: '状态', slotName: 'status', align: 'center' }, | ||||
|   { title: '状态', dataIndex: 'status', slotName: 'status', align: 'center' }, | ||||
|   { title: '排序', dataIndex: 'sort', align: 'center', show: false }, | ||||
|   { title: '系统内置', slotName: 'isSystem', align: 'center', show: false }, | ||||
|   { title: '系统内置', dataIndex: 'isSystem', slotName: 'isSystem', align: 'center', show: false }, | ||||
|   { title: '描述', dataIndex: 'description', ellipsis: true, tooltip: true }, | ||||
|   { title: '创建人', dataIndex: 'createUserString', ellipsis: true, tooltip: true, show: false }, | ||||
|   { title: '创建时间', dataIndex: 'createTime', width: 180 }, | ||||
| @@ -131,8 +130,9 @@ const columns: TableInstanceColumns[] = [ | ||||
|   { title: '修改时间', dataIndex: 'updateTime', width: 180, show: false }, | ||||
|   { | ||||
|     title: '操作', | ||||
|     dataIndex: 'action', | ||||
|     slotName: 'action', | ||||
|     width: 180, | ||||
|     width: 160, | ||||
|     align: 'center', | ||||
|     fixed: !isMobile() ? 'right' : undefined, | ||||
|     show: has.hasPermOr(['system:dept:update', 'system:dept:delete', 'system:dept:add']), | ||||
| @@ -147,7 +147,7 @@ const reset = () => { | ||||
| // 删除 | ||||
| const onDelete = (record: DeptResp) => { | ||||
|   return handleDelete(() => deleteDept(record.id), { | ||||
|     content: `是否确定删除 [${record.name}]?`, | ||||
|     content: `是否确定删除部门「${record.name}」?`, | ||||
|     showModal: true, | ||||
|   }) | ||||
| } | ||||
|   | ||||
| @@ -1,7 +0,0 @@ | ||||
| export interface DeptReq { | ||||
|   parentId: string | ||||
|   name: string | ||||
|   sort: number | ||||
|   description: string | ||||
|   status: 1 | 2 | ||||
| } | ||||
| @@ -4,8 +4,7 @@ | ||||
|     :title="title" | ||||
|     :mask-closable="false" | ||||
|     :esc-to-close="false" | ||||
|     :modal-style="{ maxWidth: '520px' }" | ||||
|     width="90%" | ||||
|     :width="width >= 500 ? 500 : '100%'" | ||||
|     draggable | ||||
|     @before-ok="save" | ||||
|     @close="reset" | ||||
| @@ -24,15 +23,17 @@ | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| import { Message } from '@arco-design/web-vue' | ||||
| import { addDictItem, getDictItem, updateDictItem } from '@/apis/system' | ||||
| import { type Columns, GiForm } from '@/components/GiForm' | ||||
| import { addDictItem, getDictItem, updateDictItem } from '@/apis/system/dict' | ||||
| import { type Columns, GiForm, type Options } from '@/components/GiForm' | ||||
| import { useForm } from '@/hooks' | ||||
|  | ||||
| const emit = defineEmits<{ | ||||
|   (e: 'save-success'): void | ||||
| }>() | ||||
| const dictId = ref('') | ||||
|  | ||||
| const dataId = ref('') | ||||
| const dictId = ref('') | ||||
| const visible = ref(false) | ||||
| const isUpdate = computed(() => !!dataId.value) | ||||
| const title = computed(() => (isUpdate.value ? '修改字典项' : '新增字典项')) | ||||
| const formRef = ref<InstanceType<typeof GiForm>>() | ||||
| @@ -90,24 +91,6 @@ const reset = () => { | ||||
|   resetForm() | ||||
| } | ||||
|  | ||||
| const visible = ref(false) | ||||
| // 新增 | ||||
| const onAdd = (id: string) => { | ||||
|   reset() | ||||
|   dictId.value = id | ||||
|   dataId.value = '' | ||||
|   visible.value = true | ||||
| } | ||||
|  | ||||
| // 修改 | ||||
| const onUpdate = async (id: string) => { | ||||
|   reset() | ||||
|   dataId.value = id | ||||
|   const res = await getDictItem(id) | ||||
|   Object.assign(form, res.data) | ||||
|   visible.value = true | ||||
| } | ||||
|  | ||||
| // 保存 | ||||
| const save = async () => { | ||||
|   try { | ||||
| @@ -127,5 +110,24 @@ const save = async () => { | ||||
|   } | ||||
| } | ||||
|  | ||||
| // 新增 | ||||
| const onAdd = (id: string) => { | ||||
|   reset() | ||||
|   dataId.value = '' | ||||
|   dictId.value = id | ||||
|   visible.value = true | ||||
| } | ||||
|  | ||||
| // 修改 | ||||
| const onUpdate = async (id: string) => { | ||||
|   reset() | ||||
|   dataId.value = id | ||||
|   const { data } = await getDictItem(id) | ||||
|   Object.assign(form, data) | ||||
|   visible.value = true | ||||
| } | ||||
|  | ||||
| defineExpose({ onAdd, onUpdate }) | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped></style> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|     </a-row> | ||||
|     <a-row align="stretch" :gutter="14" class="h-full page_content"> | ||||
|       <a-col :xs="0" :sm="8" :md="7" :lg="6" :xl="5" :xxl="4" flex="260px" class="h-full ov-hidden"> | ||||
|         <DictTree placeholder="请输入名称/编码/描述" @node-click="handleSelectDict" /> | ||||
|         <DictTree @node-click="handleSelectDict" /> | ||||
|       </a-col> | ||||
|       <a-col :xs="24" :sm="16" :md="17" :lg="18" :xl="19" :xxl="20" flex="1" class="h-full ov-hidden"> | ||||
|         <GiTable | ||||
| @@ -44,10 +44,11 @@ | ||||
|           </template> | ||||
|           <template #action="{ record }"> | ||||
|             <a-space> | ||||
|               <a-link v-permission="['system:dict:item:update']" @click="onUpdate(record)">修改</a-link> | ||||
|               <a-link v-permission="['system:dict:item:update']" title="修改" @click="onUpdate(record)">修改</a-link> | ||||
|               <a-link | ||||
|                 v-permission="['system:dict:item:delete']" | ||||
|                 status="danger" | ||||
|                 title="删除" | ||||
|                 @click="onDelete(record)" | ||||
|               > | ||||
|                 删除 | ||||
| @@ -65,7 +66,7 @@ | ||||
| <script setup lang="ts"> | ||||
| import DictTree from './tree/index.vue' | ||||
| import DictItemAddModal from './DictItemAddModal.vue' | ||||
| import { type DictItemQuery, type DictItemResp, deleteDictItem, listDictItem } from '@/apis/system' | ||||
| import { type DictItemQuery, type DictItemResp, deleteDictItem, listDictItem } from '@/apis/system/dict' | ||||
| import type { TableInstanceColumns } from '@/components/GiTable/type' | ||||
| import { useTable } from '@/hooks' | ||||
| import { isMobile } from '@/utils' | ||||
| @@ -85,7 +86,6 @@ const { | ||||
|   search, | ||||
|   handleDelete, | ||||
| } = useTable((page) => listDictItem({ ...queryForm, ...page }), { immediate: false }) | ||||
|  | ||||
| const columns: TableInstanceColumns[] = [ | ||||
|   { | ||||
|     title: '序号', | ||||
| @@ -95,7 +95,7 @@ const columns: TableInstanceColumns[] = [ | ||||
|   }, | ||||
|   { title: '标签', dataIndex: 'label', slotName: 'label', minWidth: 100, align: 'center' }, | ||||
|   { title: '值', dataIndex: 'value', minWidth: 100, align: 'center', ellipsis: true, tooltip: true }, | ||||
|   { title: '状态', slotName: 'status', align: 'center' }, | ||||
|   { title: '状态', dataIndex: 'status', slotName: 'status', align: 'center' }, | ||||
|   { | ||||
|     title: '排序', | ||||
|     dataIndex: 'sort', | ||||
| @@ -111,6 +111,7 @@ const columns: TableInstanceColumns[] = [ | ||||
|   { title: '修改时间', dataIndex: 'updateTime', width: 180, show: false }, | ||||
|   { | ||||
|     title: '操作', | ||||
|     dataIndex: 'action', | ||||
|     slotName: 'action', | ||||
|     width: 130, | ||||
|     align: 'center', | ||||
|   | ||||
| @@ -4,8 +4,7 @@ | ||||
|     :title="title" | ||||
|     :mask-closable="false" | ||||
|     :esc-to-close="false" | ||||
|     :modal-style="{ maxWidth: '520px' }" | ||||
|     width="90%" | ||||
|     :width="width >= 500 ? 500 : '100%'" | ||||
|     draggable | ||||
|     @before-ok="save" | ||||
|     @close="reset" | ||||
| @@ -16,14 +15,19 @@ | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| import { Message } from '@arco-design/web-vue' | ||||
| import { addDict, getDict, updateDict } from '@/apis/system' | ||||
| import { type Columns, GiForm } from '@/components/GiForm' | ||||
| import { useWindowSize } from '@vueuse/core' | ||||
| import { addDict, getDict, updateDict } from '@/apis/system/dict' | ||||
| import { type Columns, GiForm, type Options } from '@/components/GiForm' | ||||
| import { useForm } from '@/hooks' | ||||
|  | ||||
| const emit = defineEmits<{ | ||||
|   (e: 'save-success'): void | ||||
| }>() | ||||
|  | ||||
| const { width } = useWindowSize() | ||||
|  | ||||
| const dataId = ref('') | ||||
| const visible = ref(false) | ||||
| const isUpdate = computed(() => !!dataId.value) | ||||
| const title = computed(() => (isUpdate.value ? '修改字典' : '新增字典')) | ||||
| const formRef = ref<InstanceType<typeof GiForm>>() | ||||
| @@ -55,23 +59,6 @@ const reset = () => { | ||||
|   resetForm() | ||||
| } | ||||
|  | ||||
| const visible = ref(false) | ||||
| // 新增 | ||||
| const onAdd = () => { | ||||
|   reset() | ||||
|   dataId.value = '' | ||||
|   visible.value = true | ||||
| } | ||||
|  | ||||
| // 修改 | ||||
| const onUpdate = async (id: string) => { | ||||
|   reset() | ||||
|   dataId.value = id | ||||
|   const res = await getDict(id) | ||||
|   Object.assign(form, res.data) | ||||
|   visible.value = true | ||||
| } | ||||
|  | ||||
| // 保存 | ||||
| const save = async () => { | ||||
|   const isInvalid = await formRef.value?.formRef?.validate() | ||||
| @@ -91,5 +78,23 @@ const save = async () => { | ||||
|   } | ||||
| } | ||||
|  | ||||
| // 新增 | ||||
| const onAdd = () => { | ||||
|   reset() | ||||
|   dataId.value = '' | ||||
|   visible.value = true | ||||
| } | ||||
|  | ||||
| // 修改 | ||||
| const onUpdate = async (id: string) => { | ||||
|   reset() | ||||
|   dataId.value = id | ||||
|   const { data } = await getDict(id) | ||||
|   Object.assign(form, data) | ||||
|   visible.value = true | ||||
| } | ||||
|  | ||||
| defineExpose({ onAdd, onUpdate }) | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped></style> | ||||
|   | ||||
| @@ -1,9 +1,15 @@ | ||||
| <template> | ||||
|   <a-menu class="right-menu"> | ||||
|     <a-menu-item v-permission="['system:dict:update']" @click="onClick('update')"> | ||||
|     <a-menu-item v-permission="['system:dict:update']" title="修改" @click="onClick('update')"> | ||||
|       <span>修改</span> | ||||
|     </a-menu-item> | ||||
|     <a-menu-item v-permission="['system:dict:delete']" class="danger" :title="data.isSystem ? '系统内置数据不能删除' : undefined" :disabled="data.isSystem" @click="onClick('delete')"> | ||||
|     <a-menu-item | ||||
|       v-permission="['system:dict:delete']" | ||||
|       class="danger" | ||||
|       :disabled="data.isSystem" | ||||
|       :title="data.isSystem ? '系统内置数据不能删除' : '删除'" | ||||
|       @click="onClick('delete')" | ||||
|     > | ||||
|       <span>删除</span> | ||||
|     </a-menu-item> | ||||
|   </a-menu> | ||||
|   | ||||
| @@ -1,15 +1,15 @@ | ||||
| <template> | ||||
|   <div class="dict-tree"> | ||||
|     <div class="dict-tree__search"> | ||||
|       <a-input v-model="inputValue" :placeholder="props.placeholder" allow-clear> | ||||
|   <div class="container"> | ||||
|     <div class="search"> | ||||
|       <a-input v-model="searchKey" placeholder="请输入关键词" allow-clear> | ||||
|         <template #prefix><icon-search /></template> | ||||
|       </a-input> | ||||
|       <a-button v-permission="['system:dict:add']" type="primary" @click="onAdd"> | ||||
|         <template #icon><icon-plus /></template> | ||||
|       </a-button> | ||||
|     </div> | ||||
|     <div class="dict-tree__container"> | ||||
|       <div class="dict-tree__tree"> | ||||
|     <div class="tree-wrapper"> | ||||
|       <div class="tree"> | ||||
|         <a-tree | ||||
|           :data="(treeData as unknown as TreeNodeData[])" | ||||
|           :field-names="{ key: 'id' }" | ||||
| @@ -44,21 +44,15 @@ | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script setup lang="tsx"> | ||||
| <script setup lang="ts"> | ||||
| import { Message, Modal } from '@arco-design/web-vue' | ||||
| import type { TreeNodeData } from '@arco-design/web-vue' | ||||
| import { mapTree } from 'xe-utils' | ||||
| import DictAddModal from './DictAddModal.vue' | ||||
| import RightMenu from './RightMenu.vue' | ||||
| import { type DictQuery, type DictResp, deleteDict, listDict } from '@/apis/system' | ||||
| import { type DictResp, deleteDict, listDict } from '@/apis/system/dict' | ||||
| import has from '@/utils/has' | ||||
|  | ||||
| interface Props { | ||||
|   placeholder?: string | ||||
| } | ||||
| const props = withDefaults(defineProps<Props>(), { | ||||
|   placeholder: '请输入关键词', | ||||
| }) | ||||
| const emit = defineEmits<{ | ||||
|   (e: 'node-click', keys: Array<any>): void | ||||
| }>() | ||||
| @@ -73,21 +67,17 @@ const select = (keys: Array<any>) => { | ||||
|   emit('node-click', keys) | ||||
| } | ||||
|  | ||||
| const queryForm = reactive<DictQuery>({ | ||||
|   sort: ['createTime,asc'], | ||||
| }) | ||||
|  | ||||
| interface TreeItem extends DictResp { | ||||
|   popupVisible: boolean | ||||
| } | ||||
| const treeData = ref<TreeItem[]>([]) | ||||
| const dataList = ref<TreeItem[]>([]) | ||||
| const loading = ref(false) | ||||
| // 查询树列表 | ||||
| const getTreeData = async (query: DictQuery = { ...queryForm }) => { | ||||
| const getTreeData = async () => { | ||||
|   try { | ||||
|     loading.value = true | ||||
|     const { data } = await listDict(query) | ||||
|     treeData.value = mapTree(data, (i) => ({ | ||||
|     const { data } = await listDict() | ||||
|     dataList.value = mapTree(data, (i) => ({ | ||||
|       ...i, | ||||
|       popupVisible: false, | ||||
|       icon: () => { | ||||
| @@ -95,18 +85,31 @@ const getTreeData = async (query: DictQuery = { ...queryForm }) => { | ||||
|       }, | ||||
|     })) | ||||
|     await nextTick(() => { | ||||
|       select([treeData.value[0]?.id]) | ||||
|       select([dataList.value[0]?.id]) | ||||
|     }) | ||||
|   } finally { | ||||
|     loading.value = false | ||||
|   } | ||||
| } | ||||
|  | ||||
| // 树查询 | ||||
| const inputValue = ref('') | ||||
| watch(inputValue, (val) => { | ||||
|   queryForm.description = val | ||||
|   getTreeData() | ||||
| // 过滤树 | ||||
| const searchKey = ref('') | ||||
| const search = (keyword: string) => { | ||||
|   const loop = (data: TreeItem[]) => { | ||||
|     const result = [] as TreeItem[] | ||||
|     data.forEach((item: TreeItem) => { | ||||
|       if (item.name?.toLowerCase().includes(keyword) || item.code?.toLowerCase().includes(keyword)) { | ||||
|         result.push({ ...item }) | ||||
|       } | ||||
|     }) | ||||
|     return result | ||||
|   } | ||||
|   return loop(dataList.value) | ||||
| } | ||||
|  | ||||
| const treeData = computed(() => { | ||||
|   if (!searchKey.value) return dataList.value | ||||
|   return search(searchKey.value.toLowerCase()) | ||||
| }) | ||||
|  | ||||
| const DictAddModalRef = ref<InstanceType<typeof DictAddModal>>() | ||||
| @@ -190,7 +193,7 @@ onMounted(() => { | ||||
|   } | ||||
| } | ||||
|  | ||||
| .dict-tree { | ||||
| .container { | ||||
|   flex: 1; | ||||
|   overflow: hidden; | ||||
|   position: relative; | ||||
| @@ -199,32 +202,31 @@ onMounted(() => { | ||||
|   box-sizing: border-box; | ||||
|   height: 100%; | ||||
|  | ||||
|   &__search { | ||||
|   .search { | ||||
|     display: flex; | ||||
|     justify-content: start; | ||||
|     margin-bottom: 8px; | ||||
|     margin-bottom: 2px; | ||||
|     .arco-btn { | ||||
|       margin-left: 8px; | ||||
|       padding: 0 15px; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   &__container { | ||||
|   .tree-wrapper { | ||||
|     flex: 1; | ||||
|     overflow: hidden; | ||||
|     background-color: var(--color-bg-1); | ||||
|     position: relative; | ||||
|     height: 100%; | ||||
|     margin-bottom:10px; | ||||
|   } | ||||
|  | ||||
|   &__tree { | ||||
|     position: absolute; | ||||
|     top: 0; | ||||
|     bottom: 0; | ||||
|     left: 0; | ||||
|     right: 0; | ||||
|     overflow: auto | ||||
|     .tree { | ||||
|       position: absolute; | ||||
|       top: 0; | ||||
|       bottom: 0; | ||||
|       left: 0; | ||||
|       right: 0; | ||||
|       overflow: auto | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </style> | ||||
|   | ||||
| @@ -4,9 +4,7 @@ | ||||
|     :title="title" | ||||
|     :mask-closable="false" | ||||
|     :esc-to-close="false" | ||||
|     :modal-style="{ maxWidth: '625px' }" | ||||
|     :body-style="{ maxHeight: '70vh' }" | ||||
|     width="90%" | ||||
|     :width="width >= 600 ? 600 : '100%'" | ||||
|     draggable | ||||
|     @before-ok="save" | ||||
|     @close="reset" | ||||
| @@ -118,8 +116,9 @@ | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| import { type FormInstance, Message, type TreeNodeData } from '@arco-design/web-vue' | ||||
| import { useWindowSize } from '@vueuse/core' | ||||
| import { mapTree } from 'xe-utils' | ||||
| import { type MenuResp, addMenu, getMenu, updateMenu } from '@/apis/system' | ||||
| import { type MenuResp, addMenu, getMenu, updateMenu } from '@/apis/system/menu' | ||||
| import { useForm } from '@/hooks' | ||||
| import { filterTree, transformPathToName } from '@/utils' | ||||
|  | ||||
| @@ -134,6 +133,61 @@ const emit = defineEmits<{ | ||||
|   (e: 'save-success'): void | ||||
| }>() | ||||
|  | ||||
| const { width } = useWindowSize() | ||||
|  | ||||
| const dataId = ref('') | ||||
| const visible = ref(false) | ||||
| const isUpdate = computed(() => !!dataId.value) | ||||
| const title = computed(() => (isUpdate.value ? '修改菜单' : '新增菜单')) | ||||
| const formRef = ref<FormInstance>() | ||||
|  | ||||
| const { form, resetForm } = useForm({ | ||||
|   type: 1, | ||||
|   sort: 999, | ||||
|   isExternal: false, | ||||
|   isCache: false, | ||||
|   isHidden: false, | ||||
|   status: 1, | ||||
| }) | ||||
|  | ||||
| const componentName = computed(() => transformPathToName(form.path)) | ||||
|  | ||||
| const rules: FormInstance['rules'] = { | ||||
|   parentId: [{ required: true, message: '请选择上级菜单' }], | ||||
|   title: [{ required: true, message: '请输入菜单标题' }], | ||||
|   path: [{ required: true, message: '请输入路由地址' }], | ||||
|   name: [{ required: true, message: '请输入组件名称' }], | ||||
|   component: [{ required: true, message: '请输入组件路径' }], | ||||
|   permission: [{ required: true, message: '请输入权限标识' }], | ||||
| } | ||||
| // eslint-disable-next-line vue/return-in-computed-property | ||||
| const formRules = computed(() => { | ||||
|   if ([1, 2].includes(form.type)) { | ||||
|     const { title, name, path } = rules | ||||
|     return { title, name, path } as FormInstance['rules'] | ||||
|   } | ||||
|   if (form.type === 3) { | ||||
|     const { parentId, title, permission } = rules | ||||
|     return { parentId, title, permission } as FormInstance['rules'] | ||||
|   } | ||||
| }) | ||||
|  | ||||
| // 重置 | ||||
| const reset = () => { | ||||
|   formRef.value?.resetFields() | ||||
|   resetForm() | ||||
| } | ||||
|  | ||||
| // 设置建议组件名 | ||||
| const inputComponentName = () => { | ||||
|   form.name = componentName.value | ||||
| } | ||||
|  | ||||
| // 切换类型清除校验 | ||||
| const onChangeType = () => { | ||||
|   formRef.value?.clearValidate() | ||||
| } | ||||
|  | ||||
| // 转换为菜单树 | ||||
| const menuSelectTree = computed(() => { | ||||
|   const menus = JSON.parse(JSON.stringify(props.menus)) as MenuResp[] | ||||
| @@ -153,74 +207,6 @@ const filterOptions = (searchKey: string, nodeData: TreeNodeData) => { | ||||
|   return false | ||||
| } | ||||
|  | ||||
| const dataId = ref('') | ||||
| const isUpdate = computed(() => !!dataId.value) | ||||
| const title = computed(() => (isUpdate.value ? '修改菜单' : '新增菜单')) | ||||
| const formRef = ref<FormInstance>() | ||||
|  | ||||
| const rules: FormInstance['rules'] = { | ||||
|   parentId: [{ required: true, message: '请选择上级菜单' }], | ||||
|   title: [{ required: true, message: '请输入菜单标题' }], | ||||
|   path: [{ required: true, message: '请输入路由地址' }], | ||||
|   name: [{ required: true, message: '请输入组件名称' }], | ||||
|   component: [{ required: true, message: '请输入组件路径' }], | ||||
|   permission: [{ required: true, message: '请输入权限标识' }], | ||||
| } | ||||
|  | ||||
| const { form, resetForm } = useForm({ | ||||
|   type: 1, | ||||
|   sort: 999, | ||||
|   isExternal: false, | ||||
|   isCache: false, | ||||
|   isHidden: false, | ||||
|   status: 1, | ||||
| }) | ||||
| const componentName = computed(() => transformPathToName(form.path)) | ||||
| // eslint-disable-next-line vue/return-in-computed-property | ||||
| const formRules = computed(() => { | ||||
|   if ([1, 2].includes(form.type)) { | ||||
|     const { title, name, path } = rules | ||||
|     return { title, name, path } as FormInstance['rules'] | ||||
|   } | ||||
|   if (form.type === 3) { | ||||
|     const { parentId, title, permission } = rules | ||||
|     return { parentId, title, permission } as FormInstance['rules'] | ||||
|   } | ||||
| }) | ||||
| // 设置建议组件名 | ||||
| const inputComponentName = () => { | ||||
|   form.name = componentName.value | ||||
| } | ||||
|  | ||||
| // 切换类型清除校验 | ||||
| const onChangeType = () => { | ||||
|   formRef.value?.clearValidate() | ||||
| } | ||||
|  | ||||
| // 重置 | ||||
| const reset = () => { | ||||
|   formRef.value?.resetFields() | ||||
|   resetForm() | ||||
| } | ||||
|  | ||||
| const visible = ref(false) | ||||
| // 新增 | ||||
| const onAdd = (id?: string) => { | ||||
|   reset() | ||||
|   form.parentId = id | ||||
|   dataId.value = '' | ||||
|   visible.value = true | ||||
| } | ||||
|  | ||||
| // 修改 | ||||
| const onUpdate = async (id: string) => { | ||||
|   reset() | ||||
|   dataId.value = id | ||||
|   const res = await getMenu(id) | ||||
|   Object.assign(form, res.data) | ||||
|   visible.value = true | ||||
| } | ||||
|  | ||||
| // 保存 | ||||
| const save = async () => { | ||||
|   try { | ||||
| @@ -240,5 +226,24 @@ const save = async () => { | ||||
|   } | ||||
| } | ||||
|  | ||||
| // 新增 | ||||
| const onAdd = (id?: string) => { | ||||
|   reset() | ||||
|   form.parentId = id | ||||
|   dataId.value = '' | ||||
|   visible.value = true | ||||
| } | ||||
|  | ||||
| // 修改 | ||||
| const onUpdate = async (id: string) => { | ||||
|   reset() | ||||
|   dataId.value = id | ||||
|   const { data } = await getMenu(id) | ||||
|   Object.assign(form, data) | ||||
|   visible.value = true | ||||
| } | ||||
|  | ||||
| defineExpose({ onAdd, onUpdate }) | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped></style> | ||||
|   | ||||
| @@ -17,7 +17,7 @@ | ||||
|         <IconRight v-else /> | ||||
|       </template> | ||||
|       <template #toolbar-left> | ||||
|         <a-input v-model="title" placeholder="请输入菜单标题" allow-clear @change="search"> | ||||
|         <a-input v-model="title" placeholder="请输入菜单标题" allow-clear> | ||||
|           <template #prefix><icon-search /></template> | ||||
|         </a-input> | ||||
|         <a-button @click="reset"> | ||||
| @@ -65,11 +65,16 @@ | ||||
|       </template> | ||||
|       <template #action="{ record }"> | ||||
|         <a-space> | ||||
|           <a-link v-permission="['system:menu:update']" @click="onUpdate(record)">修改</a-link> | ||||
|           <a-link v-permission="['system:menu:add']" :disabled="![1, 2].includes(record.type)" @click="onAdd(record.id)"> | ||||
|           <a-link v-permission="['system:menu:update']" title="修改" @click="onUpdate(record)">修改</a-link> | ||||
|           <a-link v-permission="['system:menu:delete']" status="danger" title="删除" @click="onDelete(record)">删除</a-link> | ||||
|           <a-link | ||||
|             v-permission="['system:menu:add']" | ||||
|             :disabled="![1, 2].includes(record.type)" | ||||
|             :title="![1, 2].includes(record.type) ? '不可添加下级菜单' : '新增'" | ||||
|             @click="onAdd(record.id)" | ||||
|           > | ||||
|             新增 | ||||
|           </a-link> | ||||
|           <a-link v-permission="['system:menu:delete']" status="danger" @click="onDelete(record)">删除</a-link> | ||||
|         </a-space> | ||||
|       </template> | ||||
|     </GiTable> | ||||
| @@ -80,16 +85,17 @@ | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| import MenuAddModal from './MenuAddModal.vue' | ||||
| import { type MenuQuery, type MenuResp, deleteMenu, listMenu } from '@/apis/system' | ||||
| import type GiTable from '@/components/GiTable/index.vue' | ||||
| import { type MenuQuery, type MenuResp, deleteMenu, listMenu } from '@/apis/system/menu' | ||||
| import type { TableInstanceColumns } from '@/components/GiTable/type' | ||||
| import type GiTable from '@/components/GiTable/index.vue' | ||||
| import { useTable } from '@/hooks' | ||||
| import { isMobile } from '@/utils' | ||||
| import has from '@/utils/has' | ||||
| import { useTable } from '@/hooks' | ||||
|  | ||||
| defineOptions({ name: 'SystemMenu' }) | ||||
|  | ||||
| const queryForm = reactive<MenuQuery>({}) | ||||
|  | ||||
| const { | ||||
|   tableData, | ||||
|   loading, | ||||
| @@ -98,7 +104,6 @@ const { | ||||
| } = useTable(() => listMenu(queryForm), { immediate: true }) | ||||
|  | ||||
| // 过滤树 | ||||
| const title = ref('') | ||||
| const searchData = (title: string) => { | ||||
|   const loop = (data: MenuResp[]) => { | ||||
|     const result = [] as MenuResp[] | ||||
| @@ -120,6 +125,7 @@ const searchData = (title: string) => { | ||||
|   return loop(tableData.value) | ||||
| } | ||||
|  | ||||
| const title = ref('') | ||||
| const dataList = computed(() => { | ||||
|   if (!title.value) return tableData.value | ||||
|   return searchData(title.value) | ||||
| @@ -127,24 +133,25 @@ const dataList = computed(() => { | ||||
|  | ||||
| const columns: TableInstanceColumns[] = [ | ||||
|   { title: '菜单标题', dataIndex: 'title', slotName: 'title', width: 170, fixed: !isMobile() ? 'left' : undefined }, | ||||
|   { title: '类型', slotName: 'type', align: 'center' }, | ||||
|   { title: '状态', slotName: 'status', align: 'center' }, | ||||
|   { title: '类型', dataIndex: 'type', slotName: 'type', align: 'center' }, | ||||
|   { title: '状态', dataIndex: 'status', slotName: 'status', align: 'center' }, | ||||
|   { title: '排序', dataIndex: 'sort', align: 'center', show: false }, | ||||
|   { title: '路由地址', dataIndex: 'path', ellipsis: true, tooltip: true }, | ||||
|   { title: '组件名称', dataIndex: 'name', ellipsis: true, tooltip: true }, | ||||
|   { title: '组件路径', dataIndex: 'component', minWidth: 180, ellipsis: true, tooltip: true }, | ||||
|   { title: '权限标识', dataIndex: 'permission', minWidth: 180, ellipsis: true, tooltip: true }, | ||||
|   { title: '外链', slotName: 'isExternal', align: 'center' }, | ||||
|   { title: '隐藏', slotName: 'isHidden', align: 'center' }, | ||||
|   { title: '缓存', slotName: 'isCache', align: 'center' }, | ||||
|   { title: '外链', dataIndex: 'isExternal', slotName: 'isExternal', align: 'center' }, | ||||
|   { title: '隐藏', dataIndex: 'isHidden', slotName: 'isHidden', align: 'center' }, | ||||
|   { title: '缓存', dataIndex: 'isCache', slotName: 'isCache', align: 'center' }, | ||||
|   { title: '创建人', dataIndex: 'createUserString', ellipsis: true, tooltip: true, show: false }, | ||||
|   { title: '创建时间', dataIndex: 'createTime', width: 180 }, | ||||
|   { title: '修改人', dataIndex: 'updateUserString', ellipsis: true, tooltip: true, show: false }, | ||||
|   { title: '修改时间', dataIndex: 'updateTime', width: 180, show: false }, | ||||
|   { | ||||
|     title: '操作', | ||||
|     dataIndex: 'action', | ||||
|     slotName: 'action', | ||||
|     width: 180, | ||||
|     width: 160, | ||||
|     align: 'center', | ||||
|     fixed: !isMobile() ? 'right' : undefined, | ||||
|     show: has.hasPermOr(['system:menu:update', 'system:menu:delete', 'system:menu:add']), | ||||
| @@ -159,7 +166,7 @@ const reset = () => { | ||||
| // 删除 | ||||
| const onDelete = (record: MenuResp) => { | ||||
|   return handleDelete(() => deleteMenu(record.id), { | ||||
|     content: `是否确定删除 [${record.title}]?`, | ||||
|     content: `是否确定菜单「${record.title}」?`, | ||||
|     showModal: true, | ||||
|   }) | ||||
| } | ||||
|   | ||||
| @@ -1,16 +0,0 @@ | ||||
| export interface MenuForm { | ||||
|   type: 1 | 2 | 3 | ||||
|   icon: string | ||||
|   title: string | ||||
|   sort: number | ||||
|   permission: string | ||||
|   path: string | ||||
|   name: string | ||||
|   component: string | ||||
|   redirect: string | ||||
|   isExternal: boolean | ||||
|   isCache: boolean | ||||
|   isHidden: boolean | ||||
|   parentId: string | ||||
|   status: 1 | 0 | ||||
| } | ||||
| @@ -37,7 +37,7 @@ | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| import { useWindowSize } from '@vueuse/core' | ||||
| import AiEditor from './components/detail/index.vue' | ||||
| import AiEditor from './detail/components/index.vue' | ||||
| import { type NoticeResp, getNotice } from '@/apis/system' | ||||
|  | ||||
| const { width } = useWindowSize() | ||||
|   | ||||
| @@ -46,7 +46,7 @@ | ||||
|       title="用户选择" | ||||
|       :mask-closable="false" | ||||
|       :esc-to-close="false" | ||||
|       :width="width >= 1350 ? 1350 : '100%'" | ||||
|       :width="width >= 1100 ? 1100 : '100%'" | ||||
|       draggable | ||||
|       @close="reset" | ||||
|     > | ||||
| @@ -55,11 +55,10 @@ | ||||
|   </div> | ||||
| </template> | ||||
| 
 | ||||
| <script setup lang="tsx"> | ||||
| <script setup lang="ts"> | ||||
| import { Message } from '@arco-design/web-vue' | ||||
| import { useWindowSize } from '@vueuse/core' | ||||
| import { ref } from 'vue' | ||||
| import AiEditor from '../components/edit/index.vue' | ||||
| import AiEditor from './components/index.vue' | ||||
| import { useTabsStore } from '@/stores' | ||||
| import { type Columns, GiForm, type Options } from '@/components/GiForm' | ||||
| import { addNotice, getNotice, updateNotice } from '@/apis/system' | ||||
| @@ -37,10 +37,10 @@ | ||||
|   </div> | ||||
| </template> | ||||
| 
 | ||||
| <script setup lang="tsx"> | ||||
| import AiEditor from '../components/detail/index.vue' | ||||
| <script setup lang="ts"> | ||||
| import AiEditor from './components/index.vue' | ||||
| import { useTabsStore } from '@/stores' | ||||
| import { getNotice } from '@/apis/system' | ||||
| import { getNotice } from '@/apis/system/notice' | ||||
| import { useForm } from '@/hooks' | ||||
| 
 | ||||
| const containerRef = ref<HTMLElement | null>() | ||||
| @@ -35,20 +35,6 @@ | ||||
|           <template #default>新增</template> | ||||
|         </a-button> | ||||
|       </template> | ||||
|       <template #title="{ record }"> | ||||
|         <a-link @click="onDetail(record)"> | ||||
|           <a-typography-paragraph | ||||
|             class="link-text" | ||||
|             :ellipsis="{ | ||||
|               rows: 1, | ||||
|               showTooltip: true, | ||||
|               css: true, | ||||
|             }" | ||||
|           > | ||||
|             {{ record.title }} | ||||
|           </a-typography-paragraph> | ||||
|         </a-link> | ||||
|       </template> | ||||
|       <template #type="{ record }"> | ||||
|         <GiCellTag :value="record.type" :dict="notice_type" /> | ||||
|       </template> | ||||
| @@ -57,8 +43,9 @@ | ||||
|       </template> | ||||
|       <template #action="{ record }"> | ||||
|         <a-space> | ||||
|           <a-link v-permission="['system:notice:update']" @click="onUpdate(record)">修改</a-link> | ||||
|           <a-link v-permission="['system:notice:delete']" status="danger" @click="onDelete(record)"> 删除 </a-link> | ||||
|           <a-link v-permission="['system:notice:detail']" title="详情" @click="onDetail(record)">详情</a-link> | ||||
|           <a-link v-permission="['system:notice:update']" title="修改" @click="onUpdate(record)">修改</a-link> | ||||
|           <a-link v-permission="['system:notice:delete']" status="danger" title="删除" @click="onDelete(record)"> 删除 </a-link> | ||||
|         </a-space> | ||||
|       </template> | ||||
|     </GiTable> | ||||
| @@ -79,7 +66,7 @@ const { notice_type, notice_status_enum } = useDict('notice_type', 'notice_statu | ||||
|  | ||||
| const router = useRouter() | ||||
| const queryForm = reactive<NoticeQuery>({ | ||||
|   sort: ['createTime,desc'], | ||||
|   sort: ['id,desc'], | ||||
| }) | ||||
|  | ||||
| const { | ||||
| @@ -89,7 +76,6 @@ const { | ||||
|   search, | ||||
|   handleDelete, | ||||
| } = useTable((page) => listNotice({ ...queryForm, ...page }), { immediate: true }) | ||||
|  | ||||
| const columns: TableInstanceColumns[] = [ | ||||
|   { | ||||
|     title: '序号', | ||||
| @@ -98,19 +84,20 @@ const columns: TableInstanceColumns[] = [ | ||||
|     render: ({ rowIndex }) => h('span', {}, rowIndex + 1 + (pagination.current - 1) * pagination.pageSize), | ||||
|   }, | ||||
|   { title: '标题', dataIndex: 'title', slotName: 'title', minWidth: 200, ellipsis: true, tooltip: true }, | ||||
|   { title: '类型', slotName: 'type', align: 'center' }, | ||||
|   { title: '状态', slotName: 'status', align: 'center' }, | ||||
|   { title: '类型', dataIndex: 'type', slotName: 'type', align: 'center' }, | ||||
|   { title: '状态', dataIndex: 'status', slotName: 'status', align: 'center' }, | ||||
|   { title: '生效时间', dataIndex: 'effectiveTime', width: 180 }, | ||||
|   { title: '终止时间', dataIndex: 'terminateTime', width: 180 }, | ||||
|   { title: '创建人', dataIndex: 'createUserString', show: false, ellipsis: true, tooltip: true }, | ||||
|   { title: '创建时间', dataIndex: 'createTime', width: 180 }, | ||||
|   { | ||||
|     title: '操作', | ||||
|     dataIndex: 'action', | ||||
|     slotName: 'action', | ||||
|     width: 130, | ||||
|     width: 160, | ||||
|     align: 'center', | ||||
|     fixed: !isMobile() ? 'right' : undefined, | ||||
|     show: has.hasPermOr(['system:notice:update', 'system:notice:delete']), | ||||
|     show: has.hasPermOr(['system:notice:detail', 'system:notice:update', 'system:notice:delete']), | ||||
|   }, | ||||
| ] | ||||
|  | ||||
| @@ -124,7 +111,7 @@ const reset = () => { | ||||
| // 删除 | ||||
| const onDelete = (record: NoticeResp) => { | ||||
|   return handleDelete(() => deleteNotice(record.id), { | ||||
|     content: `是否确定删除公告 [${record.title}]?`, | ||||
|     content: `是否确定删除公告「${record.title}」?`, | ||||
|     showModal: true, | ||||
|   }) | ||||
| } | ||||
|   | ||||
| @@ -1,9 +1,10 @@ | ||||
| <template> | ||||
|   <a-modal | ||||
|     v-model:visible="visible" | ||||
|     :title="title" | ||||
|     title="新增角色" | ||||
|     :mask-closable="false" | ||||
|     :esc-to-close="true" | ||||
|     draggable | ||||
|     :width="width >= 600 ? 600 : '100%'" | ||||
|     @close="reset" | ||||
|   > | ||||
| @@ -12,14 +13,13 @@ | ||||
|       <a-step>功能权限</a-step> | ||||
|       <a-step>数据权限</a-step> | ||||
|     </a-steps> | ||||
|  | ||||
|     <a-form ref="formRef" :model="form" :rules="rules" size="large" auto-label-width> | ||||
|       <fieldset v-show="current === 1"> | ||||
|         <a-form-item label="名称" field="name"> | ||||
|           <a-input v-model.trim="form.name" placeholder="请输入名称" /> | ||||
|         </a-form-item> | ||||
|         <a-form-item label="编码" field="code"> | ||||
|           <a-input v-model.trim="form.code" placeholder="请输入编码" :disabled="isUpdate" /> | ||||
|           <a-input v-model.trim="form.code" placeholder="请输入编码" /> | ||||
|         </a-form-item> | ||||
|         <a-form-item label="排序" field="sort"> | ||||
|           <a-input-number v-model="form.sort" placeholder="请输入排序" :min="1" mode="button" /> | ||||
| @@ -104,22 +104,22 @@ | ||||
| <script setup lang="ts"> | ||||
| import { type FormInstance, Message, type TreeNodeData } from '@arco-design/web-vue' | ||||
| import { useWindowSize } from '@vueuse/core' | ||||
| import { addRole, getRole, updateRole } from '@/apis/system' | ||||
| import { addRole } from '@/apis/system/role' | ||||
| import { useForm } from '@/hooks' | ||||
| import { useDept, useDict, useMenu } from '@/hooks/app' | ||||
|  | ||||
| const emit = defineEmits<{ | ||||
|   (e: 'save-success'): void | ||||
| }>() | ||||
|  | ||||
| const { width } = useWindowSize() | ||||
|  | ||||
| const dataId = ref('') | ||||
| const visible = ref(false) | ||||
| const formRef = ref<FormInstance>() | ||||
| const { data_scope_enum } = useDict('data_scope_enum') | ||||
| const { deptList, getDeptList } = useDept() | ||||
| const { menuList, getMenuList } = useMenu() | ||||
| const current = ref<number>(1) | ||||
| const dataId = ref('') | ||||
| const isUpdate = computed(() => !!dataId.value) | ||||
| const title = computed(() => (isUpdate.value ? '修改角色' : '新增角色')) | ||||
| const formRef = ref<FormInstance>() | ||||
|  | ||||
| const rules: FormInstance['rules'] = { | ||||
|   name: [{ required: true, message: '请输入名称' }], | ||||
| @@ -140,6 +140,7 @@ const isMenuExpanded = ref(false) | ||||
| const isDeptExpanded = ref(true) | ||||
| const isMenuCheckAll = ref(false) | ||||
| const isDeptCheckAll = ref(false) | ||||
| const current = ref<number>(1) | ||||
| // 重置 | ||||
| const reset = () => { | ||||
|   isMenuExpanded.value = false | ||||
| @@ -148,24 +149,11 @@ const reset = () => { | ||||
|   isDeptCheckAll.value = false | ||||
|   menuTreeRef.value?.expandAll(isMenuExpanded.value) | ||||
|   deptTreeRef.value?.expandAll(isDeptExpanded.value) | ||||
|   formRef.value?.resetFields() | ||||
|   current.value = 1 | ||||
|   formRef.value?.resetFields() | ||||
|   resetForm() | ||||
| } | ||||
|  | ||||
| const visible = ref(false) | ||||
| // 新增 | ||||
| const onAdd = () => { | ||||
|   if (!menuList.value.length) { | ||||
|     getMenuList() | ||||
|   } | ||||
|   reset() | ||||
|   dataId.value = '' | ||||
|   visible.value = true | ||||
|   if (!deptList.value.length) { | ||||
|     getDeptList() | ||||
|   } | ||||
| } | ||||
| // 上一步 | ||||
| const onPrev = () => { | ||||
|   current.value = Math.max(1, current.value - 1) | ||||
| @@ -186,20 +174,6 @@ const onNext = async () => { | ||||
| const onChangeCurrent = (page: number) => { | ||||
|   current.value = page | ||||
| } | ||||
| // 修改 | ||||
| const onUpdate = async (id: string) => { | ||||
|   if (!menuList.value.length) { | ||||
|     await getMenuList() | ||||
|   } | ||||
|   if (!deptList.value.length) { | ||||
|     await getDeptList() | ||||
|   } | ||||
|   reset() | ||||
|   dataId.value = id | ||||
|   const res = await getRole(id) | ||||
|   Object.assign(form, res.data) | ||||
|   visible.value = true | ||||
| } | ||||
|  | ||||
| // 获取所有选中的菜单 | ||||
| const getMenuAllCheckedKeys = () => { | ||||
| @@ -228,27 +202,7 @@ const getDeptAllCheckedKeys = () => { | ||||
|   return checkedKeys | ||||
| } | ||||
|  | ||||
| // 保存 | ||||
| const save = async () => { | ||||
|   try { | ||||
|     const isInvalid = await formRef.value?.validate() | ||||
|     if (isInvalid) return false | ||||
|     form.menuIds = getMenuAllCheckedKeys() | ||||
|     form.deptIds = getDeptAllCheckedKeys() | ||||
|     if (isUpdate.value) { | ||||
|       await updateRole(form, dataId.value) | ||||
|       Message.success('修改成功') | ||||
|     } else { | ||||
|       await addRole(form) | ||||
|       Message.success('新增成功') | ||||
|     } | ||||
|     emit('save-success') | ||||
|     return true | ||||
|   } catch (error) { | ||||
|     return false | ||||
|   } | ||||
| } | ||||
|  | ||||
| // 操作树 | ||||
| const handleTreeAction = (type, action) => { | ||||
|   const refMap = { | ||||
|     menu: menuTreeRef, | ||||
| @@ -266,14 +220,44 @@ const handleTreeAction = (type, action) => { | ||||
| const onExpanded = (type) => handleTreeAction(type, 'expand') | ||||
| const onCheckAll = (type) => handleTreeAction(type, 'check') | ||||
|  | ||||
| // 确认时 | ||||
| // 保存 | ||||
| const save = async () => { | ||||
|   try { | ||||
|     const isInvalid = await formRef.value?.validate() | ||||
|     if (isInvalid) return false | ||||
|     form.menuIds = getMenuAllCheckedKeys() | ||||
|     form.deptIds = getDeptAllCheckedKeys() | ||||
|     await addRole(form) | ||||
|     Message.success('新增成功') | ||||
|     emit('save-success') | ||||
|     return true | ||||
|   } catch (error) { | ||||
|     return false | ||||
|   } | ||||
| } | ||||
|  | ||||
| // 确认 | ||||
| const onClickOk = () => { | ||||
|   if (unref(current) === 3) { | ||||
|     save() | ||||
|     visible.value = false | ||||
|   } | ||||
| } | ||||
| defineExpose({ onAdd, onUpdate }) | ||||
|  | ||||
| // 打开 | ||||
| const onOpen = async () => { | ||||
|   reset() | ||||
|   if (!menuList.value.length) { | ||||
|     await getMenuList() | ||||
|   } | ||||
|   if (!deptList.value.length) { | ||||
|     await getDeptList() | ||||
|   } | ||||
|   dataId.value = '' | ||||
|   visible.value = true | ||||
| } | ||||
|  | ||||
| defineExpose({ onOpen }) | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
|   | ||||
| @@ -4,7 +4,7 @@ | ||||
|     title="分配角色" | ||||
|     :mask-closable="false" | ||||
|     :esc-to-close="false" | ||||
|     :width="width >= 1350 ? 1350 : '100%'" | ||||
|     :width="width >= 1100 ? 1100 : '100%'" | ||||
|     draggable | ||||
|     @before-ok="save" | ||||
|     @close="reset" | ||||
| @@ -14,16 +14,21 @@ | ||||
| </template> | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| import { ref } from 'vue' | ||||
| import { Message } from '@arco-design/web-vue' | ||||
| import { useWindowSize } from '@vueuse/core' | ||||
| import { assignToUsers, listRoleUsers } from '@/apis/system' | ||||
| import { assignToUsers, listRoleUsers } from '@/apis/system/role' | ||||
|  | ||||
| const { width } = useWindowSize() | ||||
| const visible = ref(false) | ||||
|  | ||||
| const dataId = ref('') | ||||
| const visible = ref(false) | ||||
| const selectedUsers = ref<string[]>([]) | ||||
|  | ||||
| // 用户选择回调 | ||||
| const onSelectUser = (value: string[]) => { | ||||
|   selectedUsers.value = value | ||||
| } | ||||
|  | ||||
| const UserSelectRef = ref() | ||||
| // 重置 | ||||
| const reset = () => { | ||||
| @@ -49,11 +54,6 @@ const save = async () => { | ||||
|   } | ||||
| } | ||||
|  | ||||
| // 用户选择回调 | ||||
| const onSelectUser = (value: string[]) => { | ||||
|   selectedUsers.value = value | ||||
| } | ||||
|  | ||||
| // 打开 | ||||
| const onOpen = async (id: string) => { | ||||
|   dataId.value = id | ||||
| @@ -63,9 +63,7 @@ const onOpen = async (id: string) => { | ||||
|   visible.value = true | ||||
| } | ||||
|  | ||||
| defineExpose({ | ||||
|   onOpen, | ||||
| }) | ||||
| defineExpose({ onOpen }) | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped></style> | ||||
|   | ||||
| @@ -53,37 +53,38 @@ | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| import { useWindowSize } from '@vueuse/core' | ||||
| import { type RoleDetailResp, getRole } from '@/apis/system' | ||||
| import { type RoleDetailResp, getRole as getDetail } from '@/apis/system/role' | ||||
| import { useDept, useDict, useMenu } from '@/hooks/app' | ||||
|  | ||||
| const { width } = useWindowSize() | ||||
|  | ||||
| const dataId = ref('') | ||||
| const dataDetail = ref<RoleDetailResp>() | ||||
| const visible = ref(false) | ||||
| const { data_scope_enum } = useDict('data_scope_enum') | ||||
| const { deptList, getDeptList } = useDept() | ||||
| const { menuList, getMenuList } = useMenu() | ||||
|  | ||||
| const dataId = ref('') | ||||
| const dataDetail = ref<RoleDetailResp>() | ||||
| // 查询详情 | ||||
| const getDataDetail = async () => { | ||||
|   const res = await getRole(dataId.value) | ||||
|   dataDetail.value = res.data | ||||
|   const { data } = await getDetail(dataId.value) | ||||
|   dataDetail.value = data | ||||
| } | ||||
|  | ||||
| const visible = ref(false) | ||||
| // 详情 | ||||
| const onDetail = async (id: string) => { | ||||
| // 打开 | ||||
| const onOpen = async (id: string) => { | ||||
|   dataId.value = id | ||||
|   if (!menuList.value.length) { | ||||
|     await getMenuList() | ||||
|   } | ||||
|   if (!deptList.value.length) { | ||||
|     await getDeptList() | ||||
|   } | ||||
|   dataId.value = id | ||||
|   await getDataDetail() | ||||
|   visible.value = true | ||||
| } | ||||
|  | ||||
| defineExpose({ onDetail }) | ||||
| defineExpose({ onOpen }) | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| <template> | ||||
|   <a-drawer | ||||
|     v-model:visible="visible" | ||||
|     :title="title" | ||||
|     title="修改角色" | ||||
|     :mask-closable="false" | ||||
|     :esc-to-close="false" | ||||
|     :width="width >= 600 ? 600 : '100%'" | ||||
| @@ -83,23 +83,23 @@ | ||||
| <script setup lang="ts"> | ||||
| import { type FormInstance, Message, type TreeNodeData } from '@arco-design/web-vue' | ||||
| import { useWindowSize } from '@vueuse/core' | ||||
| import { addRole, getRole, updateRole } from '@/apis/system' | ||||
| import { getRole, updateRole } from '@/apis/system/role' | ||||
| import { useForm } from '@/hooks' | ||||
| import { useDept, useDict, useMenu } from '@/hooks/app' | ||||
|  | ||||
| const emit = defineEmits<{ | ||||
|   (e: 'save-success'): void | ||||
| }>() | ||||
|  | ||||
| const { width } = useWindowSize() | ||||
|  | ||||
| const dataId = ref('') | ||||
| const visible = ref(false) | ||||
| const formRef = ref<FormInstance>() | ||||
| const { data_scope_enum } = useDict('data_scope_enum') | ||||
| const { deptList, getDeptList } = useDept() | ||||
| const { menuList, getMenuList } = useMenu() | ||||
|  | ||||
| const dataId = ref('') | ||||
| const isUpdate = computed(() => !!dataId.value) | ||||
| const title = computed(() => (isUpdate.value ? '修改角色' : '新增角色')) | ||||
| const formRef = ref<FormInstance>() | ||||
|  | ||||
| const rules: FormInstance['rules'] = { | ||||
|   name: [{ required: true, message: '请输入名称' }], | ||||
|   code: [{ required: true, message: '请输入编码' }], | ||||
| @@ -133,45 +133,6 @@ const reset = () => { | ||||
|   resetForm() | ||||
| } | ||||
|  | ||||
| const visible = ref(false) | ||||
| // 新增 | ||||
| const onAdd = () => { | ||||
|   if (!menuList.value.length) { | ||||
|     getMenuList() | ||||
|   } | ||||
|   reset() | ||||
|   dataId.value = '' | ||||
|   visible.value = true | ||||
|   if (!deptList.value.length) { | ||||
|     getDeptList() | ||||
|   } | ||||
| } | ||||
|  | ||||
| // 修改 | ||||
| const onUpdate = async (id: string) => { | ||||
|   if (!menuList.value.length) { | ||||
|     await getMenuList() | ||||
|   } | ||||
|   if (!deptList.value.length) { | ||||
|     await getDeptList() | ||||
|   } | ||||
|   reset() | ||||
|   dataId.value = id | ||||
|   const { data } = await getRole(id) | ||||
|   Object.assign(form, data) | ||||
|   data.menuIds?.forEach((node) => { | ||||
|     nextTick(() => { | ||||
|       menuTreeRef.value?.checkNode(node, true, true) | ||||
|     }) | ||||
|   }) | ||||
|   data.deptIds?.forEach((node) => { | ||||
|     nextTick(() => { | ||||
|       deptTreeRef.value?.checkNode(node, true, true) | ||||
|     }) | ||||
|   }) | ||||
|   visible.value = true | ||||
| } | ||||
|  | ||||
| // 获取所有选中的菜单 | ||||
| const getMenuAllCheckedKeys = () => { | ||||
|   // 获取目前被选中的菜单 | ||||
| @@ -199,27 +160,6 @@ const getDeptAllCheckedKeys = () => { | ||||
|   return checkedKeys | ||||
| } | ||||
|  | ||||
| // 保存 | ||||
| const save = async () => { | ||||
|   try { | ||||
|     const isInvalid = await formRef.value?.validate() | ||||
|     if (isInvalid) return false | ||||
|     form.menuIds = getMenuAllCheckedKeys() | ||||
|     form.deptIds = getDeptAllCheckedKeys() | ||||
|     if (isUpdate.value) { | ||||
|       await updateRole(form, dataId.value) | ||||
|       Message.success('修改成功') | ||||
|     } else { | ||||
|       await addRole(form) | ||||
|       Message.success('新增成功') | ||||
|     } | ||||
|     emit('save-success') | ||||
|     return true | ||||
|   } catch (error) { | ||||
|     return false | ||||
|   } | ||||
| } | ||||
|  | ||||
| // 展开/折叠 | ||||
| const onExpanded = (type: string) => { | ||||
|   if (type === 'menu') { | ||||
| @@ -238,7 +178,48 @@ const onCheckAll = (type: string) => { | ||||
|   } | ||||
| } | ||||
|  | ||||
| defineExpose({ onAdd, onUpdate }) | ||||
| // 保存 | ||||
| const save = async () => { | ||||
|   try { | ||||
|     const isInvalid = await formRef.value?.validate() | ||||
|     if (isInvalid) return false | ||||
|     form.menuIds = getMenuAllCheckedKeys() | ||||
|     form.deptIds = getDeptAllCheckedKeys() | ||||
|     await updateRole(form, dataId.value) | ||||
|     Message.success('修改成功') | ||||
|     emit('save-success') | ||||
|     return true | ||||
|   } catch (error) { | ||||
|     return false | ||||
|   } | ||||
| } | ||||
|  | ||||
| // 打开 | ||||
| const onOpen = async (id: string) => { | ||||
|   reset() | ||||
|   dataId.value = id | ||||
|   if (!menuList.value.length) { | ||||
|     await getMenuList() | ||||
|   } | ||||
|   if (!deptList.value.length) { | ||||
|     await getDeptList() | ||||
|   } | ||||
|   const { data } = await getRole(id) | ||||
|   Object.assign(form, data) | ||||
|   data.menuIds?.forEach((node) => { | ||||
|     nextTick(() => { | ||||
|       menuTreeRef.value?.checkNode(node, true, true) | ||||
|     }) | ||||
|   }) | ||||
|   data.deptIds?.forEach((node) => { | ||||
|     nextTick(() => { | ||||
|       deptTreeRef.value?.checkNode(node, true, true) | ||||
|     }) | ||||
|   }) | ||||
|   visible.value = true | ||||
| } | ||||
|  | ||||
| defineExpose({ onOpen }) | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| <template> | ||||
|   <div class="table-page"> | ||||
|     <GiTable | ||||
|       row-key="id" | ||||
|       title="角色管理" | ||||
|       row-key="id" | ||||
|       :data="dataList" | ||||
|       :columns="columns" | ||||
|       :loading="loading" | ||||
| @@ -27,20 +27,6 @@ | ||||
|           <template #default>新增</template> | ||||
|         </a-button> | ||||
|       </template> | ||||
|       <template #name="{ record }"> | ||||
|         <a-link @click="onDetail(record)"> | ||||
|           <a-typography-paragraph | ||||
|             class="link-text" | ||||
|             :ellipsis="{ | ||||
|               rows: 1, | ||||
|               showTooltip: true, | ||||
|               css: true, | ||||
|             }" | ||||
|           > | ||||
|             {{ record.name }} | ||||
|           </a-typography-paragraph> | ||||
|         </a-link> | ||||
|       </template> | ||||
|       <template #dataScope="{ record }"> | ||||
|         <GiCellTag :value="record.dataScope" :dict="data_scope_enum" /> | ||||
|       </template> | ||||
| @@ -50,13 +36,14 @@ | ||||
|       </template> | ||||
|       <template #action="{ record }"> | ||||
|         <a-space> | ||||
|           <a-link v-permission="['system:role:update']" @click="onUpdate(record)">修改</a-link> | ||||
|           <a-link v-permission="['system:role:assign']" @click="onAssign(record)">分配</a-link> | ||||
|           <a-link v-permission="['system:role:detail']" title="详情" @click="onDetail(record)">详情</a-link> | ||||
|           <a-link v-permission="['system:role:update']" title="修改" @click="onUpdate(record)">修改</a-link> | ||||
|           <a-link v-permission="['system:role:assign']" title="分配" @click="onAssign(record)">分配</a-link> | ||||
|           <a-link | ||||
|             v-permission="['system:role:delete']" | ||||
|             status="danger" | ||||
|             :title="record.isSystem ? '系统内置数据不能删除' : undefined" | ||||
|             :disabled="record.disabled" | ||||
|             :disabled="record.isSystem" | ||||
|             :title="record.isSystem ? '系统内置数据不能删除' : '删除'" | ||||
|             @click="onDelete(record)" | ||||
|           > | ||||
|             删除 | ||||
| @@ -73,11 +60,11 @@ | ||||
| </template> | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| import RoleAddModal from './RoleAddModal.vue' | ||||
| import RoleUpdateDrawer from './RoleUpdateDrawer.vue' | ||||
| import RoleDetailDrawer from './RoleDetailDrawer.vue' | ||||
| import RoleAddModal from './RoleAddModal.vue' | ||||
| import RoleAssignModal from './RoleAssignModal.vue' | ||||
| import { type RoleQuery, type RoleResp, deleteRole, listRole } from '@/apis/system' | ||||
| import { type RoleQuery, type RoleResp, deleteRole, listRole } from '@/apis/system/role' | ||||
| import type { TableInstanceColumns } from '@/components/GiTable/type' | ||||
| import { useTable } from '@/hooks' | ||||
| import { useDict } from '@/hooks/app' | ||||
| @@ -89,7 +76,7 @@ defineOptions({ name: 'SystemRole' }) | ||||
| const { data_scope_enum } = useDict('data_scope_enum') | ||||
|  | ||||
| const queryForm = reactive<RoleQuery>({ | ||||
|   sort: ['createTime,desc'], | ||||
|   sort: ['id,desc'], | ||||
| }) | ||||
|  | ||||
| const { | ||||
| @@ -99,7 +86,6 @@ const { | ||||
|   search, | ||||
|   handleDelete, | ||||
| } = useTable((page) => listRole({ ...queryForm, ...page }), { immediate: true }) | ||||
|  | ||||
| const columns: TableInstanceColumns[] = [ | ||||
|   { | ||||
|     title: '序号', | ||||
| @@ -111,7 +97,7 @@ const columns: TableInstanceColumns[] = [ | ||||
|   { title: '编码', dataIndex: 'code', ellipsis: true, tooltip: true }, | ||||
|   { title: '数据权限', dataIndex: 'dataScope', slotName: 'dataScope', ellipsis: true, tooltip: true }, | ||||
|   { title: '排序', dataIndex: 'sort', align: 'center', show: false }, | ||||
|   { title: '系统内置', slotName: 'isSystem', align: 'center', show: false }, | ||||
|   { title: '系统内置', dataIndex: 'isSystem', slotName: 'isSystem', align: 'center', show: false }, | ||||
|   { title: '描述', dataIndex: 'description', ellipsis: true, tooltip: true }, | ||||
|   { title: '创建人', dataIndex: 'createUserString', ellipsis: true, tooltip: true, show: false }, | ||||
|   { title: '创建时间', dataIndex: 'createTime', width: 180 }, | ||||
| @@ -119,11 +105,17 @@ const columns: TableInstanceColumns[] = [ | ||||
|   { title: '修改时间', dataIndex: 'updateTime', width: 180, show: false }, | ||||
|   { | ||||
|     title: '操作', | ||||
|     dataIndex: 'action', | ||||
|     slotName: 'action', | ||||
|     width: 200, | ||||
|     align: 'center', | ||||
|     fixed: !isMobile() ? 'right' : undefined, | ||||
|     show: has.hasPermOr(['system:role:update', 'system:role:delete']), | ||||
|     show: has.hasPermOr([ | ||||
|       'system:role:detail', | ||||
|       'system:role:update', | ||||
|       'system:role:delete', | ||||
|       'system:role:assign', | ||||
|     ]), | ||||
|   }, | ||||
| ] | ||||
|  | ||||
| @@ -135,25 +127,28 @@ const reset = () => { | ||||
|  | ||||
| // 删除 | ||||
| const onDelete = (record: RoleResp) => { | ||||
|   return handleDelete(() => deleteRole(record.id), { content: `是否确定删除 [${record.name}]?`, showModal: true }) | ||||
|   return handleDelete(() => deleteRole(record.id), { | ||||
|     content: `是否确定删除角色「${record.name}(${record.code})」?`, | ||||
|     showModal: true, | ||||
|   }) | ||||
| } | ||||
|  | ||||
| const RoleUpdateDrawerRef = ref<InstanceType<typeof RoleUpdateDrawer>>() | ||||
| const RoleAddModalRef = ref<InstanceType<typeof RoleAddModal>>() | ||||
| // 新增 | ||||
| const onAdd = () => { | ||||
|   RoleAddModalRef.value?.onAdd() | ||||
|   RoleAddModalRef.value?.onOpen() | ||||
| } | ||||
|  | ||||
| const RoleUpdateDrawerRef = ref<InstanceType<typeof RoleUpdateDrawer>>() | ||||
| // 修改 | ||||
| const onUpdate = (record: RoleResp) => { | ||||
|   RoleUpdateDrawerRef.value?.onUpdate(record.id) | ||||
|   RoleUpdateDrawerRef.value?.onOpen(record.id) | ||||
| } | ||||
|  | ||||
| const RoleDetailDrawerRef = ref<InstanceType<typeof RoleDetailDrawer>>() | ||||
| // 详情 | ||||
| const onDetail = (record: RoleResp) => { | ||||
|   RoleDetailDrawerRef.value?.onDetail(record.id) | ||||
|   RoleDetailDrawerRef.value?.onOpen(record.id) | ||||
| } | ||||
|  | ||||
| const RoleAssignModalRef = ref<InstanceType<typeof RoleAssignModal>>() | ||||
|   | ||||
| @@ -94,7 +94,7 @@ | ||||
| <script setup lang="ts"> | ||||
| import { type FormInstance, Message } from '@arco-design/web-vue' | ||||
| import { useWindowSize } from '@vueuse/core' | ||||
| import { addStorage, getStorage, updateStorage } from '@/apis/system' | ||||
| import { addStorage, getStorage, updateStorage } from '@/apis/system/storage' | ||||
| import { useForm } from '@/hooks' | ||||
| import { useDict } from '@/hooks/app' | ||||
| import { encryptByRsa } from '@/utils/encrypt' | ||||
| @@ -103,13 +103,15 @@ import { isIPv4 } from '@/utils/validate' | ||||
| const emit = defineEmits<{ | ||||
|   (e: 'save-success'): void | ||||
| }>() | ||||
|  | ||||
| const { width } = useWindowSize() | ||||
| const { storage_type_enum } = useDict('storage_type_enum') | ||||
|  | ||||
| const dataId = ref('') | ||||
| const visible = ref(false) | ||||
| const isUpdate = computed(() => !!dataId.value) | ||||
| const title = computed(() => (isUpdate.value ? '修改存储' : '新增存储')) | ||||
| const formRef = ref<FormInstance>() | ||||
| const { storage_type_enum } = useDict('storage_type_enum') | ||||
|  | ||||
| const rules: FormInstance['rules'] = { | ||||
|   name: [{ required: true, message: '请输入名称' }], | ||||
| @@ -152,23 +154,6 @@ const reset = () => { | ||||
|   resetForm() | ||||
| } | ||||
|  | ||||
| const visible = ref(false) | ||||
| // 新增 | ||||
| const onAdd = () => { | ||||
|   reset() | ||||
|   dataId.value = '' | ||||
|   visible.value = true | ||||
| } | ||||
|  | ||||
| // 修改 | ||||
| const onUpdate = async (id: string) => { | ||||
|   reset() | ||||
|   dataId.value = id | ||||
|   const res = await getStorage(id) | ||||
|   Object.assign(form, res.data) | ||||
|   visible.value = true | ||||
| } | ||||
|  | ||||
| // 保存 | ||||
| const save = async () => { | ||||
|   try { | ||||
| @@ -193,5 +178,21 @@ const save = async () => { | ||||
|   } | ||||
| } | ||||
|  | ||||
| // 新增 | ||||
| const onAdd = () => { | ||||
|   reset() | ||||
|   dataId.value = '' | ||||
|   visible.value = true | ||||
| } | ||||
|  | ||||
| // 修改 | ||||
| const onUpdate = async (id: string) => { | ||||
|   reset() | ||||
|   dataId.value = id | ||||
|   const { data } = await getStorage(id) | ||||
|   Object.assign(form, data) | ||||
|   visible.value = true | ||||
| } | ||||
|  | ||||
| defineExpose({ onAdd, onUpdate }) | ||||
| </script> | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| <template> | ||||
|   <div class="table-page"> | ||||
|     <GiTable | ||||
|       row-key="id" | ||||
|       title="存储管理" | ||||
|       row-key="id" | ||||
|       :data="dataList" | ||||
|       :columns="columns" | ||||
|       :loading="loading" | ||||
| @@ -51,12 +51,12 @@ | ||||
|       </template> | ||||
|       <template #action="{ record }"> | ||||
|         <a-space> | ||||
|           <a-link v-permission="['system:storage:update']" @click="onUpdate(record)">修改</a-link> | ||||
|           <a-link v-permission="['system:storage:update']" title="修改" @click="onUpdate(record)">修改</a-link> | ||||
|           <a-link | ||||
|             v-permission="['system:storage:delete']" | ||||
|             status="danger" | ||||
|             :title="record.isDefault ? '默认存储不能删除' : undefined" | ||||
|             :disabled="record.disabled" | ||||
|             :disabled="record.isDefault" | ||||
|             :title="record.isDefault ? '默认存储不能删除' : '删除'" | ||||
|             @click="onDelete(record)" | ||||
|           > | ||||
|             删除 | ||||
| @@ -71,13 +71,13 @@ | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| import StorageAddDrawer from './StorageAddDrawer.vue' | ||||
| import { type StorageQuery, type StorageResp, deleteStorage, listStorage } from '@/apis/system' | ||||
| import { type StorageQuery, type StorageResp, deleteStorage, listStorage } from '@/apis/system/storage' | ||||
| import type { TableInstanceColumns } from '@/components/GiTable/type' | ||||
| import { DisEnableStatusList } from '@/constant/common' | ||||
| import { useTable } from '@/hooks' | ||||
| import { useDict } from '@/hooks/app' | ||||
| import { isMobile } from '@/utils' | ||||
| import has from '@/utils/has' | ||||
| import { DisEnableStatusList } from '@/constant/common' | ||||
|  | ||||
| defineOptions({ name: 'SystemStorage' }) | ||||
|  | ||||
| @@ -94,7 +94,6 @@ const { | ||||
|   search, | ||||
|   handleDelete, | ||||
| } = useTable((page) => listStorage({ ...queryForm, ...page }), { immediate: true }) | ||||
|  | ||||
| const columns: TableInstanceColumns[] = [ | ||||
|   { | ||||
|     title: '序号', | ||||
| @@ -104,8 +103,8 @@ const columns: TableInstanceColumns[] = [ | ||||
|   }, | ||||
|   { title: '名称', dataIndex: 'name', slotName: 'name', width: 140, ellipsis: true, tooltip: true }, | ||||
|   { title: '编码', dataIndex: 'code', ellipsis: true, tooltip: true }, | ||||
|   { title: '状态', slotName: 'status', align: 'center' }, | ||||
|   { title: '类型', slotName: 'type', align: 'center' }, | ||||
|   { title: '状态', dataIndex: 'status', slotName: 'status', align: 'center' }, | ||||
|   { title: '类型', dataIndex: 'type', slotName: 'type', align: 'center' }, | ||||
|   { title: '访问密钥', dataIndex: 'accessKey', ellipsis: true, tooltip: true }, | ||||
|   { title: '终端节点', dataIndex: 'endpoint', ellipsis: true, tooltip: true }, | ||||
|   { title: '桶名称', dataIndex: 'bucketName', ellipsis: true, tooltip: true }, | ||||
| @@ -117,6 +116,7 @@ const columns: TableInstanceColumns[] = [ | ||||
|   { title: '修改时间', dataIndex: 'updateTime', width: 180, show: false }, | ||||
|   { | ||||
|     title: '操作', | ||||
|     dataIndex: 'action', | ||||
|     slotName: 'action', | ||||
|     width: 130, | ||||
|     align: 'center', | ||||
| @@ -134,7 +134,10 @@ const reset = () => { | ||||
|  | ||||
| // 删除 | ||||
| const onDelete = (record: StorageResp) => { | ||||
|   return handleDelete(() => deleteStorage(record.id), { content: `是否确定删除存储 [${record.name}]?`, showModal: true }) | ||||
|   return handleDelete(() => deleteStorage(record.id), { | ||||
|     content: `是否确定删除存储「${record.name}」?`, | ||||
|     showModal: true, | ||||
|   }) | ||||
| } | ||||
|  | ||||
| const StorageAddDrawerRef = ref<InstanceType<typeof StorageAddDrawer>>() | ||||
|   | ||||
| @@ -33,7 +33,6 @@ const dataId = ref('') | ||||
| const visible = ref(false) | ||||
| const isUpdate = computed(() => !!dataId.value) | ||||
| const title = computed(() => (isUpdate.value ? '修改用户' : '新增用户')) | ||||
|  | ||||
| const formRef = ref<InstanceType<typeof GiForm>>() | ||||
| const { roleList, getRoleList } = useRole() | ||||
| const { deptList, getDeptList } = useDept() | ||||
|   | ||||
| @@ -30,7 +30,7 @@ | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| import { useWindowSize } from '@vueuse/core' | ||||
| import { type UserDetailResp, getUser } from '@/apis/system/user' | ||||
| import { type UserDetailResp, getUser as getDetail } from '@/apis/system/user' | ||||
|  | ||||
| const { width } = useWindowSize() | ||||
|  | ||||
| @@ -40,7 +40,7 @@ const visible = ref(false) | ||||
|  | ||||
| // 查询详情 | ||||
| const getDataDetail = async () => { | ||||
|   const { data } = await getUser(dataId.value) | ||||
|   const { data } = await getDetail(dataId.value) | ||||
|   dataDetail.value = data | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -4,7 +4,7 @@ | ||||
|     title="分配角色" | ||||
|     :mask-closable="false" | ||||
|     :esc-to-close="false" | ||||
|     :width="width >= 500 ? 500 : '100%'" | ||||
|     :width="width >= 600 ? 600 : '100%'" | ||||
|     draggable | ||||
|     @before-ok="save" | ||||
|     @close="reset" | ||||
|   | ||||
| @@ -58,7 +58,7 @@ | ||||
|           </template> | ||||
|           <template #action="{ record }"> | ||||
|             <a-space> | ||||
|               <a-link v-permission="['system:user:list']" title="详情" @click="onDetail(record)">详情</a-link> | ||||
|               <a-link v-permission="['system:user:detail']" title="详情" @click="onDetail(record)">详情</a-link> | ||||
|               <a-link v-permission="['system:user:update']" title="修改" @click="onUpdate(record)">修改</a-link> | ||||
|               <a-link | ||||
|                 v-permission="['system:user:delete']" | ||||
| @@ -101,7 +101,7 @@ import UserImportDrawer from './UserImportDrawer.vue' | ||||
| import UserDetailDrawer from './UserDetailDrawer.vue' | ||||
| import UserResetPwdModal from './UserResetPwdModal.vue' | ||||
| import UserUpdateRoleModal from './UserUpdateRoleModal.vue' | ||||
| import { type UserQuery, type UserResp, deleteUser, exportUser, listUser } from '@/apis/system' | ||||
| import { type UserQuery, type UserResp, deleteUser, exportUser, listUser } from '@/apis/system/user' | ||||
| import type { Columns, Options } from '@/components/GiForm' | ||||
| import type { TableInstanceColumns } from '@/components/GiTable/type' | ||||
| import { useDownload, useTable } from '@/hooks' | ||||
| @@ -148,7 +148,7 @@ const queryFormColumns: Columns = reactive([ | ||||
|   }, | ||||
| ]) | ||||
| const queryForm = reactive<UserQuery>({ | ||||
|   sort: ['t1.createTime,desc', 't1.id,desc'], | ||||
|   sort: ['t1.id,desc'], | ||||
| }) | ||||
|  | ||||
| const { | ||||
| @@ -190,11 +190,18 @@ const columns: TableInstanceColumns[] = [ | ||||
|   { title: '修改时间', dataIndex: 'updateTime', width: 180, show: false }, | ||||
|   { | ||||
|     title: '操作', | ||||
|     dataIndex: 'action', | ||||
|     slotName: 'action', | ||||
|     width: 190, | ||||
|     align: 'center', | ||||
|     fixed: !isMobile() ? 'right' : undefined, | ||||
|     show: has.hasPermOr(['system:user:update', 'system:user:delete', 'system:user:resetPwd']), | ||||
|     show: has.hasPermOr([ | ||||
|       'system:user:detail', | ||||
|       'system:user:update', | ||||
|       'system:user:delete', | ||||
|       'system:user:resetPwd', | ||||
|       'system:user:updateRole', | ||||
|     ]), | ||||
|   }, | ||||
| ] | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user